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.)
() is very often used as the result of something that has no interesting result. For example, an IO action that is supposed to perform some I/O and terminate without producing a result will typically have type IO () .
Basic Syntax Records are an extension of sum algebraic data type that allow fields to be named: data StandardType = StandardType String Int Bool --standard way to create a product type data RecordType = RecordType -- the same product type with record syntax { aString :: String , aNumber :: Int , isTrue :: Bool }
The Either type is sometimes used to represent a value which is either correct or an error; by convention, the Left constructor is used to hold an error value and the Right constructor is used to hold a correct value (mnemonic: "right" also means "correct").
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With