2012-01-31 96 views
3

我有這樣一組函數:哈斯克爾 - 型包裝統一

f1 :: String -> String -> ... -> String ->() 
f1 a b ... z = g [("a", a), ("b", b), ... ("z", z)] 
... 
fn :: String -> Int -> String -> ... -> String ->() 
fn a b ... z = g [("a", a), ("b", show b), ... ("z", z)] 

所以用戶可以叫他們像f1 "abc" "def"。我不希望他這樣做,因爲他可以輕鬆地交換「abc」和「def」(並且上帝知道調試時會浪費多少時間)。我希望他來傳遞參數一樣fk (A "abc") (B "def") 據我所看到的,有2個選項:

  1. 大規模data建設和大規模的解壓功能:

    data Value = A String 
          | B String 
          | C Int 
          | D String 
          ... 
    
    unpack :: Value -> String 
    unpack (A a) = a 
    unpack (B b) = b 
    unpack (C c) = show c 
    unpack (D c) = d 
    

    大量的代碼。

  2. 常見的類型和新類型:
    編輯:好的,那麼,我們可以在這種簡單的情況下使用GeneralizedNewtypeDeriving

    {-# LANGUAGE GeneralizedNewtypeDeriving #-} 
    
        class Value a where 
        unpack :: a -> String 
        instance Value String where 
        unpack = id 
        instance Value Int where 
        unpack = show 
    
        newtype A = A String deriving Value 
        newtype B = B String deriving Value 
        newtype C = C Int deriving Value 
        newtype D = D String deriving Value 
    
        ... 
    

    看起來好多了,但所有fk會是什麼樣子

    fk a b ... z = g [("a", unpack a), ("b", unpack b), ... ("z", unpack z)] 
    

    大量的代碼和重複。

我想要的是一些魔術這將讓我:

  1. fk a b ... z = g [("a", a), ("b", b), ... ("z", z)]
  2. g = h . map (second unpack)

回答

2

我想的問題歸結爲:該列表可以有元素只有同一種類型;這意味着要麼你必須將它合併成你的f中的一個類型,要麼你不能依賴haskells類型檢查。例如。下面的代碼會爲你工作,但類型檢查運行時:

{-# LANGUAGE GADTs #-} 

import Control.Arrow (second) 

data Item where 
    A :: String -> Item 
    B :: Int -> Item 

unpack (A s) = s 
unpack (B i) = show i 

myf [email protected](A {}) [email protected](B {}) [email protected](B {}) = 
    let g = [("a", a), ("b", b), ("c", c)] 
    in map (second unpack) g 
myf _ _ _ = error "Bad types" 

main = do 
    putStrLn $ show $ myf (A "test") (B 13) (B 14) 
    putStrLn $ show $ myf (A "test") (B 13) (A "xxx") 

當你想要編譯時類型檢查,你可以做這樣的事情;然而,你仍然需要重新輸入參數到相同的類型,所以從某種意義上說,拆包之間沒有太大的區別,只是它可能會稍微不太容易出錯。一個好的技巧來自JSON包 - 他們重新定義了一些操作(例如= :)創建的類型,所以你會:

{-# LANGUAGE ExistentialQuantification #-} 
import Control.Arrow (second) 

class Value a where 
    unpack :: a -> String 
newtype A = A String 
newtype B = B Int 

instance Value A where 
    unpack (A a) = a 

instance Value B where 
    unpack (B b) = show b 

data Item = forall b. Value b => Item b 
a =: b = (a, Item b) 

myf :: A -> B -> B -> [(String, String)] 
myf a b c = 
    let g = ["a" =: a, "b" =: b, "c" =: c] 
    in map (second (\(Item x) -> unpack x)) g 

main = do 
    putStrLn $ show $ myf (A "test") (B 13) (B 14) 

這不是太大的只是定義a =: b = (a, unpack b)雖然不同。