NovahGithubSource

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.