[英]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")
據我所知,有兩種選擇:
大規模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
很多代碼。
常見的類型類和新類型:
編輯:好的,我們可以在這么簡單的情況下使用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)]
大量的代碼和重復。
我想要的是一些魔術可以讓我:
fk ab ... z = g [("a", a), ("b", b), ... ("z", z)]
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.