Records
Records and row polymorphism are the core of Novah data structures. Records are immutable and persistent, so functions that change records will always return a new version. Novah records support duplicated fields, but you can only retrieve the last value of a field.
Accessing fields
Fields can be accessed using dot syntax.
Begin code:
module records
name : { name : String | r } -> String
name person = person.name
// nested record access
city : { address : { city : String } | r } -> String
city person = person.address.city
End code.
String fields
Novah requires every field to be a valid identifier but it's possible to use any string as a field.
Begin code:
module records
stringFields : { "Password" : String, "User Name" : String }
stringFields = { "User Name": "foo", "Password": "***"}
utfFields : { "∀" : String, "∉" : Boolean, "😉" : String }
utfFields = {"😉": "wink", "∉": true, "∀": "forall"}
End code.
Adding new fields
Begin code:
module records
addData :
{ name : String }
-> { name : String, age : Int, eyeColor : String }
addData person = { age: 31, eyeColor: "blue" | person }
// using anonymous lambdas
addData2 :
{ name : String }
-> { name : String, age : Int, eyeColor : String }
addData2 = { age: 31, eyeColor: "blue" | _ }
// the original name cannot be accessed until the current is removed
duplicates : { name : String } -> { name : String, name : String }
duplicates person = { name : "Override" | person }
End code.
Removing fields
Begin code:
module records
typealias Person = { name : String, age : Int}
nameless : Person -> { age : Int }
nameless = { - name | _ }
// removing multiple fields
nothing : Person -> {}
nothing = { - name, age | _ }
End code.
Updating and setting fields
Setting a new value to a field can be done using the { .field = newValue | record }
syntax.
Updating a field can be done using the { .field -> function | record }
syntax.
Begin code:
module records
setName : String -> { name : String | r } -> { name : String | r }
setName newName = { .name = newName | _ }
upperName : { name : String | r } -> { name : String | r }
upperName = { .name -> String.upperCase | _ }
// you can update nested fields
nested :
{ address : { city : String | r2 } | r }
-> { address : { city : String | r2 } | r }
nested = { .address.city = "Tokyo" | _ }
End code.
Merging records
Begin code:
module records
typealias Person = { name : String, age : Int}
tim : Person
tim = { name: "Tim", age: 21 }
merge : { age : Int, eye : String, height : Float64, name : String }
merge = { + tim, { eye: "Blue", height: 33.2 }}
// duplicates are allowed
dups : { age : Int, age : Int, name : String }
dups = { + tim, { age: 44 }}
// it's not possible to merge two records with unknow labels
// at least one of the records should have no unknow labels
#[noWarn]
fail rec1 rec2 = { + rec1, rec2}
End code.