繁体   English   中英

Haskell - 类型包装器统一

[英]Haskell - type wrappers unification

我有一堆功能,如:

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")据我所知,有两种选择:

  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 ab ... z = g [("a", unpack a), ("b", unpack b), ... ("z", unpack z)] 

    大量的代码和重复。

我想要的是一些魔术可以让我:

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

我认为问题归结为:列表只能包含相同类型的元素; 这意味着您必须将其“合并”为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 a@(A {}) b@(B {}) c@(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)并没有多大区别。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM