After reading around a bit, it seems that the current record situation in Haskell is a bit sticky.
Take, for example, the StateT
newtype. Both the code
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
and the docs
Constructors
StateT
runStateT :: s -> m (a, s)
say that the type of runStateT
is s -> m (a,s)
. However, GHCi show that the type really is
> :t runStateT
runStateT :: StateT s m a -> s -> m (a,s)
Is there any explanation for this discrepancy? Do the identifier in the record and the function refer to two different things, which GHC magically resolves behind the scenes? Although I see why it is nice to write s -> m (a,s)
in the record, it just seems wrong.
This is actually correct, remember that the name of a record field means two different things in different contexts,
GHCi
is telling you what the function means, but the docs are talking about the record field itself, not the function it will generate.
Essentially behind the scenes this
data Foo = Foo {fooy :: Int}
will generate
fooy :: Foo -> Int
fooy Foo{fooy=fooy} = fooy
-- Equivalently: fooy Foo{fooy=bar} = bar
This function fooy
is what you toss around in code, but the docs are talking about the record selector, which we see in the Foo{fooy=...}
part of our code.
The type of the runStateT
field is s -> m (a, s)
. The type of the runStateT
field accessor function is StateT s m a -> s -> m (a, s)
.
Let's take a simpler example:
data Foo = Bar {foo :: Int}
The type of the foo
field (i.e., the type of values you can put into the Bar
constructor) is just Int
.
The type of the foo
function is Foo -> Int
.
To illustrate further:
Bar {foo = 5}
5 is clearly an Int
.
let x = foo (Bar 5) in ...
Here foo
is applied to Bar 5
, which has type Foo
. So foo
is taking a Foo
and giving us 5 — which is an Int
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With