Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gentle Intro to Haskell: " .... there is no single type that contains both 2 and 'b'." Can I not make such a type ?

Tags:

haskell

scala

I am currently learning Haskell, so here are a beginner's questions:

What is meant by single type in the text below ?

Is single type a special Haskell term ? Does it mean atomic type here ?

Or does it mean that I can never make a list in Haskell in which I can put both 1 and 'c' ?

I was thinking that a type is a set of values.

So I cannot define a type that contains Chars and Ints ?

What about algebraic data types ?

Something like: data IntOrChar = In Int | Ch Char ? (I guess that should work but I am confused what the author meant by that sentence.)

Btw, is that the only way to make a list in Haskell in which I can put both Ints and Chars? Or is there a more tricky way ?

A Scala analogy: in Scala it would be possible to write implicit conversions to a type that represents both Ints and Chars (like IntOrChar) and then it would be possible to put seemlessly Ints and Chars into List[IntOrChar], is that not possible with Haskell ? Do I always have to explicitly wrap every Int or Char into IntOrChar if I want to put them into a list of IntOrChar ?

From Gentle Intro to Haskell:

Haskell also incorporates polymorphic types---types that are universally quantified in some way over all types. Polymorphic type expressions essentially describe families of types. For example, (forall a)[a] is the family of types consisting of, for every type a, the type of lists of a. Lists of integers (e.g. [1,2,3]), lists of characters (['a','b','c']), even lists of lists of integers, etc., are all members of this family. (Note, however, that [2,'b'] is not a valid example, since there is no single type that contains both 2 and 'b'.)

like image 735
jhegedus Avatar asked Dec 26 '22 11:12

jhegedus


1 Answers

Short answer.

In Haskell there are no implicit conversions. Also there are no union types - only disjoint unions(which are algebraic data types). So you can only write:

someList :: [IntOrChar]
someList = [In 1, Ch 'c']

Longer and certainly not gentle answer.

Note: This is a technique that's very rarely used. If you need it you're probably overcomplicating your API.

There are however existential types.

{-# LANGUAGE ExistentialQuantification, RankNTypes #-}
class IntOrChar a where
   intOrChar :: a -> Either Int Char

instance IntOrChar Int where
   intOrChar = Left

instance IntOrChar Char where
   intOrChar = Right 

data List = Nil
          | forall a. (IntOrChar a) => Cons a List

someList :: List
someList = (1 :: Int) `Cons` ('c' `Cons` Nil)

Here I have created a typeclass IntOrChar with only function intOrChar. This way you can convert anything of type forall a. (IntOrChar a) => a to Either Int Char.

And also a special kind of list that uses existential type in its second constructor. Here type variable a is bound(with forall) at the constructor scope. Therefore every time you use Cons you can pass anything of type forall a. (IntOrChar a) => a as a first argument. Consequently during a destruction(i.e. pattern matching) the first argument will still be forall a. (IntOrChar a) => a. The only thing you can do with it is either pass it on or call intOrChar on it and convert it to Either Int Char.

withHead :: (forall a. (IntOrChar a) => a -> b) -> List -> Maybe b
withHead f Nil = Nothing
withHead f (Cons x _) = Just (f x)

intOrCharToString :: (IntOrChar a) => a -> String
intOrCharToString x = 
   case intOrChar of
    Left i -> show i
    Right c -> show c

someListHeadString :: Maybe String
someListHeadString = withHead intOrCharToString someList

Again note that you cannot write

{- Wont compile
safeHead :: IntOrChar a => List -> Maybe a
safeHead Nil = Nothing
safeHead (Cons x _) = Just x
-}

-- This will
safeHead2 :: List -> Maybe (Either Int Char)
safeHead2 Nil = Nothing
safeHead2 (Cons x _) = Just (intOrChar x)

safeHead will not work because you want a type of IntOrChar a => Maybe a with a bound at safeHead scope and Just x will have a type of IntOrChar a1 => Maybe a1 with a1 bound at Cons scope.

like image 109
projedi Avatar answered May 18 '23 13:05

projedi