2015-09-18 45 views
8

讓我們假設我有一個類型的類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 ... 

回答

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 

注意,這不是默認的(標準化)哈斯克爾功能,並你需要打開它。通過將-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" 

現在它意味着你必須爲每一個可能的bStack實例,這樣就可以pushpop各種東西到Dummy的對象。

+1

在此之前,我實際上嘗試過相同的語法,但只寫了'實例堆棧(List a)'。我忘了將'FlexibleInstances'附加到編譯指示中。現在它可以工作,謝謝:) – Cubinator73

+0

如果每個'a'最多隻允許一個'b',這樣'Stack a b'就有一個實例,函數依賴'a-> b'可以顯着幫助類型推理機器。否則,每次調用'pop :: a - > a'都是不明確的:'b'不能從'pop'的上下文中確定。 (當然也可以使用類型族。) – chi

+0

@chi:IIRC你可以添加限制,例如'a'只有一個這樣的'b'。稍後再看。 –

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 

這種做法純粹是2010哈斯克爾 - 它不需要擴展。

另外,考慮讓你的失敗可觀察;例如通過分別改變poplast的類型以返回Maybe (s a)Maybe a

+0

這看起來對我來說也是一個很好的方法。我絕對想''也許':) – Cubinator73

+0

@ Cubinator73這是*通常*的方式去。如果您正在考慮多個單形態堆棧類型,則具有函數依賴關係的多參數類型類方法(或具有關聯類型的單個參數類型類型)是有意義的。 – dfeuer