4

I am confused about the following program.

{-# LANGUAGE RankNTypes #-}
newtype A a = A ( forall f. Applicative f => f a )

good :: a -> A a
good x = A $ pure $ x

bad  :: a -> A a
bad  x = A . pure $ x

When trying to compile, I get this error message, complaining about bad:

Couldn't match type `f0 a'
               with `forall (f :: * -> *). Applicative f => f a'
Expected type: f0 a -> A a
  Actual type: (forall (f :: * -> *). Applicative f => f a) -> A a
Relevant bindings include
  x :: a (bound at huh.hs:8:6)
  bad :: a -> A a (bound at huh.hs:8:1)
In the first argument of `(.)', namely `A'
In the expression: A . pure

Why does the function good typecheck, while ghc refuses to accept the function bad? And what could I do to make the latter version work? As far as I can see, both examples should be equivalent.

Hjulle
  • 2,471
  • 1
  • 22
  • 34
  • 2
    [Related](http://stackoverflow.com/questions/9468963/runst-and-function-composition). – effectfully Oct 23 '15 at 01:22
  • As well as this one: http://stackoverflow.com/questions/8343239/type-error-with-rank-2-types-and-function-composition. – Hjulle Oct 23 '15 at 01:27
  • 2
    In short: Haskell's type system is predictive, which means that it will not instantiate a type variable with a polymorphic type (one with `forall`s). In order for `bad` to typecheck, the type checker would need to instantiate some type variable with `forall (f :: * -> *). Applicative f => f a`. Even if you turn on `-XImpredictiveTypes`, though, you'll still need a type annotation on `pure`. – user2407038 Oct 23 '15 at 01:27
  • Thanks to both of you. I should probably have researched better. – Hjulle Oct 23 '15 at 01:34
  • @user3237465 Yes, that answers both of my questions. I'm voting to close this as a duplicate. – Hjulle Oct 23 '15 at 01:37
  • 3
    @user2407038, predicAtive and impredicAtive. – effectfully Oct 23 '15 at 02:04
  • 2
    Never use `ImpredicativeTypes` unless it actually gets fixed some day. – dfeuer Oct 23 '15 at 02:05

1 Answers1

2

As was explained in several comments, the problem is that the type system can't predict that this is a valid type, even though it is. In this answer there is a hint that you can specify the type of . explicitly to solve the problem.

This code snippet works:

-- Type specialized composition
(.!) :: ((forall f. Applicative f => f b) -> c) ->
        (a -> (forall f. Applicative f => f b)) -> a -> c
(.!) f g x = f(g x)

-- Use the new version of composition
notBad  :: a -> A a
notBad  x = A .! pure $ x
Community
  • 1
  • 1
Hjulle
  • 2,471
  • 1
  • 22
  • 34