Hask甚至是一个类别吗?

Is Hask even a category?

https://wiki.haskell.org/Hask:

Consider:

undef1 = undefined :: a -> b
undef2 = \_ -> undefined

Note that these are not the same value:

seq undef1 () = undefined
seq undef2 () = ()

This might be a problem, because undef1 . id = undef2. In order to make Hask a category, we define two functions f and g as the same morphism if f x = g x for all x. Thus undef1 and undef2 are different values, but the same morphism in Hask.

这是什么意思或我如何检查: undef1undef2是不同的值,但是同一个态射?

在 Haskell 中,我们认为每个表达式都可以计算为特定的 "value",我们可能有兴趣确定两个表达式是否具有 "same" 值。

非正式地,我们知道可以直接比较某些值(例如 Integer 类型的值 23)。其他值,如 sqrtid 类型 Double -> Double 可以进行比较,正如@pigworker 指出的那样,通过构造一个表达式 "witnesses" 直接比较值的差异:

sqrt 4 = 2
id 4 = 4

这里,我们可以得出结论,sqrtid是不同的值。如果没有这样的见证人,那么价值是一样的。

如果我们查看 undef1undef2() -> () 类型的单态特化:

undef1, undef2 :: () -> ()
undef1 = undefined
undef2 = \_ -> undefined

我们如何判断这些值是否不同?

好吧,我们需要找到一个可以证明差异的表达式,上面已经给出了一个。两个表达式:

> seq undef1 ()
*** Exception: Prelude.undefined
> seq undef2 ()
()
>

根据 GHCi,具有不同的值。我们也可以直接展示这一点,使用我们对 Haskell 语义的理解:

seq undef1 ()
-- use defn of undef1
= seq undefined ()
-- seq semantics:  WHNF of undefined is _|_, so value is _|_
= _|_

seq undef2 ()
-- use defn of undef2
= seq (\_ -> undefined) ()
-- seq semantics: (\_ -> undefined) is already in WHNF and is not _|_,
--   so value is second arg ()
= ()

所以,问题是什么?好吧,当将 Hask 视为一个 类别 时,对象是类型而态射是(单态)函数,我们隐含地需要一个概念 identity/equality 用于对象和态射。

对象 identity/equality 很简单:两个对象(单态 Haskell 类型)当且仅当它们是同一类型时才相等。态射 identity/equality 更难。因为 Hask 中的态射是 Haskell 值(单态函数类型),所以很容易将态射相等定义为与值相等相同,如上所述。

如果我们使用这个定义,那么undef1undef2将是不同的态射,因为我们已经证明它们是不同的Haskell以上值。

但是,如果我们比较 undef1 . idundef2,我们会发现它们具有 相同的 值。也就是说,没有任何表情可以看出它们之间的区别。证明这有点困难,但请参阅下文。

无论如何,我们现在的 Hask 范畴理论存在矛盾。因为 idHask 中的(多态族)恒等态射,我们必须有:

undef1 
= undef1 . id                            -- because `id` is identity
= undef2                                 -- same value

所以我们同时有 undef1 /= undef2 因为上面的见证,但是 undef1 = undef2 由前面的论点。

避免这种矛盾的唯一方法是放弃将 Hask 中的态射相等定义为基础 Haskell 值相等的想法。

已提供的 Hask 中态射相等的另一个替代定义是两个态射 fg 相等的较弱定义,如果和仅当它们满足所有值 x(包括 _|_)的 f x = g x 时。请注意,这里仍然存在歧义。如果 f xg x 它们自己 Haskell 函数等态射,f x = g x 是否意味着 态射相等 f xg x 或 Haskell f xg x 相等?让我们暂时忽略这个问题。

根据这个替代定义,undef1undef2 等同的态射,因为我们可以显示 undef1 x = undef2 x 所有可能的值 x 类型 ()(即 ()_|_)。也就是说,应用于 (),他们给出:

undef1 ()
-- defn of undef1
= undefined ()
-- application of an undefined function
= _|_

undef2 ()
-- defn of undef2
= (\_ -> undefined) ()
-- application of a lambda
= undefined
-- semantics of undefined
= _|_

并应用于 _|_ 他们给出:

undef1 _|_
-- defn of undef1
= undefined _|_
-- application of an undefined function
= _|_

undef2 _|_
-- defn of undef2
= (\_ -> undefined) _|_
-- application of a lambda
= undefined
-- semantics of undefined
= _|_

类似地,undef1 . idundef2 可以证明在 Hask 中作为态射是等同的(事实上,它们等同于 Haskell 值暗示它们根据 Hask 态射的较弱的相等性定义是相等的),所以没有矛盾。

但是,如果您遵循@n.m.提供的link,您会发现在形式化Haskell值相等的含义方面还有更多工作要做并精确地给出 Hask 态射相等性的适当定义,然后我们才能真正放心地相信 无矛盾 Hask 类别。

证明 undef1 . id = undef2 作为 Haskell 值

由于上述原因,这个证明必然有点不正式,但这是我的想法。

如果我们试图见证函数 fg 之间的差异,见证表达式可以 使用 这些值的唯一方法是应用它们的值 x 或使用 seq 将它们评估为 WHNF。如果已知 fg 等于 Hask 态射,那么我们已经对所有 x 有了 f x = g x,所以没有表达可以见证基于应用的差异。唯一剩下要检查的是,当它们被评估为 WHNF 时,它们要么都已定义(在这种情况下,根据先前的假设,它们在应用时将产生相同的值),要么它们都未定义。

因此,对于 undef1 . idundef2,我们只需要确保在对 WHNF 求值时它们都已定义或都未定义。很容易看出它们实际上都定义了 WHNFs:

undef1 . id 
-- defn of composition
= \x -> undef1 (id x)
-- this is a lambda, so it is in WHNF and is defined

undef2
-- defn of undef2
= \_ -> undefined
-- this is a lambda, so it is in WHNF and is defined

我们已经在上面为所有 x 建立了 undef1 x = undef2 x。从技术上讲,我们应该证明:

(undef1 . id) x
-- defn of composition
= (\x -> undef1 (id x)) x
-- lambda application
= undef1 (id x)
-- defn of id
= undef1 x

与所有 x 的 Haskell 值相等,这为所有 x 确定了 (undef1 . id) x = undef2 x。再加上两者都在上面定义了 WHNF,这足以表明 undef1 . id = undef2 与 Haskell 值相等。