2016-11-27 235 views
3

我想如下定義一個特定函子:Haskell的類型類與參數類型

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances #-} 

data ValOrError a b = Val a | Error b 

class MF c a b where 
    mcons :: a -> c -> c 
    merr :: b -> c 
    mhead :: c -> ValOrError a b 
    mtail :: c -> ValOrError c b 

32,我想在c類型的類型類MF具有類型參數ab。我想在這樣的數據結構定義過濾器的功能如下:

mfilter f e = 
    let h = mhead e in 
    let t = mtail e in 
    case h of 
    Error b -> e 
    Val a -> case (f a) of 
     True -> case t of 
     Error d -> mcons a (merr d) 
     Val b -> mcons a (mfilter f b) 
     False -> case t of 
     Error d -> merr d 
     Val b -> mfilter f b 

但我得到了以下錯誤:

haskell.hs:24:1:

Could not deduce (MF c a2 b3) 
    arising from the ambiguity check for ‘mfilter’ 
from the context (MF c a5 b6, 
        MF c a4 b5, 
        MF c a4 b4, 
        MF c a4 b, 
        MF c a3 b6, 
        MF c a b6) 
    bound by the inferred type for ‘mfilter’: 
      (MF c a5 b6, MF c a4 b5, MF c a4 b4, 
       MF c a4 b, MF c a3 b6, MF c a b6) => 
      (a4 -> Bool) -> c -> c 
    at haskell.hs:(24,1)-(35,28) 
The type variables ‘a2’, ‘b3’ are ambiguous 
When checking that ‘mfilter’ 
    has the inferred type ‘forall b c a b1 a1 a2 b2 a3 b3. 
         (MF c a3 b3, MF c a2 b2, MF c a2 b1, 
          MF c a2 b, MF c a1 b3, MF c a b3) => 
         (a2 -> Bool) -> c -> c’ 
Probable cause: the inferred type is ambiguous 

我不知道是否有在Haskell一個更好的方式來聲明類型c始終具有ab作爲類型參數。使用Java的語法:

public interface MF<A,B> { 
    MF<A,B> mcons(A head, MF<A,B> tail); 
    MF<A,B> merr(B error); 
    ValOrError<A,B> head(MF<A,B> e); 
    ValOrError<MF<A,B>,B> tail(MF<A,B> e); 
} 

此外,在另一方面,過濾功能應該有一個類型:

mfilter :: (a -> Bool) -> MF c a b -> MF c a b 

回答

5

最直接的方式做到這一點,從您的代碼開始,將是函數依賴添加到您的類型類:

{-# LANGUAGE FunctionalDependencies #-} 
class MF c a b | c -> a, c -> b where 
    ... 

這本質上只是告訴編譯器的類型的信息和b已經包含在c(因此可以在呼叫站點提取,因此a2,b3等將不會有歧義)。當您定義instance MF時,如何提取此信息可以確定爲GHC。雖然通常這種方式很好,但是我覺得爲什麼要這樣做有點令人懷疑:如果c總是有X a bX是一個合適的data-型函數,可以部分應用),那麼爲什麼甚至在課堂上提及ab?他們基本上是多餘的。爲什麼不給班級一個參數(類別Type -> Type -> Type),然後可以將應用於ab

class MF x where 
    mcons :: a -> x a b -> x a b 
    merr :: b -> x a b 
    mhead :: x a b -> ValOrError a b 
    mtail :: x a b -> ValOrError (x a b) b 

或者,如果你真的想c有一種Type(!這的確是有意義的),我建議在類定義類型家庭充填的ab類型:

{-# LANGUAGE TypeFamilies #-} 
class MF c where 
    type ValType c :: * 
    type ErrType c :: * 
    mcons :: ValType c -> c -> c 
    merr :: ErrType c -> c 
    mhead :: c -> ValOrError (ValType c) (ErrType c) 
    mtail :: c -> ValOrError c (ErrType c) 

這基本上等同於TypeFamilies解決方案,但給出了一個更加明確,不那麼神祕(儘管也更詳細)的界面。

+0

我有一個問題,關於最後的解決方案,我最喜歡的。如果我有以下數據 'data MemoryMonad a b = Lift b |守衛b | Elem a(MemoryMonad a b)' 我該如何讓它成爲這個類型類的一個實例? – jackb

+2

@jackb'instance MF(MemoryMonad a b)where type ValType(MemoryMonad a b)= a;鍵入ErrType(MemoryMonad a b)= b' – user2407038