2011-08-28 8 views
24

uncurry功能僅適用於函數帶有兩個參數:確實哈斯克爾具有可變參數的功能/元組?

uncurry :: (a -> b -> c) -> (a, b) -> c 

如果我想用的參數任意數量uncurry功能,我可以只寫獨立的功能:

uncurry2 f (a, b)   = f a b 
uncurry3 f (a, b, c)  = f a b c 
uncurry4 f (a, b, c, d) = f a b c d 
uncurry5 f (a, b, c, d, e) = f a b c d e 

不過這樣會很快乏味。有什麼辦法可以概括這個,所以我只需要編寫一個函數?

+0

不是這樣,但無論如何,你爲什麼要這樣做?根據我的經驗,在極少數情況下,你需要的不僅僅是uncurry –

+7

@Paul:沒有具體的原因,但只要我看到任何一種重複的模式,我都會問自己,我怎麼能夠從中進行概括和抽象。 – fredoverflow

回答

23

嘗試從tupleuncurryN。像所有形式的重載一樣,它使用類型類來實現。在這種情況下通過手動拼寫出實例高達15元組,這應該是綽綽有餘。

可變參數函數也是可能的使用類型的類。其中一個例子是Text.Printf。在這種情況下,它是通過對函數類型進行結構歸納來完成的。簡化的,它的工作原理是這樣的:

class Foo t 

instance Foo (IO a) 
instance Foo b => Foo (a -> b) 

foo :: Foo 

它不應該是很難看到foo可以實例化的類型IO aa -> IO ba -> b -> IO c等。 QuickCheck也使用這種技術。

結構歸納不會在元組工作,雖然,作爲一個ñ元組是完全無關的N + 1元組,所以這就是爲什麼實例必須手動拼寫出來。

+5

有趣的是,Data.Tuple.Curry的來源提到,不同的實例實際上是由一個單獨的程序生成的,然後可能被粘貼,就好像它們是用手工輸入的。 –

2

有沒有簡單的方法來編寫一個單一的定義uncurry將適用於不同數量的參數。

然而,可以使用Template Haskell生成,否則你將必須手工編寫的許多不同的變體。

+0

你可以用Daniel Fridlender和Mia Indrika的arity family功能模式寫一個n-ary'uncurry'。文章「我們需要依賴類型嗎?」給出了'zipWithN'的例子。 –

+0

「沒有辦法」可能有點大膽。我已將我的答案改爲「不直接的方式」。 –

+0

@stephentetley該實施是否已在任何地方實施? – CMCDragonkai

11

找到方法來使用過渡類型系統技巧來僞造這類東西是我的興趣之一,所以當我說結果非常難看時,請相信我。特別要注意的元組不是遞歸定義的,所以有直接抽象對他們沒有真正的方法;至於Haskell的類型系統而言,每個元組大小是完全不同。

任何直接處理元組的可行方法都需要代碼生成 - 無論是使用TH還是使用外部工具(如tuple包)。要想在不使用生成的代碼的情況下僞造它,您必須首先使用遞歸定義 - 通常爲具有「nil」值的右嵌套對來標記結束標記,即(,)()或與之等效的東西。您可能會注意到,這與(:)[]中的列表定義類似 - 實際上,這種遞歸定義的人造元組可以被視爲是類型級數據結構(類型列表),還是異構列表(例如,HList以這種方式工作)。

其缺點包括但不限於事實上,以這種方式構建的東西實際上可能比它的價值更尷尬,實現類型系統技巧的代碼通常是莫名其妙且完全不可移植的,並且最終結果不一定等同 - 例如,在(a, (b, (c,())))(a, b, c)之間存在多個不平凡的差異。

如果你想看看它有多可怕,你可以看看the stuff I have on GitHub,特別是位here