11

I was doing some code golf in Haskell just now, and I ran into an error that didn't make much sense to me at the time. Decided to check it out in GHCi, and now I'm truly baffled.

λ> :t replicate <$> readLn
replicate <$> readLn :: IO (a -> [a])
λ> f <- replicate <$> readLn
-- I type 4 and press Enter
λ> :t f
f :: GHC.Types.Any -> [GHC.Types.Any]

Why is f not of type a -> [a]? I can unsafeCoerce, of course, but that's lengthy and hideous.

  • 1
    I think it's due to the [monomorphism restriction](https://wiki.haskell.org/Monomorphism_restriction) but I don't understand Haskell well enough to explain. [This question](https://stackoverflow.com/questions/38649638/why-does-the-type-of-a-function-change-when-it-comes-out-of-a-monad-in-ghci) explains a similar case (but not close enough to be a duplicate). – Gilles 'SO- stop being evil' May 14 '20 at 19:52
  • @Gilles'SO-stopbeingevil', this is really much more about impredicative types than about the moromorphism restriction. The monomorphism restriction is turned off in GHCi by default anyway. – dfeuer Nov 09 '21 at 20:22

2 Answers2

8

IO (a -> [a]) is a polymorphic type. Expanded, it means forall a. IO (a -> [a]). Now, there are two things that don't work here. One, this is a polymorphic IO action that produces a monomorphic function. Essentially, each execution of this action will produce a function for one type. a -> [a] is not really a valid type, but if you meant you wanted a forall a. a -> [a], you won't get one:

main = do
    f <- replicate <$> readLn
    print (f (5 :: Int)) -- f can be *one of* Int -> [Int] or Float -> [Float], but not both
    print (f (5 :: Float)) -- doesn't compile, comment either line out and it will

Two, GHC doesn't support impredicative polymorphism. Theoretically, if you had written your IO action correctly, you could have gotten it to be IO (forall a. a -> [a]), but GHC doesn't support putting polymorphic types (like forall a. a -> [a]) into containers, like IO.

In your case, since you don't use f, GHC doesn't know which type it should instantiate the action at, but it has to choose one, so it defaults to Any.

Edit: the conventional way to get around the "no impredicative types" restriction is to hide them into newtypes:

{-# LANGUAGE RankNTypes #-}
-- interestingly, this is a numeric type (it represents the natural numbers)
newtype Replicator = Replicator { runReplicator :: forall a. a -> [a] }
mkReplicator :: Int -> Replicator
mkReplicator i = Replicator (replicate i)
-- mkReplicator =# replicate
main = do
    Replicator f <- mkReplicator <$> readLn
    print (f (5 :: Int))
    print (f (5 :: Float)) -- should work now

Probably not worth it...

HTNW
  • 27,182
  • 1
  • 32
  • 60
  • "GHC doesn't support putting polymorphic types (like `forall a. a -> [a]`) into containers, like IO." Your answer has made me curious about whether the `RankNTypes` extension would allow this. Normally it's used to put such a polymorphic type to the left of a function arrow, but I never thought if it allowed the use of one inside a type constructor. – Robin Zigmond May 14 '20 at 20:04
  • Just the answer I was looking for. The original error arose when I tried using `f . f`—that first paragraph explains it perfectly. Many thanks! – Khuldraeseth na'Barya May 14 '20 at 20:16
  • @RobinZigmond Yes, `RankNTypes` is an exception. But that's not this, and you would need `ImpredicativeTypes` to make this work. Currently, I think that one is just completely broken. It *would* be nice to have, and I think making it work is a (becoming?) a long term goal. – HTNW May 14 '20 at 20:54
1

There are two issues here. One is illustrated by this code:

Prelude> do { f <- return (replicate 4); print (f 'a'); print (f 'b') }
"aaaa"
"bbbb"
Prelude> do { f <- return (replicate 4); print (f 'a'); print (f "b") }
    [...]
    * Couldn't match expected type `Char' with actual type `[Char]'
    [...]

A limitation of Haskell's type system results in f being monomorphic, that is, only being usable at one type (of your choice). HTNW already covered this.

The second problem is specific to GHCi. It's illustrated by this code:

Prelude> do { f <- return (replicate 4); print (f 'a') }
"aaaa"
Prelude> f <- return (replicate 4)
Prelude> print (f 'a')
    [...]
    * Couldn't match expected type `GHC.Types.Any'
                  with actual type `Char'
    [...]

GHC has two ways of assigning a type to an expression whose type is ambiguous. One is numeric defaulting, which isn't relevant here. The other applies when there are no typeclass constraints on the type, numeric or otherwise. In that case, the type can't affect the program's run time behavior, so it doesn't matter what you pick. GHC uses GHC.Types.Any.

Normally, you would never see the result of the second kind of defaulting, because it happens only after the compiler knows that the type doesn't matter.

GHCi, though, applies these defaulting rules after every line you type, giving you no chance to constrain the type with later code. So instead of any type you want, you get any type you want so long as it's Any.

benrg
  • 1,395
  • 11
  • 13
  • I don't think there's any sensible way to defer the defaulting in general. When you write `x <- e` at the GHC prompt, the I/O performed by `e` might well depend on its type. For the special case of unconstrained polymorphism, yes, it would be nice to defer the choice of type until it is used, or, in some cases when `ImpredicativeTypes` is enabled, to generalize the type variable(s) instead. But I imagine that would add significant complexity for relatively limited benefit. – dfeuer Nov 09 '21 at 20:30
  • @dfeuer I removed the text calling it a bug. You're right that it couldn't match the behavior of non-interactive Haskell in cases like `x <- return 3`, `print x`, `print (x+1.5)`. You're probably right that it would add significant complexity. – benrg Nov 09 '21 at 20:48
  • Ah, yeah, good point, because the first `print x` will lead to `Integer` defaulting, while the `print (x + 1.5)` would require it to have been defaulted to `Double`. Yech. – dfeuer Nov 09 '21 at 20:50