使用vinyl(“可扩展记录”包):
import Data.Vinyl
-- `vinyl` exports `Rec`
type Nums = Rec Identity [Float, Int]
相当于
data Nums' = Nums' (Identity Float) (Identity Int)
它本身等价于
data Nums'' = Nums'' Float Int
那么addR就是简单
-- vinyl defines `recAdd`
addR :: Nums -> Nums -> Nums
addR = recAdd
如果你添加一个新字段
type Nums = Rec Identity [Float, Int, Word]
您无需触摸addR。
顺便说一句,recAdd 很容易定义你自己,如果你想“提升”你自己的自定义数字操作,它只是
-- the `RecAll f rs Num` constraint means "each field satisfies `Num`"
recAdd :: RecAll f rs Num => Rec f rs -> Rec f rs -> Rec f rs
recAdd RNil RNil = RNil
recAdd (a :& as) (b :& bs) = (a + b) :& recAdd as bs
为方便起见,您可以定义自己的构造函数:
nums :: Float -> Int -> Num
nums a b = Identity a :& Identity b :& RNil
甚至是用于构造和匹配值的模式:
-- with `-XPatternSynonyms`
pattern Nums :: Float -> Int -> Num
pattern Nums a b = Identity a :& Identity b :& RNil
用法:
main = do
let r1 = nums 1 2
let r2 = nums 3 4
print $ r1 `addR` r2
let (Nums a1 _) = r1
print $ a1
let r3 = i 5 :& i 6 :& i 7 :& z -- inferred
print $ r1 `addR` (rcast r3) -- drop the last field
由于r3 被推断为
(Num a, Num b, Num c) => Rec Identity [a, b, c]
您可以(安全地)将其上载到
rcast r3 :: (Num a, Num b) => Rec Identity [a, b]
然后你专攻它
rcast r3 :: Nums
https://hackage.haskell.org/package/vinyl-0.5.2/docs/Data-Vinyl-Class-Method.html#v:recAdd
https://hackage.haskell.org/package/vinyl-0.5.2/docs/Data-Vinyl-Tutorial-Overview.html