... | ... | @@ -46,7 +46,7 @@ Products are useful when you want to keep certain values together. You can also |
|
|
### Lists
|
|
|
Lists are a collection of terms with the same type, and they can have an arbitrary length. Their type is denoted by the type of the list elements placed in brackets:
|
|
|
```
|
|
|
[] : [#a]
|
|
|
[] : ['a]
|
|
|
[1, 2, 3] : [int]
|
|
|
[1, 2, "hi"] // TYPE ERROR
|
|
|
```
|
... | ... | @@ -98,12 +98,12 @@ Abstraction type signatures are just the type of their parameters in a parenthes |
|
|
```
|
|
|
(+) : (int, int) -> int
|
|
|
print : (str) -> <>
|
|
|
cons : (#a, [#a]) -> [#a]
|
|
|
cons : ('a, ['a]) -> ['a]
|
|
|
```
|
|
|
|
|
|
### Universal Types
|
|
|
Some functions or structures are universally valid for all types. For example, the function `{|x| x}` just returns its argument, so it could work for all types. We'd write its type as `(#a) -> #a`, which means that for any argument of an arbitrary type `#a`, the function returns a value of that same type. These types are kind of like parameters for universal types (which can be seen as functions from types to other types...), and in mushroom they're prefixed with a `#`.
|
|
|
You'll often see the REPL deduce values to have some sort of universal type (like `[] : [#!3]`). The names of these types are strange-looking, but the `!3` part is essentially just a unique identifier for that type that's internally used for unification (by the way, you can't put exclamation points in your own types -- this is so your own types don't come into conflict with the variables that the typechecker makes up).
|
|
|
Some functions or structures are universally valid for all types. For example, the function `{|x| x}` just returns its argument, so it could work for all types. We'd write its type as `('a) -> 'a`, which means that for any argument of an arbitrary type `'a`, the function returns a value of that same type. These types are kind of like parameters for universal types (which can be seen as functions from types to other types...), and in mushroom they're prefixed with a `'`.
|
|
|
You'll often see the REPL deduce values to have some sort of universal type (like `[] : ['!3]`). The names of these types are strange-looking, but the `!3` part is essentially just a unique identifier for that type that's internally used for unification (by the way, you can't put exclamation points in your own types -- this is so your own types don't come into conflict with the variables that the typechecker makes up).
|
|
|
|
|
|
## Record Types
|
|
|
Sometimes, you'll want to name the members of product values in order to denote what they represent. For example, what if you want a datatype that represents a person (with an age and a name)?
|
... | ... | @@ -119,27 +119,27 @@ age(jack); // using a field accessor for the age |
|
|
Variant types represent an option between multiple types (hence the name). An example of variants would be types like `Maybe` in Haskell or `Option` in Rust.
|
|
|
In mushroom, each variant type has one or more possible tags, each of which hold zero or more values of certain types.
|
|
|
```
|
|
|
/* A very simple variant type -- it can either be the term @t or @f */
|
|
|
variant boolean { @t, @f };
|
|
|
@t // here's how you'd use a value in this variant
|
|
|
/* A very simple variant type -- it can either be the term #t or #f */
|
|
|
variant boolean { #t, #f };
|
|
|
#t // here's how you'd use a value in this variant
|
|
|
/* This type is either the @just tag with a value of an arbitrary type associated with it,
|
|
|
or a @nothing that that holds no associated values. */
|
|
|
variant maybe { @just[#a], @nothing };
|
|
|
@just[1]
|
|
|
@just[<1, "hi">]
|
|
|
@nothing
|
|
|
@just[@nothing]
|
|
|
variant maybe { #just<'a>, #nothing };
|
|
|
#just<1>
|
|
|
#just<<1, "hi">>
|
|
|
#nothing
|
|
|
#just<#nothing>
|
|
|
```
|
|
|
|
|
|
When a variant type has some type parameters (e.g. the #a in maybe), you can pass it concrete type arguments when you make other types, so you can have more specific instances of that type:
|
|
|
```
|
|
|
variant foo { @possible_int[maybe<int>], @possible_str[maybe<str>] };
|
|
|
variant foo { #possible_int<maybe<int>>, #possible_str<maybe<str>> };
|
|
|
```
|
|
|
|
|
|
Variants can also be recursive. To make a variant refer to itself, use the `self` keyword:
|
|
|
```
|
|
|
variant linked_list { @nil, @cons[#a, self] };
|
|
|
@cons[1, @cons[2, @nil]] : linked_list
|
|
|
variant linked_list { #nil, #cons<'a, self> };
|
|
|
#cons<1, #cons<2, #nil>> : linked_list
|
|
|
```
|
|
|
|
|
|
## Pattern Matching
|
... | ... | @@ -154,7 +154,7 @@ match <1, 2> { |
|
|
}
|
|
|
|
|
|
/* matches are often useful with variants, and can be used as values */
|
|
|
let x = match @just[2] { @just[n] => n + 1, @nothing => 0 }; // x is 3
|
|
|
let x = match #just<2> { #just<n> => n + 1, #nothing => 0 }; // x is 3
|
|
|
|
|
|
/* you can have multiple statements in the result of a match branch */
|
|
|
match %Person{ name: "Voldemort", age: 70 } {
|
... | ... | @@ -172,8 +172,8 @@ You can also _pin_ values in match statements. This means that values are evalua |
|
|
/* Who needs a built-in equality function when there's match statements? */
|
|
|
fn equal(x, y) {
|
|
|
match x {
|
|
|
^y => @t,
|
|
|
_ => @f
|
|
|
^y => #t,
|
|
|
_ => #f
|
|
|
}
|
|
|
};
|
|
|
|
... | ... | |