Haskell中的函数和函子有什么区别?仅定义? [英] What's the difference between a function and a functor in Haskell? Only definition?

查看:141
本文介绍了Haskell中的函数和函子有什么区别?仅定义?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Haskell中,编写函数时,这意味着我们将某物(输入)映射到另一物(输出)。我尝试过LYAH来了解Functor的定义:就像普通的Functor一样。


  1. 对函数可能有任何限制吗?称为Functor?

  2. 是否允许Functor具有I / O或任何其他副作用?

  3. 如果在Haskell中,一切都是函数 ,那么引入 Functor概念又有什么意义呢?函数的受限版本还是功能的增强版本?

非常困惑,需要您的建议。
谢谢。

解决方案

它有助于了解一些类别理论。类别只是一组对象,它们之间带有箭头。他们可以在数学中为许多事物建模,但是出于我们的目的,我们对类型类别感兴趣。 Hask 是Haskell类型的类别,每种类型都是 Hask 中的对象,每个函数都是参数类型和返回类型之间的箭头。例如, Int Char [Char] ,和 Bool 都是 Hask 中的对象,而 ord :: Char-> Int odd :: Int-> Bool repeat :: Char-> [Char] Hask 中箭头的一些示例。



每个类别都有几个属性:


  1. 每个对象都有一个标识箭头。


  2. 箭头组成,因此,如果 a-> b b-> c 是箭头,那么 a->也是如此。 c


  3. 标识箭头既是左右标识,也是组成。


  4. 组合是关联的。


Hask 是一个原因类别是每个类型都有一个标识函数,并且函数组成。也就是说, id :: Int-> Int id :: Char-> Char 是类别的标识箭头,而 odd是。 ord ::字符->布尔(Bool)是组合箭头。



