Reputation: 107
So in an exercise I am given a list like ["xyz", True, 42]
. The question was if that is a valid expression in Haskell and what the type of that expression is.
A list can only hold homogenous types but the type of "xyz"
is [Char]
, the type of True
is Bool
and the type of 42
is Num p => p
. That is different types so I can't put them into a list.
That's what I thought. But the given answer to that exercise is "Yes, it is a valid expression. Show-instance!."
Why is it a valid expression although the types of the list elements are different and what is meant with show-instance? I'm thinking of something like superclasses from object oriented languages but I thought this is not how Haskell works.
Upvotes: 3
Views: 122
Reputation: 107
My lecturer gave me a hint to check for Existential types in Haskell.
I produced a working example from the description from the link above:
{-# LANGUAGE ExistentialQuantification #-}
module Main where
data Showable = forall a . Show a => MkShowable a
pack:: Show a => a -> Showable
pack a= MkShowable a
instance Show Showable where
show (MkShowable a) = show a
hlist :: [Showable]
hlist = [pack "xyz", pack True, pack 42]
main :: IO ()
main = do
putStrLn "Heterogenous list 'camouflaged' as Showable:"
print hlist
This works and produces indeed the input from the exercise. The datatype extension for Existential Quantification in the first line is necessary.
My explanation (I might have gotten something wrong though):
I create a new type Showable
with one constructor MkShowable
that takes any value a
as long as it is in typeclass Show
and thus makes a Showable
out of it.
The method pack makes a Show a
become a Showable
by using the constructor MkShowable
I described in 1.
Showable
is made an instance of Show
-typeclass and tells that if a Showable
(MkShowable a
) is to be shown, simply show a
. So we can easily print our Showable
s.
Furthermore I created a (heterogenous) list hlist
of type [Showable]
and packed the values from my example into it, using pack
. The list is printed in the main function.
This really reminds me of object-oriented programming.
Upvotes: 0
Reputation: 476739
If we are allowed to define some more context, we can make this a valid expression, for instance with:
import Data.String(IsString(fromString))
instance IsString Bool where
fromString [] = False
fromString _ = True
instance Num Bool where
(+) = (||)
(*) = (&&)
abs = id
signum = id
fromInteger 0 = False
fromInteger _ = True
negate = not
(here I used the truthiness of Python to convert from an Integer
and String
literal)
Then we can write it with the OverloadedStrings
pragma:
{-# LANGUAGE OverloadedStrings #-}
the_list = ["xyz", True, 42]
This will then be equivalent to:
Prelude Data.String> ["xyz", True, 42]
[True,True,True]
But note that the list still contains only Bool
s, we only made Bool
an instance of IsString
and Num
to enable us to convert string literals and number literals to Bool
s.
A list of heterogeneous types is not possible in Haskell, and since by default a Bool
is not a Num
, we thus can not parse that expression without adding some extra magic.
An additional note is that it is valid Haskell grammar: syntactically there is nothing wrong, it is only in the next stage of the compiler: type checking, etc. that it will raise errors, since the syntax is nonsensical.
Upvotes: 6