Haskell中的((a-> b)->(c-> d)`吗?

时间:2018-12-21 14:27:08

标签: haskell definition category-theory

这是另一个Haskell通过类别理论的问题。

让我们以一个简单而众所周知的例子为例。 fmap? 因此,fmap :: (a -> b) -> f a -> f b忽略了f实际上是Functor的事实。据我了解,(a -> b) -> f a -> f b只是(a -> b) -> (f a -> f b)语法糖;因此得出结论:

(1) fmap是产生函数的函数。

现在, Hask 也包含函数,因此(a -> b),尤其是(f a -> f b)是Hask的一个对象(因为对象的Hask是定义明确的Haskell类型-一种数学集合-的确存在每个可能的(a -> b)的类型a的集合,对吗?)。因此,再次:

(2) (a -> b)是Hask的对象。

现在发生了奇怪的事情:fmap很明显是Hask的态射,因此它是一个函数,它需要另一个函数并将其转换为另一个函数; 最终功能尚未应用

因此,从(f a -> f b)f b,还需要Hask的态射。对于类型为i的每个项目a,存在定义为apply_i :: (f a -> f b) -> f b的同态\f -> f (lift i),其中lift i是使用以下方法构建f a的方法特别是i

另一种查看方式是GHC样式:(a -> b) -> f a -> f b。与我上面所写的相反,(a -> b) -> f a映射到Hask的常规对象。但是这样的观点与基本的Haskell公理相矛盾-没有多元函数,而是应用了(咖喱的)替代方法。


在这一点上,我想问一下:(a -> b) -> f a -> f b是否应该是(a -> b) -> (f a -> f b) -> f b,为简单起见而加糖,还是我在这里真的缺少了非常重要的东西?

4 个答案:

答案 0 :(得分:6)

  

(a -> b) -> f a -> f b应该是(a -> b) -> (f a -> f b) -> f b,为简单起见加糖

不。我认为您所缺少的并不是您的错,这只是在非常特殊的情况下,(a -> b) -> (f a -> f b)中的中间箭头可以称为 morphism ,外(a -> b) -> (f a -> f b)可以。 Functor类的一般情况是(使用伪语法)

class (Category (──>), Category (~>)) => Functor f (──>) (~>) where
  fmap :: (a ──> b) -> f a ~> f b

因此,它把箭头表示为──>的类别中的态射映射到~>类别中的射态,但是这种射态映射本身只是一个 function 。您的权利,在 Hask 中,功能箭头与态射箭头的箭头相同,但是从数学上讲,这是一个相当简陋的场景。

答案 1 :(得分:4)

fmap实际上是整个射影的一个家族 Hask 中的变态总是从一种具体类型到另一种具体类型。如果函数具有具体的参数类型和具体的返回类型,则可以将其视为同态。类型为Int -> Int的函数表示 Hask 中从IntInt的一个态(实际上是一个内态)。 fmap,但类型为Functor f => (a -> b) -> f a -> f b。看不到具体的类型!我们只有类型变量和拟运算符=>要处理。

请考虑以下一组具体的函数类型。

Int -> Int
Char -> Int
Int -> Char
Char -> Char

进一步,考虑以下类型构造函数

[]
Maybe
应用于[]

Int返回一个我们可以调用List-of-Ints的类型,但是我们通常只调用[Int]。 (当我刚开始使用函子时,最令人困惑的事情之一是我们只是没有单独的名称来引用各种类型构造函数产生的类型;输出只是由对其求值的表达式来命名。){{ 1}}返回我们刚刚调用的类型,Maybe Int

现在,我们可以定义一堆类似于以下的功能

Maybe Int

Hask 中,每一个都是不同的形态,但是当我们在Haskell中定义它们时,会有很多重复。

fmap_int_int_list :: (Int -> Int) -> [Int] -> [Int]
fmap_int_char_list :: (Int -> Char) -> [Int] -> [Char]
fmap_char_int_list :: (Char -> Int) -> [Char] -> [Int]
fmap_char_char_list :: (Char -> Char) -> [Char] -> [Char]
fmap_int_int_maybe :: (Int -> Int) -> Maybe Int -> Maybe Int
fmap_int_char_maybe :: (Int -> Char) -> Maybe Int -> Maybe Char
fmap_char_int_maybe:: (Char -> Int) -> Maybe Char -> Maybe Int
fmap_char_char_maybe :: (Char -> Char) -> Maybe Char -> Maybe Char

fmap_int_int_list f xs = map f xs fmap_int_char_list f xs = map f xs fmap_char_int_list f xs = map f xs fmap_char_char_list f xs = map f xs fmap_int_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y) fmap_int_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y) fmap_char_int_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y) fmap_char_char_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y) 的类型不同时,定义没有不同,仅f / x的类型不同时。这意味着我们可以定义以下 polymorphic 函数

xs

每个代表 Hask 中的一组射态。

fmap_a_b_list f xs = map f xs fmap_a_b_maybe f x = case x of Nothing -> Nothing; Just y -> Just (f y) 本身是一个笼统的术语,我们用来指代所有多态函数所引用的特定于构造函数的态射。

通过这种方式,我们可以更好地了解fmap

鉴于fmap :: Functor f => (a -> b) -> f a -> f b,我们首先来看一下fmap f的类型。例如,我们可能会发现f,这意味着f :: Int -> Int必须返回fmap ffmap_int_int_list中的一个,但是我们不确定哪个。因此,它返回类型为fmap_int_int_maybe constrained 函数。一旦将该函数应用于类型为Functor f => (Int -> Int) -> f Int -> f Int[Int]的值,我们最终将获得足够的信息来知道实际上是哪个形态学。

答案 2 :(得分:3)

  

现在发生了奇怪的事情:fmap很明显是Hask的一种形态,因此它是一个函数,它需要另一个函数并将其转换为另一个函数;最终功能尚未应用。

     

因此,从(f a-> f b)到f b还需要一个Hask的态射。对于类型为a的每个项i,都有一个态射apply_i ::(fa-> fb)-> fb定义为\ f-> f(提升i),其中,提升i是一种建立带有特定i的fa的方法。

类别理论中的应用概念以CCC's - Cartesian Closed Categories的形式建模。如果您具有自然双射(X×Y,Z)≅(X,Y⇒Z),则类别为CCC。

尤其意味着存在一个自然变换(评估),其中[Y,Z] :(Y⇒Z)×Y→Z,这样对于每一个g:X×Y→Z都有ag: X→(Y⇒Z)这样,g = g×id; [Y,Z]。所以当你说

  

因此,从(f a-> f b)到f b还需要一个Hask的态射。

(f a -> f b)f b,或者使用上面的符号,从(f a ⇒ f b)[f a,f b]:(f a ⇒ f b) × f a → f b的方式。

要记住的另一个重要点是,类别理论中的“元素”不是原始概念。而是一个元素,其形式为→X,这是终端对象。如果取X =,则具有(Y,Z)≅(×Y,Z)≅(,Y⇒Z)。也就是说,态素g:Y→Z与元素g:→(Y⇒Z)具有双射性。

在Haskell中,这意味着功能正好是箭头类型的“元素”。因此,在Haskell中,将通过对y:→Y的h:→(Y⇒Z)求值来对应用h y进行建模。也就是说,对(h)×y:→(Y⇒Z)×Y的求值由(h)×y; [Y,Z]:→Z组成。

答案 3 :(得分:2)

为完整起见,此答案的重点是各种评论中提到的问题,而其他答案则没有。

  

另一种查看方式是GHC风格:(a -> b) -> f a -> f b。与我上面所写的相反,(a -> b) -> f a映射到Hask的常规对象。

类型签名中的

->是右关联的。既然如此,(a -> b) -> f a -> f b实际上与(a -> b) -> (f a -> f b)相同,并且在其中看到(a -> b) -> f a就是语法上的混淆。没什么不同...

(++) :: [a] -> [a] -> [a]

...并不意味着部分应用(++)会给我们一个[a]列表(相反,它给我们提供了添加一些列表的功能)。

从这个角度来看,您提出的类别理论问题(例如,关于“需要从(f a -> f b)f b的另一个Hask态射”)是一个单独的问题问题,Jorge Adriano's answer解决得很好。