【问题标题】:Is there a way to apply Maybe constructor to each field of record with generics?有没有办法使用泛型将 Maybe 构造函数应用于每个记录字段?
【发布时间】:2016-12-25 12:53:42
【问题描述】:

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

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

有没有办法制作函数

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

对字段本身一无所知? (如果第一个参数的值不是 g 将采用第二个参数的默认值)

【问题讨论】:

  • 我认为这是为了让您可以为函数提供可选参数。我宁愿让它接受一个 A 并提供一个默认 A,调用者可以在使用记录更新语法选择性地修改它之后将其传递给您的函数。
  • B 数据类型用作 yaml 配置文件的同构,其中一些选项可能会丢失。但是程序应该使用简单安全的 A 类型。除了将其转换为 A 之外,B 不应用于任何用途。
  • 首先是编译时,所以我不能使用不同的默认配置。第二件事在每次使用配置时都会产生大量代码。镜头很复杂,需要很长时间才能理解它们,许多 haskell 开发人员建议不要使用它们。
  • Yaml.Config 也是只读的。

标签: haskell generics types


【解决方案1】:

怎么样:

{-# 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 来构造和 访问字段值。

【讨论】:

    【解决方案2】:

    这可以通过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 中。

    有关泛型sop 的另一个示例,请参阅here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-01
      • 1970-01-01
      • 2015-02-10
      • 2021-11-11
      相关资源
      最近更新 更多