PureScript has a special syntax for combining records:
type Common = ( a :: Int, b :: Int )
type Record1 = { y :: String | Common }
type Record2 = { z :: Boolean | Common }
newtype RecordType3 = RecordType3 { w :: Number | Common }
Note that the definition of Common
uses parentheses, not curly braces. That is because Common
is a row, not a record. You can make a record out of it though:
type CommonRec = Record Common
-- equivalent to: CommonRec = { a :: Int, b :: Int }
In fact, the curly braces notation is just syntactic sugar for applying Record
to a row. An expression { xyz }
gets desugared to Record ( xyz )
.
You can use the "pipe" syntax to extend rows as well:
type CommonPlusFoo = ( foo :: Bar | Common )
type RecWithFoo = { x :: Int | CommonPlusFoo }
You can also make your record types polymorphic by providing Common
as a type parameter:
type Record1Poly r = { y :: String | r }
type Record1 = Record1Poly Common
This is very handy for writing functions that work with partial records, e.g.:
updateName :: forall r. { name :: String | r } -> { name :: String | r }
updateName x = x { name = "Mr. " <> x.name }
jones = { name: "Jones", occupation: "Plumber" }
mrJones = updateName jones -- mrJones = { name: "Mr. Jones", occupation: "Plumber" }
In this example, the function can work with any record that has a name
field, regardless of what else it might have.
Finally, to express an empty row, use empty parens:
type Record1Poly r = { y :: String | r }
type Record1 = Record1Poly Common
type OnlyY = Record1Poly ()
On a slightly unrelated topic, note that records in PureScript are not the same as records in Haskell. For example, above Record1
and Record2
are true PureScript ad-hoc extensible records (something that Haskell doesn't have), but RecordType3
is a newtype that has one constructor whose parameter is a record.
One important difference is that, unlike Haskell, this wouldn't work:
x = RecordType3 { w: 42.0, a: 1, b: 2 }
y = w x
The expression w x
(or even expression x.w
) doesn't compile, because RecordType3
is not itself a record, it's a newtype that wraps a record. In order to get w
out of it you need to match on the constructor first:
(RecordType3 k) = x
y = k.w
Or wrap that as an accessor function:
unRecordType3 (RecordType3 k) = k
y = (unRecordType3 x).w
In practice this is really inconvenient if you're approaching records with a Haskell mindset. Instead, what you want to do in PureScript is prefer "naked" records (like Record1
and Record2
in my example above) and only resort to wrapping them in newtype
when you really have to.