0xd34df00d
0xd34df00d

Reputation: 1505

Function types as MonadReader

I'm going through the lens tutorial and, in particular, trying to wrap my head around the types involved in view.

So, taking the definitions

data Atom = Atom { _element :: String, _point :: Point }
data Point = Point { _x :: Double, _y :: Double }
makeLenses ''Atom
makeLenses ''Point

let's inspect the type of the expression view (point . x):

*Main> :t view (point . x)
view (point . x) :: MonadReader Atom m => m Double

So, given an environment with an Atom we can produce a Double wrapped in a MonadReader. Nice, that's even more generic than a lens newbie could expect!

Now, let's try to apply this to some atom:

*Main> :t view (point . x) (Atom "C" (Point 1 2))
view (point . x) (Atom "C" (Point 1 2)) :: Double

How does this type-check given the above type for view (point . x)? I know there is an instance of MonadReader for the partially applied function type r ->, but how is ghc able to figure out that this particular instance shall be used in this case (if that's the one that's indeed used, of course)?

Upvotes: 3

Views: 59

Answers (1)

chi
chi

Reputation: 116139

The key point is that you used the syntax for application.

When you write

view (point . x) (Atom "C" (Point 1 2))

you are essentially writing the application f a where f = view (point . x) and a = Atom "C" (Point 1 2). To type check f a GHC initially starts from

f :: t1 -> t2     -- in prefix for rm, this is written as (->) t1 t2
a :: t1

for some fresh type variables t1, t2. This is always done for all applications. (Well, almost. Some advanced types require a more careful algorithm, but I'll neglect that.)

However, GHC also knows

f :: MonadReader Atom m => m Double
a :: Atom

so it deduces t1 ~ Atom and m ~ (->) t1 ~ (->) Atom. Since this is indeed a MonadRead Atom instance, it proceeds with that.

Upvotes: 4

Related Questions