tags:

views:

114

answers:

2

Hello everyone.

I'm a haskell newbie and I couldn't find an answer to this question.

Can we define types with conditions? For example, simple user-defined data type would be:

data MyList = MyList [a]

Can I somehow modify this code so MyList constructor can take only lists with even number of elements? Something like

data MyList = MyList [a] where (even (length a))

Thank you!

+7  A: 

No, you can't.

If it's really necessary, just write a constructor-like function by yourself.

toMyList :: [a] -> MyList
toMyList l | even (length l) = MyList l
           | otherwise       = error "Length of list has to be even"

or if error-checking is likely to occur:

toMyList :: [a] -> Maybe MyList

But depending on the use-case, maybe you can express yourself through types (e.g. tuples or two lists) than through runtime-checks.

Dario
Thank you very much!
Alex
+1  A: 

You could use something like

data MyList a = MyNil
              | MyCons a a (MyList a)

This makes sure that your list gets longer two elements at a time. This is equivalent to Alexandre's comment, but worth looking at in some detail.

Along with some from/to conversion functions like

fromMyList :: MyList a -> [a]
fromMyList MyNil = []
fromMyList (MyCons x y rest) = x : y : fromMyList rest

toMyList :: [a] -> Maybe (MyList a)
toMyList [] = Just MyNil
toMyList [_] = Nothing
toMyList (x:y:rest) = fmap (MyCons x y) (toMyList rest)

toMyListError :: [a] -> MyList a
toMyListError [] = MyNil
toMyListError [_] = error "Length of list has to be even"
toMyListError (x:y:rest) = MyCons x y (toMyListError rest)

-- these two may be a bit more difficult to use...
fromMyListTuple :: MyList a -> [(a,a)]
fromMyListTuple MyNil = []
fromMyListTuple (MyCons x y rest) = (x,y) : fromMyListTuple rest

toMyListTuple :: [(a,a)] -> MyList a
toMyListTuple = foldr (\(x,y) -> MyCons x y) MyNil    

it becomes possible to reuse existing list functions with a bit of thought:

myAppend :: MyList a -> MyList a -> MyList a
myAppend xs ys = toMyListError (fromMyList xs ++ fromMyList ys)

But that all depends on what you actually want to do with these lists!

You can state a lot of properties of the number of elements in the container this way, see Chris Okasaki's work for example. Other conditions might be possible as well, but in most cases you'll be better off with Dario's approach.

But, finally, note that if your type is to be fully polymorphic you can't really use much more information about the lists contained than the number of elements in it anyway!

yatima2975