我有两种数据类型,第二种是第一种副本,但每个字段都使用了Maybe。
data A = {a :: Int, b :: String}
data B = {c :: Maybe Int, d :: Maybe String}
有没有办法制作函数
f :: A -> B
g :: B -> A -> A
对字段本身没有任何了解? (如果第一个参数的值为空,g
将采用第二个参数的默认值)
答案 0 :(得分:5)
这可以通过generics-sop完成,这是一个扩展GHC默认泛型机制的库。
“generics-sop”可以定期记录并推断出它的通用表示。此表示形式具有包装每个字段的类型参数,并且库允许跨记录字段进行Applicative sequence-like操作。
{-# language TypeOperators #-}
{-# language DeriveGeneric #-}
{-# language TypeFamilies #-}
{-# language DataKinds #-}
import qualified GHC.Generics as GHC
import Generics.SOP
data A = A {a :: Int, b :: String} deriving (Show,GHC.Generic)
instance Generic A -- this Generic is from generics-sop
defaulty :: (Generic a, Code a ~ '[ xs ]) => NP Maybe xs -> a -> a
defaulty maybes r = case (from r) of
SOP (Z np) -> let result = hliftA2 (\m i -> maybe i I m) maybes np
in to (SOP (Z result))
main :: IO ()
main = do
print $ defaulty (Nothing :* Just "bar" :* Nil) (A 99 "foo")
Nothing :* Just "bar" :* Nil
是与原始记录定义中的字段列表匹配的通用表示形式。请注意,表示中的每个字段都包含在Maybe
。
有关generics-sop的另一个例子,请参阅here。
答案 1 :(得分:2)
怎么样:
{-# LANGUAGE RankNTypes #-}
data R f = R { a :: f Int, b :: f String, c :: f Char }
newtype I a = I { unI :: a }
fromMaybeI :: I a -> Maybe a -> I a
fromMaybeI a Nothing = a
fromMaybeI _ (Just a) = I a
fromMaybeR :: R I -> R Maybe -> R I
fromMaybeR ri rm =
R (go a) (go b) (go c)
where
go :: (forall f. R f -> f a) -> I a
go x = fromMaybeI (x ri) (x rm)
R Maybe
是带有Maybe值的记录,R I
是带有具体值的记录。
使用RankNTypes可减少fromMaybeR
中的样板代码量。
一个缺点是您使用I
和unI
来构建和
访问字段值。