(现在忽略 id 是类型为 a-> a 的多态函数,而不是一堆具有具体类型的单独函数,这证明了范畴论中称为自然变换的概念




在范畴论中,函子F是两者之间的映射两类;它将一个类别的每个对象映射到另一个类别的对象,并且将一个类别的每个箭头映射到另一个类别的箭头。如果 a 是一个类别中的对象,我们说F a是另一个类别中的对象。我们还说,如果f是第一类中的箭头,则另一个箭头中的相应箭头是F f。



不仅任何映射都是函子。它必须服从看起来应该很熟悉的两个属性。


  1. F必须将对象a的标识箭头映射到对象a的标识箭头。对象F a。

  2. F必须保留组成。这意味着第一类中的两个箭头的组成必须映射到另一类中相应的箭头的组成。也就是说,如果 h = g∘f 在第一类中,则 h 被映射到 F h = F g∘F f 在另一个。

最后,一个 endofunctor 是函子的特殊名称,该函子将一个类别映射到其自身。在 Hask 中,类型类 Functor 捕获了将endofunctor从 Hask 转换为 Hask 的想法。 。类型构造函数本身会映射类型,并且 fmap 用于映射箭头。






让我们以也许为例。类型构造器 Maybe 是一个endofuntor,因为它将 Hask (类型)中的对象映射到 Hask 中的其他对象(其他类型)。 (由于我们没有用于目标类型的新名称,因此这一点有点模糊,因此可以考虑将也许映射为 Int 的类型为也许是Int 。)



要映射箭头 a -> b 也许是->也许b ,我们在 Maybe Int 的实例中为 fmap 提供了一个定义。
也许也会映射函数,但改用名称 fmap 。必须遵守的函子定律与函子定义中列出的两个定律相同。


  1. fmap id = id (将 id :: Int-> Int 映射到 id :: Maybe Int-> Maybe Int

  2. fmap f。fmap g = fmap f。g (即 fmap奇数。fmap ord $ x 必须返回与 fmap(odd。ord)$ x 相同的值,以获取任何可能的值<$ c $类型为也许是Int 的c> x 。






作为不相关的切线,其他人指出Haskell中的某些内容不是函数,即像 4 hello 。虽然在编程语言中为true(例如,您不能将 4 与另一个函数以 Int 作为值),那么是真的,根据类别理论,您可以用单元类型<$ c $中的函数替换值c>()到值的类型,即eral值4可以看作是箭头 4 ::()-> Int 表示,当将其应用于()类型的(唯一)值时,它返回的类型为 Int 对应于整数4。 odd。 4 ::()-> Bool 会将值从单位类型映射到一个布尔值,指示整数4是否为奇数。



从数学上讲,这很好。我们不必为类型定义任何结构;它们只是 are ,并且由于我们已经定义了类型,所以我们不需要为类型的值定义单独的定义;我们只是根据功能定义它们。 (不过,您可能会注意到我们仍然需要单位类型的实际值。在我们的定义中可能有一种避免该值的方法,但是我对分类理论的了解不够,无法以一种或另一种方式进行解释。) / p>

对于我们的编程语言的实际实现,请将字面值视为一种优化,以避免必须使用 4的概念和性能开销( )代替每次只需要一个恒定值的 4


In Haskell, when writing a function, it means we map something(input) to another thing(output). I tried LYAH to understand the definition of Functor: seems just the same like a normal Functor.

  1. Is there any restriction that a function could be called a Functor?
  2. Is Functor allowed to have I/O or any other side effect?
  3. If in Haskell, "everthing is a function", then what's the point of introducing the "Functor" concept? A restricted version of function, or an enhancement version of a function?

Very confused, need your advice. Thanks.

解决方案

It helps to know a little category theory. A category is just a set of objects with arrows between them. They can model many things in mathematics, but for our purposes we are interested in the category of type; Hask is the category of Haskell types, with each type being an object in Hask and each function being an arrow between the argument type and the return type. For example, Int, Char, [Char], and Bool are all objects in Hask, and ord :: Char -> Int, odd :: Int -> Bool, and repeat :: Char -> [Char] would be some examples of arrows in Hask.

Each category has several properties:

  1. Every object has an identity arrow.

  2. Arrows compose, so that if a -> b and b -> c are arrows, then so is a -> c.

  3. Identity arrows are both left and right identities for composition.

  4. Composition is associative.

The reason that Hask is a category is that every type has an identity function, and functions compose. That is, id :: Int -> Int and id :: Char -> Char are identity arrows for the category, and odd . ord :: Char -> Bool are composed arrows.

(Ignore for now that we think of id is polymorphic function with type a -> a instead of a bunch of separate functions with concrete types. This demonstrates a concept in category theory called a natural transformation that you don't need to think about now.)


In category theory, a functor F is a mapping between two categories; it maps each object of one category to an object of the other, and it also maps each arrow of one category to an arrow of the other. If a is an object in one category, we say that F a is the object in the other category. We also say that if f is an arrow in the first category, the corresponding arrow in the other if F f.

Not just any mapping is a functor. It has to obey two properties which should look familiar.

  1. F has to map the identity arrow for an object a to the identity arrow of the object F a.
  2. F has to preserve composition. That means that the composition of two arrows in the first category has to be mapped to the composition of the corresponding arrows in the other category. That is, if h = g ∘ f is in the first category, then h is mapped to F h = F g ∘ F f in the other.

Finally, an endofunctor is a special name for a functor that maps one category to itself. In Hask, the typeclass Functor captures the idea of an endofunctor from Hask to Hask. The type constructor itself maps the types, and fmap is used to map the arrows.


Let's take Maybe as an example. The type constructor Maybe is an endofuntor, because it maps objects in Hask (types) to other objects in Hask (other types). (This point is obscured a little bit since we don't have new names for the target types, so think of Maybe as mapping Int to the type Maybe Int.)

To map an arrow a -> b to Maybe a -> Maybe b, we provide a defintion for fmap in the instance of Maybe Int. Maybe also maps functions, but using the name fmap instead. The functor laws it must obey are the same as two listed in the definition of a functor.

  1. fmap id = id (Maps id :: Int -> Int to id :: Maybe Int -> Maybe Int.
  2. fmap f . fmap g = fmap f . g (That is, fmap odd . fmap ord $ x has to return the same value as fmap (odd . ord) $ x for any possible value x of type Maybe Int.


As an unrelated tangent, others have pointed out that some things in Haskell are not functions, namely literal values like 4 and "hello". While true in the programming language (you can't, for instance, compose 4 with another function that takes an Int as a value), it is true that in category theory that you can replace values with functions from the unit type () to the type of the value. That is, the literal value 4 can be thought of as an arrow 4 :: () -> Int that, when applied to the (only) value of type (), it returns a value of type Int corresponding to the integer 4. This arrow would compose like any other; odd . 4 :: () -> Bool would map the value from the unit type to a Boolean value indicating whether the integer 4 is odd or not.

Mathematically, this is nice. We don't have to define any structure for types; they just are, and since we already have the idea of a type defined, we don't need a separate definition for what a value of a type is; we just just define them in terms of functions. (You might notice we still need an actual value from the unit type, though. There might be a way of avoiding that in our definition, but I don't know category theory well enough to explain that one way or the other.)

For the actual implementation of our programming language, think of literal values as being an optimization to avoid the conceptual and performance overhead of having to use 4 () in place of 4 every time we just want a constant value.

这篇关于Haskell中的函数和函子有什么区别?仅定义?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