[untitled]
1 pointsby suzuki0 comments
class Obj
end
# Value operated by Scheme
alias Val = Nil | Obj | Bool | String | Int32 | Float64 | BigInt
to define Cons Cell of Scheme: # Cons cell
class Cell < Obj
include Enumerable(Val)
getter car : Val # Head part of the cell
property cdr : Val # Tail part of the cell
def initialize(@car : Val, @cdr : Val)
end
...
end # Cell
Note that you see generics, Enumerable(Val), and constructor arguments with '@' in the excerpt above. type Enumerator(type T) func(yield func(element T))
and the "Select" method can be written as func (loop Enumerator(T)) Select(f func(T) T) Enumerator(T) {
return func(yield func(T)) {
loop(func(element T) {
value := f(element)
yield(value)
})
}
}
You can call this method with type-safety as follows. Yay! func main() {
squares := Range(1, 3).Select(func(x int) int { return x * x })
squares(func(num int) {
fmt.Println(num)
})
}
// Output:
// 1
// 4
// 9
See https://go2goplay.golang.org/p/b0ugT68QAy2 for the complete code. func (loop Enumerator(T)) Select(type R)(f func(T) R) Enumerator(R) {
return func(yield func(R)) {
loop(func(element T) {
value := f(element)
yield(value)
})
}
}
However, you will get the error message then: type checking failed for main
prog.go2:17:33: methods cannot have type parameters
According to https://go.googlesource.com/proposal/+/refs/heads/master/des... this seems an intended restriction: # Cons cell
class Cell
include Enumerable
attr_reader :car
attr_accessor :cdr
def initialize(car, cdr)
@car = car
@cdr = cdr
end
# Yield car, cadr, caddr and so on, à la for-each in Scheme.
def each
j = self
begin
yield j.car
j = j.cdr
end while Cell === j
j.nil? or raise ImproperListException, j
end
end # Cell
and
https://github.com/nukata/little-scheme-in-crystal/blob/v0.2... # Cons cell
class Cell < Obj
include Enumerable(Val)
getter car : Val # Head part of the cell
property cdr : Val # Tail part of the cell
def initialize(@car : Val, @cdr : Val)
end
# Yield car, cadr, caddr and so on, à la for-each in Scheme.
def each
j = self
loop {
yield j.as(Cell).car
j = j.as(Cell).cdr
break unless Cell === j
}
raise ImproperListException.new(j) unless j.nil?
end
end # Cell
and they will make the point clear.
Ruby and Crystal are different languages, but you can translate your code from Ruby to Crystal line by line fairly easily. rescue ex: Errno
raise ErrorException.new(ex.message, NONE) if ex.errno == Errno::EPIPE
to rescue ex: IO::Error
raise ErrorException.new(ex.message, NONE) if ex.os_error == Errno::EPIPE
though it is still dependent on POSIX. >_< type Any = interface{}
type Enumerator func(yield func(element Any))
// Select creates an Enumerator which applies f to each of elements.
func (loop Enumerator) Select(f func(Any) Any) Enumerator {
return func(yield func(Any)) {
loop(func(element Any) {
value := f(element)
yield(value)
})
}
}
// Range creates an Enumerator which counts from start
// up to start + count - 1.
func Range(start, count int) Enumerator {
end := start + count
return func(yield func(Any)) {
for i := start; i < end; i++ {
yield(i)
}
}
}
Now you can write the following: squares := Range(1, 10).Select(func(x Any) Any { return x.(int) * x.(int) })
squares(func(num Any) {
Println(num)
})
// Output:
// 1
// 4
// 9
// 16
// 25
// 36
// 49
// 64
// 81
// 100
I'd say it is so elegant in Go! $ cat yin-yang-puzzle.scm
;; The yin-yang puzzle
;; cf. https://en.wikipedia.org/wiki/Call-with-current-continuation
((lambda (yin)
((lambda (yang)
(yin yang))
((lambda (cc)
(display '*)
cc)
(call/cc (lambda (c) c)))))
((lambda (cc)
(newline)
cc)
(call/cc (lambda (c) c))))
;; => \n*\n**\n***\n****\n*****\n******\n...
$ little-scheme-in-go yin-yang-puzzle.scm | head
*
**
***
****
*****
******
*******
********
*********
$ // Generate a sequence of integers from 1 to 10 and then select their squares.
squares := Range(1, 10).Select(func(x Any) Any { return x.(int) * x.(int) })
squares(func(num Any) {
fmt.Println(num)
})
Here the function Range is defined as follows: // Range creates an Enumerator which counts from start
// up to start + count - 1.
func Range(start, count int) Enumerator {
end := start + count
return func(yield func(Any)) {
for i := start; i < end; i++ {
yield(i)
}
}
}
Note that Enumerator is just a function type defined as func(func(Any)).
The space complexity is O(1) and you can yield values infinitely if you want. /// <summary>Evaluate a Lisp expression in an environment.</summary>
public object Eval(object x, Cell env) {
try {
for (;;) {
switch (x) {
case Arg xarg:
return xarg.GetValue(env);
case Sym xsym:
try {
return Globals[xsym];
} catch (KeyNotFoundException) {
throw new EvalException("void variable", x);
}
case Cell xcell:
Agreed. And if you prefer methods to functions, you can define methods on such a function type! In my LINQ in Go (https://github.com/nukata/linq-in-go), I define a higher order function type:
and several methods on it, for example,
Naturally, if you need another type parameter for a method, you must define it as a function. For example,
Now, with the function which converts a slice to an Enumerator,
you can write the following: