2013-02-09 95 views

回答

4

您不能將類型簽名應用於函數定義模式。這是編寫它的語法上正確的方法:

normalize :: (Modular s a, Integral a) => a -> M s a 
normalize a = M (mod a (modulus (__ :: s))) :: M s a 

但是,這不起作用。你真正想要的是在函數的類型簽名中引用類型變量s。這可以通過使用ScopedTypeVariables擴展,這需要明確的量化來實現:

normalize :: forall a s. (Modular s a, Integral a) => a -> M s a 
normalize x = M (Mod x (modulus (__ :: s))) 

作爲一個建議,以提高你的代碼,我建議使用標記庫:

import Data.Proxy 

modulus :: (Modular s a) => Proxy s -> a 

這可以讓你獲得沒有醜陋的佔位符底部。把它寫的另一種方法是:

modulus :: (Modular s a) => Tagged s a 

這也給你一個很好的概念的好處:你現在有兩種類型,Mod模塊化的價值觀和Tagged其模量。你可以自己定義的類型,也給它一個更好的名字:

newtype Mod  s a = Mod  { residue :: a } 
newtype Modulus s a = Modulus { modulus :: a } 

這一切不談,如果你想實際使用此,我建議什麼ocharles說:使用reflection庫。

2

這完成了使用reflection庫的論文中的示例。我將這個示例作爲庫源代碼中的Monoid示例。

data M a s = M a -- Note the phantom comes *after* the concrete 

-- In `normalize` we're tying the knot to get the phantom types to align 
-- note that reflect :: Reifies s a => forall proxy. proxy s -> a 

normalize :: (Reifies s a, Integral a) => a -> M a s 
normalize a = b where b = M (mod a (reflect b)) 

instance (Reifies s a, Integral a) => Num (M a s) where 
    M a + M b = normalize (a + b) 
    ... 

withModulus :: Integral a => a -> (forall s. Reifies s a => M a s) -> a 
withModulus m ma = reify m (runM . asProxyOf ma) 
    where asProxyOf :: f s -> Proxy s -> f s 
     asProxyOf a _ = a 
+0

聰明的方式來寫它,雖然爲了清晰起見,我更喜歡明確的類型簽名。我也建議翻轉'Mod'類型。 – ertes 2013-02-09 20:46:53

+1

這是edwardk的方法。我想要避免'ScopedTypeVariables'。 – 2013-02-09 20:48:12