假设我有一个类型类Stack
,其中包含一个实例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
为了使Stack
不限于List
值,必须如何定义Integer
?
-- class Stack (?) where ...
data List a = Empty | Element a (List a)
-- instance Show (List a) where ...
答案 0 :(得分:8)
考虑使用更高级的类变量。因此:
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)
实例与您编写的内容完全一样(尽管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
这种方法纯粹是Haskell 2010 - 它不需要扩展。
另外,考虑让你的失败可以观察到;例如,将pop
和last
的类型分别更改为Maybe (s a)
和Maybe a
。
答案 1 :(得分:6)
在这种情况下,您可以创建一个多参数类:
class Stack a b where
push :: a -> b -> a
pop :: a -> a
last :: a -> b
并用以下内容定义:
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
请注意,这不是默认(标准化)Haskell功能,您需要将其打开。通过将-XMultiParamTypeClasses
和-XFlexibleInstances
传递给编译器。
或者你可以写:
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
在源文件的标题中。
请注意,您为其定义一个实例的b
可能有多个a
(反之亦然)。这可能使这些类很难使用。比如你写一个Dummy
类型:
data Dummy = Dummy
你可以定义:
instance Stack Dummy b where
push x = const x
pop = id
last = const $ error "Dummy object"
现在,这意味着每个可能的Stack
都有b
个实例,这样您就可以push
和pop
向Dummy
个对象发送各种内容