Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent types for record fields

Tags:

haskell

record

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.

like image 512
crockeea Avatar asked Dec 26 '22 12:12

crockeea


2 Answers

This is actually correct, remember that the name of a record field means two different things in different contexts,

  1. A function from the type of the value it's in to the individual field.
  2. A magical record field which can be used in pattern matching and construction

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.

like image 195
Daniel Gratzer Avatar answered Jan 08 '23 17:01

Daniel Gratzer


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.

like image 38
MathematicalOrchid Avatar answered Jan 08 '23 15:01

MathematicalOrchid