为什么没有办法在Haskell中派生Applicative Functors?

Mai*_*tor 17 haskell generic-programming applicative

在Haskell中,你可以得到Functor,FoldableTraversable自动使用deriving.Applicative但是,没有办法得出.考虑到有一种明显的方法来定义一个Applicative实例(相当于一个压缩的应用程序),有没有办法启用deriving Applicative

dfe*_*uer 18

不,这根本不明显.比较以下Applicative实例:

  1. []
  2. ZipList
  3. Data.Sequence.Seq,其Applicative实例声明运行到几百行.
  4. IO
  5. (->) r
  6. 解析器parsec,attoparsec,regex-applicative.
  7. 代理服务器pipes包.

这里几乎没有一致性,大多数情况都不明显.


正如David Young所 评论的那样[],ZipList实例"最终是两个不同的,同样有效Applicative的列表类型实例."

  • 此外,`[]`和`ZipList`最终都是列表类型的两个不同的,同样有效的`Applicative`实例. (17认同)

Hju*_*lle 5

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

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.
Run Code Online (Sandbox Code Playgroud)

要派生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
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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


这个工作的原因是,有对实例ApplicativeMonadGeneric1这些数据类型的表示。例如,请参阅产品类型(:* :)。然而,有没有实例Applicative总和型(:+ :),这就是为什么我们不能推导出它的不确定性类型。

您可以Generic1通过写:kind! Rep1 FooGHCi 来查看数据类型的表示形式。这是上述类型的表示的简化版本(不包括元数据):

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
Run Code Online (Sandbox Code Playgroud)

编辑:Generically1包装器也可以在这里找到:https : //hackage.haskell.org/package/generic-data-0.7.0.0/docs/Generic-Data.html#t : Generically1

  • [`Genericically1`](https://gitlab.haskell.org/ghc/ghc/-/issues/17147) 已添加到 `GHC.Generics` 中 (2认同)