Kiara Grouwstra
Kiara Grouwstra

Reputation: 5923

issues reproducing Haskell lens tutorial

I feel like I am doing something wrong, as I am not even managing to reproduce Haskell's lens tutorial:

> import Control.Lens
> data Point = Point { _x :: Double, _y :: Double } deriving (Show)
> data Atom = Atom { _element :: String, _point :: Point } deriving (Show)
> point = lens _point (\atom newPoint -> atom { _point = newPoint })
> :t point
point :: Functor f => (Point -> f Point) -> Atom -> f Atom
> point :: Lens' Atom Point = lens _point (\atom newPoint -> atom { _point = newPoint })

<interactive>:6:10: error:
    • Illegal polymorphic type: Lens' Atom Point
      Perhaps you intended to use RankNTypes or Rank2Types
    • In a pattern type signature: Lens' Atom Point
      In the pattern: point :: Lens' Atom Point
      In a pattern binding:
        point :: Lens' Atom Point
          = lens _point (\ atom newPoint -> atom {_point = newPoint})
> :set -XRankNTypes
> point :: Lens' Atom Point = lens _point (\atom newPoint -> atom { _point = newPoint })

<interactive>:8:29: error:
    • Couldn't match type ‘(Point -> f0 Point) -> Atom -> f0 Atom’
                     with ‘forall (f :: * -> *).
                           Functor f =>
                           (Point -> f Point) -> Atom -> f Atom’
      Expected type: Lens' Atom Point
        Actual type: (Point -> f0 Point) -> Atom -> f0 Atom
    • In the expression:
        lens _point (\ atom newPoint -> atom {_point = newPoint})
      In a pattern binding:
        point :: Lens' Atom Point
          = lens _point (\ atom newPoint -> atom {_point = newPoint})

It certainly seems to be seeing some difference between f0 vs Functor f.

My code here is no different from that in the tutorial though, and no amount of extensions appears to be saving me from as far as I can tell.

Would anyone have any pointers, perhaps?

Upvotes: 1

Views: 69

Answers (1)

Carl
Carl

Reputation: 27013

I've confirmed the guess in my comment. This is because you put the type in the pattern match. It may be a trivial pattern match, but it is a pattern match, as it's binding a name on the LHS of an =.

That isn't even allowed by default in Haskell. In GHC, you need to enable the ScopedTypeVariables extension before it is allowed. And when you do it, it has strange impacts on polymorphism. I don't understand all the interactions between that feature and higher-rank polymorphism, but it definitely is monomorphizing a type and also expecting it to be polymorphic. That's just not going to work.

I strongly recommend that you only put types in patterns if you're binding a name for the type itself along with a value. Sometimes that comes up when working with existentials, but I've never seen it come up in any other case.

Otherwise, just use the normal type ascription syntax, even if you have to throw in a few semicolons to make it work in ghci:

point :: Lens' Atom Point ; point = lens _point (\atom newPoint -> atom { _point = newPoint })

Upvotes: 2

Related Questions