2012-09-18 67 views
6

我正在爲我的pipes庫編寫一個類型類來定義類似於Proxy類型的抽象接口。 Type類看起來類似:約束類型類或實例的派生變量

class ProxyC p where 
    idT :: (Monad m) => b' -> p a' a b' b m r 
    (<-<) :: (Monad m) 
      => (c' -> p b' b c' c m r) 
      -> (b' -> p a' a b' b m r) 
      -> (c' -> p a' a c' c m r) 
    ... -- other methods 

我還編寫擴展爲Proxy類型的形式是:

instance (ProxyC p) => ProxyC (SomeExtension p) where .... 

...我想這些情況下,能夠如果mMonad那麼p a' a b' b m對於所有a',a,b'bMonad

但是,我不知道如何幹淨地編碼,作爲ProxyC類或實例的約束。我目前所知道的唯一的解決辦法是做這樣的事情在類的方法簽名對其進行編碼:

(<-<) :: (Monad m, Monad (p b' b c' c m), Monad (p a' a b' b m)) 
      => (c' -> p b' b c' c m r) 
      -> (b' -> p a' a b' b m r) 
      -> (c' -> p a' a c' c m r) 

...但我希望會有一個更簡單,更優雅的解決方案。

編輯:即使不是最後的解決方案有效,因爲編譯器不推導出(Monad (SomeExtension p a' a b' b m))意味着(Monad (p a' a b' b m))爲變量的具體選擇,給出下面的實例,即使:

instance (Monad (p a b m)) => Monad (SomeExtension p a b m) where ... 

編輯# 2

class ProxyC p where 
    return' :: (Monad m) => r -> p a' a b' b m r 
    (!>=) :: (Monad m) => ... 
:我考慮的只是複製 ProxyC類中的 Monad類的方法下一個解決方案

...然後用每個ProxyC實例對它們進行實例化。由於Monad方法僅需要在內部用於擴展寫入,並且原始類型仍然具有適合下游用戶的適當的Monad實例,所以這對我的目的來說似乎沒問題。所有這些只是將Monad方法暴露給實例編寫器。

+0

AFAIK你只能用醜陋的黑客來做它,比如f.e.愛德華Kmett做在http://hackage.haskell.org/packages/archive/constraints/0.3.2/doc/html/Data-Constraint-Forall.html –

回答

1

一個相當瑣碎的方法來做到這一點是使用GADT來證明移動到價值層面

data IsMonad m where 
    IsMonad :: Monad m => IsMonad m 

class ProxyC p where 
    getProxyMonad :: Monad m => IsMonad (p a' a b' b m) 

您將需要顯式打開字典,無論你需要它

--help avoid type signatures 
monadOf :: IsMonad m -> m a -> IsMonad m 
monadOf = const 

--later on 
case getProxyMonad `monadOf` ... of 
    IsMonad -> ... 

的使用GADT來傳遞命題證明的策略是非常普遍的。如果您在使用約束種類,而不是僅僅GADTs都還好,你可以改用愛德華Kmett的Data.Constraint

class ProxyC p where 
    getProxyMonad :: Monad m => Dict (Monad (p a' a b' b m)) 

它可以讓你定義

getProxyMonad' :: ProxyC p => (Monad m) :- (Monad (p a' a b' b m)) 
getProxyMonad' = Sub getProxyMonad 

,然後用一個花哨的管道符告訴編譯器在哪裏尋找單子例如

... \\ getProxyMonad' 
其實

,該:-蘊涵類型構成類別(如果對象是騙子這個類別有很多有很好的結構,也就是說用它做證明是相當不錯的。

p.s.這些片段都沒有經過測試。

編輯: 你也可以用NEWTYPE包裝組合的價值水平的證明,而不是需要打開GADTs所有的地方

newtype WrapP p a' a b' b m r = WrapP {unWrapP :: p a' a b' b m r} 

instance ProxyC p => Monad (WrapP p) where 
    return = case getProxyMonad of 
       Dict -> WrapP . return 
    (>>=) = case getProxyMonad of 
       Dict -> \m f -> WrapP $ (unWrapP m) >>= (unWrapP . f) 

instance ProxyC p => ProxyC (WrapP p) where 
    ... 

我懷疑,但顯然還沒有進行測試,這實施也將相對有效。

+0

我想我唯一擔心的是這是多少開銷包裝和展開'data'類型。請記住,我會在每一次綁定中做這樣一次往返。但是,我沒有對它進行基準測試,所以這是我的純粹推測。現在我有另一種解決方案,看起來像這樣:'newtype P pa'ab'bmr = P(pa'ab'bmr)'然後我寫'instance(ProxyC p,Monad m)=> Monad(P pa'ab 'bm)其中...',並且我改變了所有'ProxyC'方法在其簽名中具有'P'。 –

+0

儘管我最終使用了Edward的''constraint'包來完成這個工作,但字典實現技巧確實奏效。 –