Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 936
Khuldraeseth na'Barya Avatar asked May 14 '20 19:05

Khuldraeseth na'Barya


2 Answers

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

like image 55
HTNW Avatar answered Oct 11 '22 05:10

HTNW


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.

like image 24
benrg Avatar answered Oct 11 '22 03:10

benrg