我有多个数据定义,作为一个简单的例子:
data Fruit = Apple String Bool
| Cherry String String
| Grape String
data Vegetable = Carrot String
| Onion Bool String
| Tomato String String
现在我想要一个应该继续这两种类型的函数,我试过这样的事情:
f :: a -> String
f (Carrot s) = s
f (Apple s b) = s
f (Onion b s) = s
...
但这不起作用,因为预期的类型a
不能与例如类型Carrot
匹配。我想知道如何定义一个函数,它可以借助模式匹配或其他技术处理多个现有的独立数据定义。
一种做你想要做的事情的方法是使用一种涉及两种食物的新数据类型,所以,我们称之为食物,它将是:
data Food = Veg Vegetable | Fr Fruit deriving Show
data Fruit = Apple String Bool
| Cherry String String
| Grape String deriving Show
data Vegetable = Carrot String
| Onion Bool String
| Tomato String String deriving Show
f :: Food -> String
f (Veg v) = fVeg v
f (Fr f) = fFruit f
fVeg (Carrot s) = s
fVeg (Onion b s) = s
fVeg (Tomato s1 s2) = s1 ++ s2
fFruit (Apple s b) = s
...
...
f $ Veg $ Onion True "friend"
=> "friend"
有两种选择。一个是Damian Lattenero所说的另一种选择,另一方面是使用类型类。
class Food a where
f :: a -> String
instance Food Fruit where
f (Apple ...) = ...
f (Cherry ...) = ...
f ...
instance Food Vegetable where
f (Carrot ...) = ...
f (Onion ...) = ...
f (Tomato ...) = ...
问题在于你不能拥有食物清单,因为水果和蔬菜是不同类型的。但是你可以毫无问题地使用f作为两种类型。
编辑:
另一种选择,存在量化,将两种类型都放在一个列表中,但只对数据应用f(也使用上面的代码):
{-# LANGUAGE ExistentialQuantification #-}
data F = forall a. (Food a) => F a
instance Food F where
f (F x) = f x
xs :: [F]
xs = [F (Apple ...), F (Carrot ..), F (Tomato ...)]
以及使用f的函数示例:
mapF :: [F] -> [String]
mapF xs = map f xs