Search code examples
haskelltypesghccoercion

Why GHC.Types.Any here?


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.


Solution

  • 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...