In the expression
data Frank a b = Frank {frankField :: b a} deriving (Show)
What does {frankField :: b a}
means?
Is {frankField :: b a}
a type constructor? If so, should the parameters look like b->a
instead b a
?
Frank
is a type of kind * -> (* -> *) -> *
, that is, it takes a type a
(of kind *
) such as Int
, Char
, or String
; and a unary type constructor b
(of kind * -> *
) such as Maybe
or Either String
. (You can check the kind of a type using the :kind
or :k
command in GHCi.)
It has one constructor, also named Frank
, which contains one field (not a constructor) of type b a
called frankField
—for example, the type of frankField
in a value of type Frank Int Maybe
is Maybe Int
, since b
= Maybe
and a
= Int
, so b a
= Maybe Int
.
This definition is using record notation to give a name to the field—you could also have written just data Frank a b = Frank (b a)
to leave it anonymous, but the advantage of naming the field is that you can use explicit record syntax to construct a Frank
value:
frank1 :: Frank Int Maybe
frank1 = Frank { frankField = Just 1 }
Or to modify a value:
frank2 :: Frank Int Maybe
frank2 = frank1 { frankField = Nothing }
Or access the field by name:
value :: Maybe Int
value = frankField frank1
This is more convenient, and more common, when a constructor includes multiple fields; also, you’ll typically see newtype
instead of data
when a type wraps only a single value, since newtype
has less overhead and slightly different laziness semantics.