【问题标题】:Why there is no way to derive Applicative Functors in Haskell?为什么没有办法在 Haskell 中推导出 Applicative Functor?
【发布时间】:2015-04-22 03:35:45
【问题描述】:

在 Haskell 中,您可以使用 deriving 自动派生 FunctorFoldableTraversable。但是,无法派生Applicative。考虑到定义Applicative 实例(相当于压缩应用程序)的一种明显方法,是否有任何方法可以启用deriving Applicative

【问题讨论】:

    标签: haskell generics applicative deriving derivingvia


    【解决方案1】:

    不,这一点都不明显。比较以下Applicative 实例:

    1. []
    2. ZipList
    3. Data.Sequence.Seq,其Applicative instance declaration 运行到数百行。
    4. IO
    5. (->) r
    6. parsecattoparsecregex-applicative 中的解析器。
    7. Proxypipes 包中。

    这里几乎没有统一性,而且大多数情况并不明显。


    作为David Youngcomments[]ZipList 实例“最终都是两个不同的、同样有效的列表类型的Applicative 实例。”

    【讨论】:

    • 另外,[]ZipList 最终都是两个不同的、同样有效的列表类型的 Applicative 实例。
    • 所以也许我们需要一个 Zippable 类?也就是说,对于诸如派生Additive(反过来,它似乎有一个明显的定义)之类的事情,无需声明Applicative instance。 (也许我应该问另一个问题?)
    • 我的错。 Additive 是 Linear 包中的一个类,它实现了向量空间的加法组。例如,[1,2,3] ^+^ [1,1,1] == [2,3,4]。它有一个通用的实现,但它依赖于不可派生的 Applicative,因此您不能为 data Triple a = Triple a a a 派生Additive,无论是否有明显的派生。编辑:嗯,谁问什么添加剂被删除了。我不是一个人说话哈哈。
    • @Viclib Ha,我很抱歉。我只记得它在linear,我想还没有人看到它。无论如何,我认为主要问题是某些类型可以有多个同样有效的Applicative 实例,类似于Monoid
    • 是的,但不是多个同样有效的Additive 实例,对吧?所以这里似乎有什么问题......
    【解决方案2】:

    现在DerivingVia 已经发布(GHC-8.6 或更高版本),实际上可以在DeriveGeneric 的帮助下为任何确定性 数据类型派生Applicative!也就是说,任何数据类型都只有一个变体:

    data Foo x = Foo x | Fe  -- This is non-deterministic and can't derive Applicative
    data Bar x = Bar x x (Bar x) -- This is deterministic and can derive Applicative
    data Baz x = Baz (Either Int x) [x] -- This is also ok, since [] and Either Int
                                        -- are both Applicative
    data Void x -- This is not ok, since pure would be impossible to define.
    

    要派生Applicative,我们首先需要定义一个包装器,用于通过泛型派生:

    {-# LANGUAGE DerivingVia #-}
    {-# LANGUAGE DeriveFunctor #-}
    {-# LANGUAGE UndecidableInstances #-}
    {-# LANGUAGE DeriveGeneric #-}
    module Generically1 where
    
    import GHC.Generics
    
    newtype Generically1 f x = Generically1 { generically1 :: f x }
    
    fromg1 :: Generic1 f => Generically1 f a -> Rep1 f a
    fromg1 = from1 . generically1
    
    tog1 :: Generic1 f => Rep1 f x -> Generically1 f x
    tog1 = Generically1 . to1
    
    instance (Functor f, Generic1 f, Functor (Rep1 f)) 
           => Functor (Generically1 f) where
      fmap f (Generically1 x) = Generically1 $ fmap f x
    
    instance (Functor f, Generic1 f, Applicative (Rep1 f)) 
           => Applicative (Generically1 f) where
      pure = tog1 . pure
      f <*> x = tog1 $ fromg1 f <*> fromg1 x
    
    instance (Functor f, Generic1 f, Monad (Rep1 f)) => Monad (Generically1 f) where
      return = pure
      m >>= f = tog1 $ fromg1 m >>= fromg1 . f
    

    为了使用它,我们首先为我们的数据类型派生Generic1,然后通过我们新的Generically1 包装器派生Applicative

    data Foo x = Foo x (Int -> x) (Foo x)
      deriving (Functor, Generic1)
      deriving (Applicative, Monad) via Generically1 Foo
    
    data Bar x = Bar x (IO x)
      deriving (Functor, Generic1)
      deriving (Applicative, Monad) via Generically1 Bar
    
    data Baz f x = Baz (f x) (f x)
      deriving (Show, Functor, Generic1)
      deriving (Applicative, Monad) via Generically1 (Baz f)
    

    如您所见,我们不仅为我们的数据类型派生了Applicative,还可以派生Monad


    之所以有效,是因为有ApplicativeMonad 的实例用于这些数据类型的Generic1 表示。参见例如Product type (:*:)。然而,Sum type (:+:) 没有 Applicative 的实例,这就是为什么我们不能为非确定性类型派生它的原因。

    您可以通过在 GHCi 中编写 :kind! Rep1 Foo 来查看数据类型的 Generic1 表示。以下是上述类型表示的简化版本(不包括元数据):

    type family Simplify x where
      Simplify (M1 i c f) = Simplify f
      Simplify (f :+: g) = Simplify f :+: Simplify g
      Simplify (f :*: g) = Simplify f :*: Simplify g
      Simplify x = x
    
    λ> :kind! Simplify (Rep1 Foo)
    Simplify (Rep1 Foo) :: * -> *
    = Par1 :*: (Rec1 ((->) Int) :*: Rec1 Foo)
    
    λ> :kind! Simplify (Rep1 Bar)
    Simplify (Rep1 Bar) :: * -> *
    = Par1 :*: Rec1 IO
    
    λ> :kind! forall f. Simplify (Rep1 (Baz f))
    forall f. Simplify (Rep1 (Baz f)) :: k -> *
    = forall (f :: k -> *). Rec1 f :*: Rec1 f
    

    编辑:Generically1 包装器也可在此处获得:https://hackage.haskell.org/package/generic-data-0.7.0.0/docs/Generic-Data.html#t:Generically1

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    • 2013-11-21
    • 1970-01-01
    • 1970-01-01
    • 2012-01-29
    • 2011-11-05
    相关资源
    最近更新 更多