A recent question通常询问各种Haskell类之间的界限。我提出Handler
作为有效Functor
的示例,没有明智的 Apply
**实例,其中
class Functor f => Apply f where
(<.>) :: f (a -> b) -> f a -> f b
-- optional bits omitted.
但是,我还没有找到一个有效Functor
的示例,该示例无法成为{em>有效(如果无意义)Apply
的实例。 Apply
已的事实(见更新)但是单一的法律,
(.) <$> u <.> v <.> w = u <.> (v <.> w)
似乎让这很棘手。
McBride先前Functor
的{{1}} Applicative
并非pure
,但他依靠Apply
这样做,Apply
中没有Handler
1}}。
**后来我意识到Apply
实际上可能有一个明智的(虽然有些奇怪的)Apply (Coyoneda f)
实例,它在概念上收集了同时发生的异常。
Edward Kmett现在gave an example为x <.> (f <$> y) = (. f) <$> x <.> y
f <$> (x <.> y) = (f .) <$> x <.> y
提出了另外两条法律(用于验证我对{{1}}实例所做的优化):
{{1}}
看看这些增加是否会改变这个问题的答案会很有趣。
答案 0 :(得分:10)
两个仿函数(来自transformers
的{{3}})的“总和”似乎就是一个例子。
可以轻松映射一个分支或另一个分支,但是如果函数中的函数和函数中的参数位于不同的分支中,如何实现<.>
?
ghci> import Data.Functor.Sum
ghci> import Data.Functor.Identity
ghci> let f = InL (Const ()) :: Sum (Const ()) Identity (Int -> Int)
ghci> let x = InR (Identity 5) :: Sum (Const ()) Identity Int
ghci$ f <.> x = ..... ?
答案 1 :(得分:9)
是的,有Functor
个没有Apply
个实例。考虑两个函数的总和(exponents in algebraic data types):
data EitherExp i j a
= ToTheI (i -> a)
| ToTheJ (j -> a)
所有Functor
和i
s都有j
个实例:
instance Functor (EitherExp i j) where
fmap f (ToTheI g) = ToTheI (f . g)
fmap f (ToTheJ g) = ToTheJ (f . g)
但所有Apply
和i
s
j
个实例
instance Apply (EitherExp i j) where
...
ToTheI f <.> ToTheJ x = ____
无法填写空白____
。为此,我们必须了解i
和j
的内容,但无法查看Haskell中的每个类型i
或j
。直觉拒绝这个答案;如果您知道关于i
或j
的任何,就像他们居住的是单个值一样,那么您可以为Apply
编写EitherExp
个实例}
class Inhabited a where
something :: a
instance (Inhabited i, Inhabited j) => Apply (EitherExp i j) where
...
ToTheI f <.> ToTheJ x = ToTheI (const ((f something) (x something)))
但我们不知道每个i
和每个j
都是Inhabited
。 Void
类型没有任何东西居住。我们甚至没有办法知道每种类型都是Inhabited
或Void
。
我们的直觉实际上非常好;当我们可以检查如何构造类型时,对于代数数据类型,没有Functor
没有Apply
个实例。以下是两个可能更令我们直觉感到满意的答案。
...代数数据类型。有3种可能性。结构无效,结构可以是空的,或者结构不能为空。如果结构无效,则absurd
为Apply
。如果它可以为空,请选择任何空实例并不断返回以进行任何应用。如果它不能为空,那么它是一个结构的总和,每个结构都不能为空,可以通过从第一个到第一个应用其中一个值†来实现守法的应用。来自第二个的值,并以一些不变的结构返回它。
适用法律非常宽松。申请不需要任何意义。它不需要是“zip-y”。与fmap
pure
中的Applicative
可疑内容结合使用时,无需pure
;没有u <.> v = empty
的概念可以写出要求它有意义的法律。
选择任何空实例并不断返回任何应用
(.) <$> u <.> v <.> w = u <.> (v <.> w)
(((.) <$> u) <.> v) <.> w = u <.> (v <.> w) -- by infixl4 <$>, infixl4 <.>
(_ ) <.> w = u <.> (_ ) -- by substitution
empty = empty -- by definition of <.>
证明
f
如果结构extract :: forall a. f a -> a
不能为空,则存在函数c :: forall a. a -> f a
。选择另一个函数u <.> v = c (extract u $ extract v)
,它始终构造与参数填充的相同的非空结构,并定义:
extract (f <$> u) = f (extract u)
extract . c = id
使用自由定理
(.) <$> u <.> v <.> w = u <.> (v <.> w)
(((.) <$> u) <.> v) <.> w = u <.> (v <.> w) -- by infixl4 <$>, infixl4 <.>
(c (extract ((.) <$> u) $ extract v)) <.> w = u <.> (v <.> w) -- by definition
(c ((.) (extract u) $ extract v)) <.> w = u <.> (v <.> w) -- by free theorem
c (extract (c ((.) (extract u) $ extract v)) $ extract w) = u <.> (v <.> w) -- by definition
c ( ((.) (extract u) $ extract v) $ extract w) = u <.> (v <.> w) -- by extract . c = id
c (((.) (extract u) $ extract v) $ extract w) = u <.> c (extract v $ extract w) -- by definition
c (((.) (extract u) $ extract v) $ extract w) = c (extract u $ extract (c (extract v $ extract w))) -- by definition
c (((.) (extract u) $ extract v) $ extract w) = c (extract u $ (extract v $ extract w) ) -- by extract . c = id
let u' = extract u
v' = extract v
w' = extract w
c (((.) u' $ v') $ w') = c (u' $ (v' $ w'))
c ((u' . v') $ w') = c (u' $ (v' $ w')) -- by definition of partial application of operators
c (u' $ (v' $ w')) = c (u' $ (v' $ w')) -- by definition of (.)
证明
extract
关于为指数类型函数定义i -> a
,值得多说一点。对于函数i
,有两种可能性。 i
有人居住或不居住。如果它有人居住,请选择一些居民extract f = f i
†并定义
i
如果i -> a
无人居住(它无效),那么absurd
是具有单个值Void -> a
的单位类型。 a
只是另一个精心设计的空类型,它不包含任何absurd :: Void -> a
absurd x = case x of {}
个;将其视为可以为空的结构。
当结构无效时,没有办法构造它。我们可以从每个可能的构造(没有传递给它)写一个函数到任何其他类型。
Functor
Void结构可以是fmap f = absurd
,Apply
。以同样的方式,他们可以拥有(<.>) = absurd
实例
u
我们可以简单地证明所有v
,w
和(.) <$> u <.> v <.> w = u <.> (v <.> w)
u
没有v
,w
或a
,且声明为vacuously true。
†关于接受选择公理的一些注意事项,为指数类型a -> b
选择索引Monad
...对于Haskell。想象一下除了IO
之外还有另一个基础OI
,我们称之为Sum IO OI
。然后Functor
是Apply
,但永远不能是{{1}}。
......为现实世界。如果你有一台可以发送功能的机器(或 Hask 以外的类别中的箭头),但是不能将两台机器组合在一起或者提取它们的运行状态,那么它们就是一个没有的Functor应用
答案 2 :(得分:4)
这不是你正在寻找的,但它已经接近所以我想我会分享它。标准Writer
monad对其Apply
实例有一个额外的约束(即,w
类型是Monoid
或Semigroup
的实例){ {1}}实例没有,因此如果Functor
不是Writer Foo
/ Functor
,则Apply
是Foo
而不是Semigroup
:
Monoid
然而,这并不是你所要求的一个例子,因为实际上可以创建一个守法的data Writer w a = Writer w a
instance Monoid w => Apply (Writer w) where
Writer w1 f <.> Writer w2 x = Writer (mappend w1 w2) (f x)
实例而没有Apply
约束:
Monoid
此实例的问题在于它不允许匹配的instance Apply (Writer w) where
Writer w1 f <.> Writer w2 x = Writer w1 (f x)
实例,因为无法实现Applicative
以便获得左侧标识,即:
pure
这只是一种长期的方式,可以为您提供与Conor相同的答案:依赖于pure id <.> x /= x
来破坏实例的演示。