2012-01-31 73 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)虽然不同。