
Im having difficulty to understand what phantom types are good for. Is this just for improving runtime performance?
No. As the wiki says, you can use them to add static guarantees.
I read the wiki, and it says "this is useful if you want to increase the type-safety of your code", but the code below does not give a compiler error for the function test1, I get a runtime error, just like test2.
It seems you're mixing up GADT's and phantom types.
-- CODE -- -- With phantom types data T1 a = TI1 Int | TS1 String deriving Show
Here, the 'a' is an extra type parameter, which has no manifestation on the value level. Note the type of the constructors: TI1 :: Int -> T1 a TS1 :: String -> T1 a In particular, the 'a' is not related to the 'Int' or 'String' arguments.
foo1 :: T1 String -> T1 String -> T1 String foo1 (TS1 x) (TS1 y) = TS1 (x++y)
test1 = foo1 (TI1 1) (TI1 2) -- Shouldn't this give a compiler error instead of a runtime error?
'TI1 1' has type 'T1 a', so this unifies with 'T1 String' (the type of the argument of 'foo1'. The type parameter 'a' can still be useful, but you have to use an explicit type signature to constrain the type 'a': ti1 :: Int -> T1 Int ti1 x = TI1 x Now, 'ti1' will create values with the restricted type 'T1 Int', that you can't use as arguments for your 'foo1'. GADTs are perhaps more useful for what you seem to want. Try something like this: data T1 :: * -> * where -- T1 has one type parameter TI1 :: Int -> T1 Int TS1 :: String -> T1 String Now, the type systems guarantees that all values of the form 'TI1 x' have type 'T1 Int'. Greetings, Arie