有没有办法将Maybe构造函数应用于具有泛型的每个记录字段?

时间:2016-08-18 14:26:37

标签: haskell generics types

我有两种数据类型,第二种是第一种副本,但每个字段都使用了Maybe。

data A = {a :: Int, b :: String}
data B = {c :: Maybe Int, d :: Maybe String}

有没有办法制作函数

f :: A -> B
g :: B -> A -> A

对字段本身没有任何了解? (如果第一个参数的值为空,g将采用第二个参数的默认值)

2 个答案:

答案 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中的样板代码量。

一个缺点是您使用IunI来构建和 访问字段值。