Generics allow you to parameterize a type based on other type. Consider a Box type:
class MyBox(T) def initialize(@value : T) end def value @value end end int_box = MyBox(Int32).new(1) int_box.value # => 1 (Int32) string_box = MyBox(String).new("hello") string_box.value # => "hello" (String) another_box = MyBox(String).new(1) # Error, Int32 doesn't match String
Generics are especially useful for implementing collection types.
Set are generic types, as is
More than one type argument is allowed:
class MyDictionary(K, V) end
Any name can be used for type arguments:
class MyDictionary(KeyType, ValueType) end
Type restrictions in a generic type's constructor are free variables when type arguments were not specified, and then are used to infer them. For example:
MyBox.new(1) # : MyBox(Int32) MyBox.new("hello") # : MyBox(String)
In the above code we didn't have to specify the type arguments of
MyBox, the compiler inferred them following this process:
initialize(@value : T)
Tisn't bound to a type yet, so the compiler binds it to the type of the given argument
In this way generic types are less tedious to work with.
Structs and modules can be generic too. When a module is generic you include it like this:
module Moo(T) def t T end end class Foo(U) include Moo(U) def initialize(@value : U) end end foo = Foo.new(1) foo.t # Int32
Note that in the above example
Int32, which in turn makes
Int32 via the inclusion of the generic module.
Generic classes and structs can be inherited. When inheriting you can specify an instance of the generic type, or delegate type variables:
class Parent(T) end class Int32Child < Parent(Int32) end class GenericChild(T) < Parent(T) end
To the extent possible under law, the persons who contributed to this workhave waived
all copyright and related or neighboring rights to this workby associating CC0 with it.