【问题标题】:Working with data types parameterized by a functor处理由仿函数参数化的数据类型
【发布时间】:2021-12-07 10:21:44
【问题描述】:

我最近定义了一个类型,我可能无法计算其字段:

data Foo = Foo {x, y :: Int, others :: NonEmpty Int}

data Input

computeX, computeY :: Input -> Maybe Int
computeOthers :: Input -> Maybe (NonEmpty Int)

现在,我可能会做的一件显而易见的事情就是使用liftA3

foo :: Input -> Maybe Foo
foo i = liftA3 Foo (computeX i) (computeY i) (computeOthers i)

这很好用,但我认为将Foo 概括为也持有Maybes,然后将一种类型的Foo 转换为另一种类型可能会很有趣。在一些类似的情况下,我可以给 Foo 类型一个类型参数并派生 Traversable。然后在创建Foo (Maybe Int) 之后,我可以使用sequenceA :: Foo (Maybe Int) -> Maybe (Foo Int) 一次反转整个事情。但这在这里不起作用,因为我的函数没有给我NonEmpty (Maybe Int),它给了我Maybe (NonEmpty Int)

所以我想我会尝试通过函子进行参数化:

data Foo f = Foo {x, y :: f Int, others :: f (NonEmpty Int)}

但问题是,我如何将Foo Maybe 变成Maybe (Foo Identity)?显然,我可以手动编写该函数:它与上面的 liftA3 内容同构。但是这种高阶类型是否有一些 Traversable 的相似之处,以便我可以对这个问题应用更通用的函数,而不是使用定制函数重新执行它?

【问题讨论】:

    标签: haskell functor higher-kinded-types


    【解决方案1】:

    此类数据类型称为“高级数据”(HKD)。操作它们通常使用泛型或模板 Haskell。

    有像higgledy 这样的库,它们为港元提供内置功能。我相信construct 是您正在寻找的功能:

    {-# LANGUAGE DeriveGeneric #-}
    
    import Data.Generic.HKD
    import GHC.Generics
    import Data.Monoid
    
    data Foo = Foo { x, y :: Int, z :: [Int] }
      deriving (Generic, Show)
    
    emptyFoo :: HKD Foo Last
    emptyFoo = mempty
    
    sampleFoo :: HKD Foo Last
    sampleFoo = deconstruct (Foo 1 2 [3])
    
    emptyFoo' :: Last Foo
    emptyFoo' = construct emptyFoo
    
    sampleFoo' :: Last Foo
    sampleFoo' = construct sampleFoo
    
    main = do
      print emptyFoo'
      print sampleFoo'
    

    这将打印:

    Last {getLast = Nothing}
    Last {getLast = Just (Foo {x = 1, y = 2, z = [3])}
    

    编辑:我刚刚发现一个更受欢迎的库是barbies(higgledy 也依赖于芭比娃娃)。您正在寻找的函数也作为btraverse 的应用程序存在于该库中:

    {-# LANGUAGE DeriveGeneric #-}
    {-# LANGUAGE DeriveAnyClass #-}
    {-# LANGUAGE StandaloneDeriving #-}
    {-# LANGUAGE UndecidableInstances #-}
    
    import Data.List.NonEmpty
    import Barbies
    import GHC.Generics
    import Data.Functor.Identity
    
    data Foo f = Foo {x, y :: f Int, others :: f (NonEmpty Int)}
      deriving (Generic, FunctorB, TraversableB, ConstraintsB)
    
    deriving instance AllBF Show f Foo => Show (Foo f)
    
    f :: Applicative f => Foo f -> f (Foo Identity)
    f = btraverse (fmap Identity)
    
    main :: IO ()
    main = do
      print (f (Foo (Just 1) (Just 2) (Just (3 :| []))))
    

    打印出来:

    Just (Foo {x = Identity 1, y = Identity 2, others = Identity (3 :| [])})
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2013-01-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多