In my attempts to grasp Existential Types in Haskell I decided to implement an integer-based fixed-length vector data type. I'm using ghc 7.8.3.
Specifically I wanted to write a program which asks the user for a possible new value to append to a fixed-length vector and then displays the resulting vector.
First I wrote a first version of the program like this:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE Rank2Types #-}
import System.IO (hFlush, stdout)
data Z
data S n
data Vect n where
ZV :: Vect Z
CV :: Int -> Vect n -> Vect (S n)
data AnyVect = forall n. AnyVect (Vect n)
instance Show (Vect n) where
show ZV = "Nil"
show (CV x v) = show x ++ " : " ++ show v
vecAppend :: Int -> Vect n -> Vect (S n)
vecAppend x ZV = CV x ZV
vecAppend x v = CV x v
appendElem :: AnyVect -> IO AnyVect
appendElem (AnyVect v) = do
putStr "> "
hFlush stdout
x <- readLn
return $ if x == 0 then AnyVect v else AnyVect $ vecAppend x v
main = do
AnyVect v <- appendElem $ AnyVect ZV
putStrLn $ show v
which works as expected. Then I decided to get rid of the unecessary AnyVect:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE Rank2Types #-}
import System.IO (hFlush, stdout)
data Z
data S n
data Vect n where
ZV :: Vect Z
CV :: Int -> Vect n -> Vect (S n)
instance Show (Vect n) where
show ZV = "Nil"
show (CV x v) = show x ++ " : " ++ show v
vecAppend :: Int -> Vect n -> Vect (S n)
vecAppend x ZV = CV x ZV
vecAppend x v = CV x v
appendElem :: Vect n -> (forall n. Vect n -> a) -> IO a
appendElem v f = do
putStr "> "
hFlush stdout
x <- readLn
return $ if x == 0 then f v else f $ vecAppend x v
main = do
appendElem ZV show >>= putStrLn
which works as well even I don't really like the way main is written.
Is there any other simpler/cleaner way to write it?
vecAppend = CV
is more direct. I would also rename it asvecCons
, since "append" sounds as if it worked on two vectors. – chi