是否有一个不能守法的Functor申请?

时间:2016-03-29 22:16:50

标签: haskell functor

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 examplex <.> (f <$> y) = (. f) <$> x <.> y f <$> (x <.> y) = (f .) <$> x <.> y 提出了另外两条法律(用于验证我对{{1}}实例所做的优化):

{{1}}

看看这些增加是否会改变这个问题的答案会很有趣。

3 个答案:

答案 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)

所有Functori s都有j个实例:

instance Functor (EitherExp i j) where
    fmap f (ToTheI g) = ToTheI (f . g)
    fmap f (ToTheJ g) = ToTheJ (f . g)

但所有Applyi s

都没有j个实例
instance Apply (EitherExp i j) where
    ...
    ToTheI f <.> ToTheJ x = ____

无法填写空白____。为此,我们必须了解ij的内容,但无法查看Haskell中的每个类型ij。直觉拒绝这个答案;如果您知道关于ij任何,就像他们居住的是单个值一样,那么您可以为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都是InhabitedVoid类型没有任何东西居住。我们甚至没有办法知道每种类型都是InhabitedVoid

我们的直觉实际上非常好;当我们可以检查如何构造类型时,对于代数数据类型,没有Functor没有Apply个实例。以下是两个可能更令我们直觉感到满意的答案。

不......

...代数数据类型。有3种可能性。结构无效,结构可以是空的,或者结构不能为空。如果结构无效,则absurdApply。如果它可以为空,请选择任何空实例并不断返回以进行任何应用。如果它不能为空,那么它是一个结构的总和,每个结构都不能为空,可以通过从第一个到第一个应用其中一个值来实现守法的应用。来自第二个的值,并以一些不变的结构返回它。

适用法律非常宽松。申请不需要任何意义。它不需要是“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 = absurdApply。以同样的方式,他们可以拥有(<.>) = absurd 实例

u

我们可以简单地证明所有vw(.) <$> u <.> v <.> w = u <.> (v <.> w)

u

没有vwa,且声明为vacuously true

关于接受选择公理的一些注意事项,为指数类型a -> b选择索引Monad

是......

...对于Haskell。想象一下除了IO之外还有另一个基础OI,我们称之为Sum IO OI。然后FunctorApply,但永远不能是{{1}}。

......为现实世界。如果你有一台可以发送功能的机器(或 Hask 以外的类别中的箭头),但是不能将两台机器组合在一起或者提取它们的运行状态,那么它们就是一个没有的Functor应用

答案 2 :(得分:4)

这不是你正在寻找的,但它已经接近所以我想我会分享它。标准Writer monad对其Apply实例有一个额外的约束(即,w类型是MonoidSemigroup的实例){ {1}}实例没有,因此如果Functor不是Writer Foo / Functor,则ApplyFoo而不是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 来破坏实例的演示。