User types and subtyping¶
In Arza programmer can define abstract and concrete types
Abstract types¶
type None
type Bool
type One
type LessThan
type GreaterThan
Such types has an instances of themselves and can be used as singleton values For example in ML family languages one could write
data Maybe a = Just a | Nothing
in Arza it would be just
type Maybe
// this is subtyping
type Nothing is Maybe
type Just(val) is Maybe
// now use this type as pattern
// all clauses will be successful here
match Just(1)
| maybe of Maybe
| maybe of Just
| {val} of Just
| {val=1 as value} of Just
// treating types as Tuples
| Just(val)
| Just(1)
// Treating types as maps
| Just{val=1 as value}
match Nothing
| maybe of Maybe
| maybe of Nothing
| type Nothing //need to distinguish between name and empy typeS
// TODO make simpler type literal
| x when x == Nothing
// now lets write some function
fun operation(data) =
if can_do_something(data) then
let val = do_something(data)
Just(val)
else
Nothing
Concrete types¶
Type Just
from example above is a concrete type. Such types when called like functions create records.
Records in Arza are something like structs in C or named tuples. Internally they differ from tuples because they
provide more efficient data sharing between mutated copies.
Records support both access by name and by field index.
It is forbidden to add new fields to records. You only create copy of existing ones with different values
let t = (1, 2, 3)
//this is highly undesirable
let t1 = put(t, 0, 42)
// instead
type Three(x, y, z)
let t2 = Three(1, 2, 3)
// much more efficient
let t3 = put(t2, 0, 42)
By default concrete types initalize fields in order of declaration in constructor, but programmer
can create custom initalizer. Such initializer is function defined with init
keyword.
Initializer receives uninitialized record as first argument and must set all of it’s declared fields. If any of the fields will not be set then exception will be thrown
type Library(available_books, loaned_books)
//initializer
init(lib, books of List) =
// here lib variable is an empty record with uninitialized fields
// returning modified copy of lib
lib.{available_books = books, loaned_books}
// lets write function for borrowing book from library
fun loan_book(library, book_index) =
let book = library.available_books.[book_index]
new_lib = library.{available_books = seq:delete(@, book), loaned_books = book::@}
//return tuple with book and modified library
(new_lib, book)
// reverse process
fun return_book(library, book) =
library.{
available_books = book::@,
loaned_books = seq:selete(@, book)
}
Subtyping¶
Arza supports nominal subtyping for abstract and concrete types. Type can have only one supertype and supertype can have multiple subtypes.
Concrete types can not be used as supetypes for abstract types.
Subtypes inherit behavior from supertypes and can be used in multiple dispatch in same roles.
When defining subtype from concrete supertype fields of supertype will be added to fields of would be subtype
type Vec2(x, y)
type Vec3 is Vec2 (z)
// Vec3 will have fields(x, y, z)
// defining generic method
def sum(v of Vec2) = v.x + v.y
let v2 = Vec2(1, 2)
let v3 = Vec2(1, 2, 3)
// because sum not defined for Vec3
sum(v2) == sum(v3)
//but after
def sum(v of Vec3) = v.x + v.y + v.z
sum(v3) == 6 != sum(v2)
If you don’t need behavioral subtyping but want to reuse fields from other types you can paste type in field declaration
type Vec2 (x, y)
// paste fields from Vec2
type Vec3 (...Vec2, z)
// Vec2 and Vec3 are unrelated
// More complex example
type AB(a, b)
type C(c)
type DE(d, e)
type FGH(f, g, h)
// paste multiple types in multiple position
type Alphabet (...AB, ...C, ...DE, ...FGH, i, j, k)