Generics & Traits
Generics are used for making types and functions more flexible.
Imagine a scenario such as
fn select cond x y as bool string string -> string =
if cond
then x
else y
fn main =
select (1 > 2) "this" "that"
. io:println
Here we're using the type string
. However; the function select
doesn't actually care which type its used with.
Instead; we use a generic type.
fn select cond x y as bool a a -> a =
...
When defining functions, any lowercase single-letter type will be treated as a generic type.
Generics can also be used for type declarations to grant type parameters for a declared type.
type WithId v {
id int
value v
}
fn create_user as string -> WithId User =
...
Traits
If type T {...}
defines what a type has, and type T = A | B
defines what a type can have, then trait
defines what a type should be able to do.
trait Animal
fn make_noise as string
fn move as (int, int) -> (int, int)
Traits can be used in one of two ways. Either as a type by itself, which we call a trait object. Or as a constraint for generic types.
// Used as trait object
fn add as Animal Zoo -> Zoo =
...
// Used as trait constraint
when
a can Animal
fn add as a Zoo -> Zoo =
...
To implement a trait for a type, use the impl
keyword.
type Cat
impl Animal for Cat
fn make_noise = "meow"
fn move (x, y) = (x + 5, y + 2)
TODO: Most of this only makes sense if you're used to Rust or some degree Haskell. It should be explained more fundamentally.