I feel an excellent feature to Crystal would be the ability to create overloadable implicit type casts for any class. This would reduce the amount of boilerplate overloaded methods with slightly different parameter types simply to support extra types.
Consider this example:
class Foo
def initialize(@bar : Bar)
end
def implicit : Bar
@bar
end
end
class Bar
def initialize(@message : String)
end
def say
puts @message
end
end
def baz(bar : Bar)
puts bar.say
end
bar = Bar.new "Hello, world"
baz(bar) #=> Hello, world
# Passing an object of type `Bar` to `baz` yields expected functionality
foo = Foo.new bar
baz(foo) #=> Hello, world
# Passing an object of type `Foo` (which shares no common inherited ancestor
# with `Bar`) internally calls the `#implicit` method which determines how
# the type should mutate with the expected type.
# Thus, `baz(foo)` is identical to `baz(foo.implicit)`
In this example, there exists two types, Foo and Bar. The top-level method baz is responsible for invoking Bar#say. It is clear that baz will not accept Foo any time, as Foo does not share any inherited ancestors with Bar, and no overloads exist for baz that accept a Foo directly.
However, with the proposition of overloadable implicit type casts, Foo can define a sort of “implicit” method that explains to the compiler how the object should act as if it were used as another type. In this case, Foo has an instance variable, bar, that defines a clear instance of type Bar. Additionally, Foo defines a type-annotated implicit method that returns its instance variable of bar, so that when it is used in an implicit type cast, it pulls this value from its defined implicit type cast method.
This proposition would reduce the amount of redundant checks to responds_to?, and reduce the amount of overloaded methods with slightly different parameter types.
This feature is found in several other languages with well-defined approaches to OOP. For example, C# allows you to overload implicit type casts as an operator:
public static implicit operator Bar(Foo foo) {
return foo.Bar;
}
Additionally, C# also allows you to specify explicit over implicit to provide behavior when explicitly casting. (Bar) foo would yield this functionality.