2011-07-13 81 views
2

我想要實現與顯示了不同類型的函數參數不同的行爲的arr -member功能箭頭,例如arr (\x -> (x,x))應該從arr id表現不同......GHC爲相同的表達式選擇不同的實例?

下面的代碼:

{-# LANGUAGE Arrows, OverlappingInstances, IncoherentInstances, FlexibleInstances#-} 
import Control.Arrow 
import Control.Category 
import Prelude hiding (id, (.)) 

class ToPredefinedStr a where 
    toStr :: a -> String 


instance ToPredefinedStr ((->) b (b,b)) where 
    toStr _ = "b -> (b,b)" 

instance ToPredefinedStr (a -> (b,c)) where 
    toStr _ = "a -> (b,c)" 

instance ToPredefinedStr ((a,b) -> c) where 
    toStr _ = "(a,b) -> c" 

instance ToPredefinedStr (a -> b) where 
    toStr _ = "a -> b" 

newtype MyArrow a b c = MA (a b (c, String)) 

instance (Category a, Arrow a) => Category (MyArrow a) where 
    -- irrelevant for this example ... 

instance (Arrow a) => Arrow (MyArrow a) where 
    arr f  = MA (arr (\x -> (f x, toStr f))) 

appMyArr (MA a) = a 

但是:它顯示了以下非常奇怪behavor:

> toStr (\x -> (x,x)) -- that works as expected! 
"b -> (b,b)" 
> appMyArr (arr (\x -> (x,x)))() -- but this does'nt!! 
(((),()),"a -> b") 

任何人都可以解釋如何得到ghci中選擇b -> (b,b) - 第二個示例中的表達式\x -> (x,x)的示例?

回答

6

簡而言之,發生這種情況是因爲編譯器在第一種情況下可以訪問比第二種情況更具體的類型信息。

如果編譯你的arr定義,編譯器只能看到函數參數f類型爲b -> c,所以考慮到呼叫toStr f時,必須選擇僅基於此信息的一實例。畢竟,arr可能會被任何函數調用。很顯然,它只能選擇instance ToPredefinedStr (a -> b)

現在,當我們內嵌它像toStr (\b -> (b, b)),編譯器可在通話網站的更多信息,並可以選擇更具體的實例。

而且,如果您正在考慮使用INLINE編譯指示,則不會更改實例選擇。

對於你想達到什麼樣的,我能想到的最接近的是,限制類型,這樣的情況下選擇將發生arr

{-# LANGUAGE FlexibleContexts, ... #-} 

class FancyArrow a where 
    myArr :: (ToPredefinedStr (b -> c)) => (b -> c) -> a b c 
    ... 

instance (Arrow a) => FancyArrow (MyArrow a) where 
    myArr f  = MA (arr (\x -> (f x, toStr f))) 

這給你想要的結果。

*Main> appMyArr (myArr (\x -> (x,x)))() 
(((),()),"b -> (b,b)") 

注意,這是有點脆,因爲你必須控制在何處實例的選擇是由傳播ToPredefinedStr約束製成。例如,如果刪除類型簽名,此函數會自動更改行爲。

foo :: (Arrow a, ToPredefinedStr (b -> c)) => (b -> c) -> a b (c, String) 
foo f = appMyArr (myArr f) 
+0

一個有趣的建議;我明天一定會試試這個! ...你爲什麼認爲它不能工作? – phynfo

+0

主要是因爲它不會與標準'Arrow'類工作,但如果你的罰款與再肯定的是,它應該工作。但是,它感覺有些脆弱。 – hammar

10

如果您使用IncoherentInstances任何事情都可能發生。不再有任何承諾,這些實例是以一致的方式挑選出來的。

+0

我想,IncoherentInstances會從上到下,選擇適合的第一個實例...? – phynfo

+0

@phynfo:適合給出的推斷類型從中去尋找上下文中的第一個實例。我對細節並不清楚,GHC用戶指南在這個問題上有些沉默,但Hammar的回答聽起來似乎合理。但真的,它是*不連貫*。它說*它的名字*。你期望什麼? :] –

相關問題