Let’s consider the following code:
multi :: String -> a
multi "Int" = (1 :: Int)
multi "Float" = (1 :: Float) Output:
Error occurred
ERROR line 5 - Type error in explicitly typed binding
*** Term : multi
*** Type : String -> Float
*** Does not match : String -> IntOr
multi :: String -> a
multi _ = 1.0Output:
Error occurred
ERROR line 4 - Cannot justify constraints in explicitly typed binding
*** Expression : multi
*** Type : String -> a
*** Given context : ()
*** Constraints : Fractional aOr even:
multi :: String -> a
multi _ = 1.0 :: FloatOutput:
Error occurred
ERROR line 4 - Inferred type is not general enoughSo that does not work. How about faking polymorphism via data type members?
data X = X1 Int | X2 Float | X3 String
data Z a = Z1 Int | Z2 Float | Z3 String
bar :: String -> Z a
bar s = case s of
_ -> Z3 undefined :: Z a
-- "Int" -> Z1 1 :: Z Int
foo :: String -> X
foo s = case s of
"Int" -> X1 1
"Float" -> X2 1.0
_ -> X3 "Vrotebal"
That works, but our functions/types aren’t polymorphic in the original sense… So let’s modify the first example:
multi :: String -> (forall a. a -> b) -> b
multi "Int" f = f (1 :: Int)
multi "Float" f = f (1 :: Float)Now think about what f can be :P…
To be useful, you’ve got to include information that would let the caller figure out which of the two you’ve returned The easiest is obviously data Foo = FooInt Int | FooFloat Float, like in the second code example above. There’s also Data.Typeable, where the compiler provides some magic to give you a “tag” for every type and an existential wrapper that preserves the tag…
So, on the one hand the type of ... :: String -> a exists and is legit, e.g. abort :: String -> a, error :: String -> a, absurd :: Void -> a, on the other hand they are, practically speaking, all special functions with seemingly no real way to make custom useful functions with the type of ... :: String -> a.
To see why this can’t work, consider
main = do
str <- getLine
let x = multi str -- What type does x have?
print x -- Instances are chosen at compile time. Which
-- instance should be picked here? What should
-- the output be?I hope that helps in case you ended up in a similar rabbit hole of haskell polymorphism.
Special thanks to the contributors, whose questions and comments have provoked this post: OlexP, Morrow, Xal, Ailrun the Corgi