In his talk
Maybe Not,
Rich Hickey explores a way of thinking about data in terms of the
functions that work on them, rather than baking in any assumptions
into the system directly. He's trying to take his spec system in
this direction. I also have a keen interest in moving over to using
records more often in typed Haskell-like languages.
An example from the slides was:
(s/select ::user [::id ::addr {::addr [::zip]}])This expresses that I just want the fields id, addr and from
within addr I just want zip. If the data structure has more
things, I don't care about that. I just care about the fields I need
to do my work.
The way to express this in PureScript (a few years old language like Haskell) is:
forall user addr. { id :: Int, addr :: { zip :: String | addr } | user } -> {}Which says the same thing, the syntax { f1 :: T1, f2 :: T2 | r }
says "for some record r, it has at least fields f1 of type T1
and f2 of type T2. You can pass a function expecting this type
something like:
{ id: 1, address: { zip: "M3123", address1: "2 King Street" } }The fields id, address, and zip must be present in the
input.
Rich also mentions having a list of schemas, which can be written as, e.g.
contactFriend :: forall friend.
Array { name :: String, number :: String | friend }
-> {}
contactFriend r = {}I can return the given record, too:
uppercaseName :: forall friend. { name :: String | friend } -> { name :: String | friend }
uppercaseName r = r { name: upperCase r.name }I can also delete, insert, rename or union/merge or do a disjoint
union on two records, via
functions from the Record module.
For example, use disjointUnion to combine two records, disallowing
duplicate fields:
import Record
person = { name: "Chris", address: { country: "UK" } }
creds = { user: "wibble", pass: "woop" }
combined = disjointUnion person credsThe compiler will issue a helpful warning, offering the following type
signature for combined:
No type declaration was provided for the top-level declaration of combined.
It is good practice to provide type declarations as a form of documentation.
The inferred type of combined was:
{ address :: { country :: String
}
, name :: String
, pass :: String
, user :: String
}