系统地将函数应用于haskell记录的所有字段 [英] Systematically applying a function to all fields of a haskell record
问题描述
我有一条记录,其中包含不同类型的字段,并且有一个适用于所有这些类型的函数。作为一个小(愚蠢的)示例:
data Rec = Rec {flnum :: Float,intnum :: Int}派生(显示)
我想定义一个在每个字段中添加两个记录的函数:
addR :: Rec-> Rec-> Rec
addR ab = Rec {flnum =(flnum a)+(flnum b),intnum =(intnum a)+(intnum b)}
有没有一种方法可以在不重复每个字段的操作的情况下表达这一点(记录中可能有很多字段)?
实际上,我有一个仅由也许
字段组成的记录,并且我想将实际数据与包含某些字段默认值的记录结合起来,以当实际数据为 Nothing
时使用。
(我想模板haskell应该可以,但是我对便携式实现更感兴趣。)
您可以使用 gzipWithT 。
我不是专家,所以我的版本有点傻。应该只能调用 gzipWithT
一次,例如使用 extQ
和 extT
,但是我没有找到做到这一点的方法。无论如何,这是我的版本:
{-#语言DeriveDataTypeable#-}
导入数据。泛型
数据Test = Test {
test1 :: Int,
test2 :: Float,
test3 :: Int,
test4 :: String,
test5 ::字符串
}
派生(可键入,数据,等式,显示)
t1 ::测试
t1 =测试1 1.1 2 t1 t11
t2 ::测试
t2 =测试3 2.2 4 t2 t22
merge ::测试->测试->测试
merge ab =让b'= gzipWithT mergeFloat ab
b''= gzipWithT mergeInt a b'
在gzipWithT mergeString a b''
mergeInt :: (数据a,数据b)=> -> b-> b
mergeInt = mkQ(mkT(id :: Int-> Int))(\a-> mkT(\b-> a + b :: Int))
mergeFloat ::(数据a,数据b)=> -> b-> b
mergeFloat = mkQ(mkT(id :: Float-> Float))(\a-> mkT(\b-> a + b :: Float))
mergeString ::(数据a,数据b)=> -> b-> b
mergeString = mkQ(mkT(id ::字符串->字符串))(\a-> mkT(\b-> a ++ b ::字符串))
main :: IO()
main = print $ merge t1 t2
输出:
Test {test1 = 4,test2 = 3.3000002,test3 = 6,test4 = t1t2,test5 = t11t22 }
代码晦涩难懂,但思路很简单, gzipWithT
将指定的通用函数( mergeInt
, mergeString
等)应用于一对对应的字段。 / p>
I have a record with fields of different types, and a function that is applicable to all of those types. As a small (silly) example:
data Rec = Rec { flnum :: Float, intnum :: Int } deriving (Show)
Say, I want to define a function that adds two records per-field:
addR :: Rec -> Rec -> Rec
addR a b = Rec { flnum = (flnum a) + (flnum b), intnum = (intnum a) + (intnum b) }
Is there a way to express this without repeating the operation for every field (there may be many fields in the record)?
In reality, I have a record comprised exclusively of Maybe
fields, and I want to combine the actual data with a record containing default values for some of the fields, to be used when the actual data was Nothing
.
(I guess it should be possible with template haskell, but I am more interested in a "portable" implementation.)
You can use gzipWithT for that.
I'm not an expert, so my version it a bit silly. It should be possible to call gzipWithT
only once, e.g. using extQ
and extT
, but I failed to find the way to do that. Anyway, here is my version:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Generics
data Test = Test {
test1 :: Int,
test2 :: Float,
test3 :: Int,
test4 :: String,
test5 :: String
}
deriving (Typeable, Data, Eq, Show)
t1 :: Test
t1 = Test 1 1.1 2 "t1" "t11"
t2 :: Test
t2 = Test 3 2.2 4 "t2" "t22"
merge :: Test -> Test -> Test
merge a b = let b' = gzipWithT mergeFloat a b
b'' = gzipWithT mergeInt a b'
in gzipWithT mergeString a b''
mergeInt :: (Data a, Data b) => a -> b -> b
mergeInt = mkQ (mkT (id :: Int -> Int)) (\a -> mkT (\b -> a + b :: Int))
mergeFloat :: (Data a, Data b) => a -> b -> b
mergeFloat = mkQ (mkT (id :: Float -> Float)) (\a -> mkT (\b -> a + b :: Float))
mergeString :: (Data a, Data b) => a -> b -> b
mergeString = mkQ (mkT (id :: String -> String)) (\a -> mkT (\b -> a ++ b :: String))
main :: IO ()
main = print $ merge t1 t2
Output:
Test {test1 = 4, test2 = 3.3000002, test3 = 6, test4 = "t1t2", test5 = "t11t22"}
The code is obscure, but the idea is simple, gzipWithT
applies the specified generic function (mergeInt
, mergeString
, etc) to pair of corresponding fields.
这篇关于系统地将函数应用于haskell记录的所有字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!