Reputation: 1951
Lets assume I have a type class Stack
with one instance List
:
class Stack a where
push :: a -> Integer -> a
pop :: a -> a
last :: a -> Integer
data List = Empty | Element Integer List
instance Stack List where
push list value = Element value list
pop Empty = error "No elements"
pop (Element _ list) = list
last Empty = error "No elements"
last (Element value _) = value
How Stack
has to be defined in order for List
to not be limited to Integer
values?
-- class Stack (?) where ...
data List a = Empty | Element a (List a)
-- instance Show (List a) where ...
Upvotes: 11
Views: 1363
Reputation: 152682
Consider using a higher-kinded class variable. Thus:
class Stack s where
push :: s a -> a -> s a
pop :: s a -> s a
last :: s a -> a
data List a = Empty | Element a (List a)
The instance remains exactly as you wrote it (though List
now has kind * -> *
instead of *
):
instance Stack List where
push list value = Element value list
pop Empty = error "No elements"
pop (Element _ list) = list
last Empty = error "No elements"
last (Element value _) = value
This approach is pure Haskell 2010 -- it requires no extensions.
Also, consider making your failures observable; for instance by changing the type of pop
and last
to return Maybe (s a)
and Maybe a
, respectively.
Upvotes: 14
Reputation: 476503
In that case you can make a multiparameter class:
class Stack a b where
push :: a -> b -> a
pop :: a -> a
last :: a -> b
and define it with:
instance Stack (List b) b where --You don't need to use `b`, but this make it easier to understand
push list value = Element value list
pop Empty = error "No elements"
pop (Element _ list) = list
last Empty = error "No elements"
last (Element value _) = value
Note that this is not a default (standardized) Haskell feature, and that you will need to turn it on. Either by passing -XMultiParamTypeClasses
and -XFlexibleInstances
to the compiler.
Or you can write:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
in the header of your source file.
Note that there can be several b
's for one a
for which you define an instance (and vice versa). This can make it hard to work with such classes. Say for instance you write a Dummy
type:
data Dummy = Dummy
you can define:
instance Stack Dummy b where
push x = const x
pop = id
last = const $ error "Dummy object"
Now it means that you have Stack
instance for every possible b
, so that you can push
and pop
all kinds of stuff to Dummy
objects.
Upvotes: 7