2012-03-03 63 views
17

我的意思是定義一個適用於函數中的本地(letwhere)作用域的類型實例。更重要的是,我希望在這個實例中的函數是閉包,即能夠關閉定義實例的詞法範圍中的變量(這意味着實例可能在下次調用它的函數時以不同方式工作) )。是否有可能有一個「本地」類型實例?

我可以給你一個簡單的用例。假設我有一個基於類型類的類型的函數。在這個例子中,我使用了平方,它可以對任何類型的實例進行操作,例如Num(是的,平方很簡單,可以很容易地重新實現,但它可能會更復雜)。我需要能夠按原樣使用現有功能(無需更改或重新實現)。

square :: Num a => a -> a 
square x = x * x 

現在,假設我希望在模運算中使用這個操作,即加法,乘法等mod某些數字。這對於任何固定的模基很容易實現,但我想要有一些通用的東西,我可以重新使用不同的基模。我希望能夠來定義是這樣的:

newtype ModN = ModN Integer deriving (Eq, Show) 

-- computes (x * x) mod n 
squareModN :: 
squareModN x n = 
    let instance Num ModN where 
    ModN x * ModN y = ModN ((x * y) `mod` n) -- modular multiplication 
    _ + _ = undefined   -- the rest are unimplemented for simplicity 
    negate _ = undefined 
    abs _ = undefined 
    signum _ = undefined 
    fromInteger _ = undefined 
    in let ModN y = square (ModN x) 
    in y 

這樣做的一點是,我需要使用的功能,從上面(square),要求它的參數是一個類型,它是有一定的實例類型的類。我定義了一個新類型並將其作爲Num的一個實例;然而,爲了正確地執行模運算,它取決於基模n,由於該函數的通用設計,其可能因呼叫而改變。我希望將實例函數定義爲square函數的一次性「回調」(如果您願意的話),以定製它此次(以及僅此一次)執行操作的方式。

一種解決方案可能是將「閉包變量」直接集成到數據類型本身中(即ModN (x, n)來表示它所屬的數量和基數),操作可以從參數中提取這些信息。然而,這有幾個問題:1)對於多參數函數(例如(*)),它需要在運行時檢查這些信息是否匹配,這很醜陋; 2)實例可能包含0參數的「值」,我可能想依賴於閉包變量,但是,由於它們不包含參數,因此無法從參數中提取它們。

+2

不,你不能有本地實例。對於模塊化算法,Oleg Kiselyov和CC Shan已經在他們的論文「隱式配置」中解決了這個問題 - http://www.cs.rutgers.edu/~ccshan/prepose/prepose.pdf。我個人傾向於避免這種情況,並簡單地爲模塊化的基礎 - 即Z7,Z12做一個新的類型。 Conal Elliott提供了另一種選擇替代類型的模式,請參閱CxMonoid的文章「適用性數據驅動計算」 - http://conal.net/papers/data-driven/paper.pdf – 2012-03-03 12:42:52

+1

事實上,反射包本質上是Oleg紙的包裝版本。 – ehird 2012-03-03 12:56:49

+0

關於Haskell的現狀:沒有。實例將隨處隨地都可以到達。關於未來對Haskell的可能調整:也許。關於使用你自己的機制,而不是typeclasses:是的。 – 2012-03-03 22:01:02

回答

11

建議的擴展有我的this previous answer中顯示的相同問題;您可以使用本地實例創建兩個具有相同密鑰類型但不同Ord實例的Map,從而導致所有不變量崩潰。

然而,reflection包允許定義這樣的ModN類型:你定義一個實例與Reifies約束,並激活該實例特定Ñreify。 (我相信implicit parameters也會使這成爲可能,但這種擴展很少使用。)

相關問題