2017-12-27 210 views
2

我有多個數據定義,作爲一個簡單的例子:函數來處理多個現有獨立的數據定義

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匹配。我想知道如何在模式匹配或其他技術的幫助下定義一個可以處理多個現有獨立數據定義的函數。

+2

您可以定義一個'˚F::無論是果蔬 - > String',也可以定義一個類型類(和定義兩個'f's)。 –

+0

@WillemVanOnsem謝謝你的提示。我是對的,'只有'只有兩種類型? –

+2

沒有一種類型,即「任何水果蔬菜」類型。所以你寫'f(Left(Apple s b))= ...','f(Right(Carrot c))= ...'。 –

回答

6

做你正在嘗試做的方法是與同時涉及食品新的數據類型,所以,讓我們把它叫做食物,這將是:

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" 
+0

非常感謝。非常乾淨的解決方案,無需更改現有類 –

+0

@ClaudioP沒問題!我一直很喜歡幫助,我希望你接受兩個答案中的一個:) –

5

有兩個選項。一個是Damian Lattenero說的,另一個選擇是使用typeclasses。

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 
+2

請注意,typeclasses + existstentials通常會導致[已知的反模式](https://lukepalmer.wordpress.com/2010/01/24/haskell -antipattern-存在-類型類/) – chi