> If you have an interface with a single method, or something with a single varying piece of behavior, please strongly consider accepting a function instead.
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:
type Enumerator[T any] func(yield func(element T))
and several methods on it, for example,
// Where creates an Enumerator which selects elements by appling
// predicate to each of them.
func (loop Enumerator[T]) Where(predicate func(T) bool) Enumerator[T] {
return func(yield func(T)) {
loop(func(element T) {
if predicate(element) {
yield(element)
}
})
}
}
Naturally, if you need another type parameter for a method, you must define it as a function. For example,
// Select creates an Enumerator which applies f to each of elements.
func Select[T any, R any](f func(T) R, loop Enumerator[T]) Enumerator[R] {
return func(yield func(R)) {
loop(func(element T) {
value := f(element)
yield(value)
})
}
}
Now, with the function which converts a slice to an Enumerator,
// From creates an Enumerator from a slice.
func From[T ~[]E, E any](x T) Enumerator[E] {
return func(yield func(E)) {
for _, element := range x {
yield(element)
}
}
}
I agree with you. I wish Python 3 had strings as byte sequences mainly in UTF-8 as Python 2 had once and Go has now. Then things would be kept simple in Japan.
Python 3 feels cumbersome. To handle a raw input as a string, you must decode it in some encoding first. It is a fragile process. It would be adequate to treat the input bytes transparently and put an optional stage to convert other encodings to UTF-8 if necessary.
I have written almost the identical Scheme interpreters in Ruby and Crystal: [1] and [2]. The biggest difference I have felt between them is the absence of good old Object, which can represent everything at runtime, from Crystal. I had to declare Obj and Val:
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.
As for performance, Crystal is faster than Ruby 8.6 times as interpreter and 39.4 times as compiler [3]. You can use Crystal as a superfast (and typed) Ruby interpreter, in a sense.
IANAL, but I believe you cannot put "(c) Your Name 2020" on Shakespeare's works
in Japan and other countries. Their copyright acts protect the moral rights of authors[1][2]. So I think Zen's file headers would be violating the law in Japan and other countries.
> Although methods of a generic type may use the type's parameters, methods may not themselves have additional type parameters. Where it would be useful to add type arguments to a method, people will have to write a suitably parameterized top-level function.
> This is not a fundamental restriction but it complicates the language specification and the implementation.
# 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
# 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.
The release notes of Crystal 0.34 say "Having as much as possible portable code is part of the goal of the std-lib. One of the areas that were in need of polishing was how Errno and WinError were handled. The Errno and WinError exceptions are now gone, and were replaced by a new hierarchy of exceptions."
So I have modified
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
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: