Haskell 有没有办法用泛型将Maybe构造函数应用于记录的每个字段?
我有两种数据类型,第二种是第一种的副本,但每个字段上可能都有Haskell 有没有办法用泛型将Maybe构造函数应用于记录的每个字段?,haskell,generics,types,Haskell,Generics,Types,我有两种数据类型,第二种是第一种的副本,但每个字段上可能都有 data A = {a :: Int, b :: String} data B = {c :: Maybe Int, d :: Maybe String} 有没有办法制作一个函数 f :: A -> B g :: B -> A -> A 对字段本身一无所知?(如果第一个参数的值为nothing,g将从第二个参数中获取默认值)如何: {-# LANGUAGE RankNTypes #-} data R f = R
data A = {a :: Int, b :: String}
data B = {c :: Maybe Int, d :: Maybe String}
有没有办法制作一个函数
f :: A -> B
g :: B -> A -> A
对字段本身一无所知?(如果第一个参数的值为nothing,g
将从第二个参数中获取默认值)如何:
{-# 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可以减少mayber中的样板代码量
一个缺点是您必须使用I
和unI
来构造
访问字段值。这可以通过扩展GHC默认泛型机制的库来完成
“泛型sop”可以获取一个常规记录并推断出它的泛型表示。此表示有一个包装每个字段的类型参数,库允许跨记录字段进行应用程序操作
{-# 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
中
请参阅泛型sop的另一个示例。我假定这是为了让您可以对函数使用可选参数。相反,我希望它采用A,并提供一个默认A,调用方可以在使用记录更新语法选择性地修改它后将其传递给您的函数。B数据类型用作yaml配置文件的同构,其中可能缺少一些选项。但程序应该使用简单安全的A类型。除了将B转换为A之外,B不应用于其他任何用途。您可以使用或来代替?\。第一件事是编译时,所以我不能使用不同的默认配置。第二件事是每次使用config都会产生大量代码。镜头非常复杂,理解它们需要花费很多时间,很多haskell开发人员都建议不要使用它们。而且Yaml.Config是只读的。