为什么无法在 Haskell 中导出 Applicative Functors?
Why there is no way to derive Applicative Functors in Haskell?
在Haskell中,您可以使用deriving
自动导出Functor
、Foldable
和Traversable
。但是,无法导出 Applicative
。考虑到有一种明显的方法可以定义 Applicative
实例(相当于压缩应用程序),难道没有任何方法可以启用 deriving Applicative
?
不,这一点都不明显。比较以下 Applicative
个实例:
[]
ZipList
Data.Sequence.Seq
, whose Applicative
instance declaration 运行到几百行。
IO
(->) r
parsec
、attoparsec
、regex-applicative
中的解析器。
- Proxy 在
pipes
包中。
这里几乎没有统一性,而且大多数情况都不明显。
作为 David Young ,[]
和 ZipList
实例 "are both, ultimately, two different, equally valid Applicative
instances for the list type."
既然 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
。
之所以可行,是因为这些数据类型的 Generic1
表示有 Applicative
和 Monad
的实例。例如参见 [=31=],这就是为什么我们不能为非确定性类型派生它。
您可以通过在 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
在Haskell中,您可以使用deriving
自动导出Functor
、Foldable
和Traversable
。但是,无法导出 Applicative
。考虑到有一种明显的方法可以定义 Applicative
实例(相当于压缩应用程序),难道没有任何方法可以启用 deriving Applicative
?
不,这一点都不明显。比较以下 Applicative
个实例:
[]
ZipList
Data.Sequence.Seq
, whoseApplicative
instance declaration 运行到几百行。IO
(->) r
parsec
、attoparsec
、regex-applicative
中的解析器。- Proxy 在
pipes
包中。
这里几乎没有统一性,而且大多数情况都不明显。
作为 David Young []
和 ZipList
实例 "are both, ultimately, two different, equally valid Applicative
instances for the list type."
既然 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
。
之所以可行,是因为这些数据类型的 Generic1
表示有 Applicative
和 Monad
的实例。例如参见 [=31=],这就是为什么我们不能为非确定性类型派生它。
您可以通过在 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