简体   繁体   English

为什么2元组Functor实例仅将函数应用于第二个元素?

[英]Why does the 2-tuple Functor instance only apply the function to the second element?

import Control.Applicative

main = print $ fmap (*2) (1,2)

produces (1,4) . 产生(1,4) I would expect it it to produce (2,4) but instead the function is applied only to the second element of the tuple. 我希望它会产生(2,4)但是该功能仅应用于元组的第二个元素。

Update I've basically figured this out almost straight away. 更新我基本上已经几乎马上就知道了。 I'll post my own answer in a minute.. 一分钟后,我会发布我自己的答案。

Let me answer this with a question: Which output do you expect for: 让我回答一个问题:您期望得到什么输出:

main = print $ fmap (*2) ("funny",2)

You can have something as you want (using data Pair a = Pair aa or so), but as (,) may have different types in their first and second argument, you are out of luck. 可以根据需要设置某些内容(使用data Pair a = Pair aa左右),但是由于(,)在其第一个和第二个参数中可能具有不同的类型,因此您很不走运。

Pairs are, essentially, defined like this: 对本质上是这样定义的:

data (,) a b = (,) a b

The Functor class looks like this: Functor类如下所示:

class Functor f where
  fmap :: (a -> b) -> f a -> f b

Since the types of function arguments and results must have kind * (ie they represent values rather than type functions that can be applied further or more exotic things), we must have a :: * , b :: * , and, most importantly for our purposes, f :: * -> * . 由于函数参数和结果的类型必须具有* (即它们表示值,而不是可以进一步应用或用于其他奇特事物的类型函数),因此我们必须具有a :: *b :: * ,并且最重要的是我们的目的是f :: * -> * Since (,) has kind * -> * -> * , it must be applied to a type of kind * to obtain a type suitable to be a Functor . 由于(,)种类为* -> * -> * ,因此必须将其应用于种类*以获得适合用作Functor的类型。 Thus 从而

instance Functor ((,) x) where
  -- fmap :: (a -> b) -> (x,a) -> (x,b)

So there's actually no way to write a Functor instance doing anything else. 因此,实际上没有办法编写Functor实例来做其他事情。


One useful class that offers more ways to work with pairs is Bifunctor , from Data.Bifunctor . 提供更多使用对的方法的有用类是Bifunctor Data.Bifunctor

class Bifunctor f where
  bimap :: (a -> b) -> (c -> d) -> f a c -> f b d
  bimap f g = first f . second g

  first :: (a -> b) -> f a y -> f b y
  first f = bimap f id

  second :: (c -> d) -> f x c -> f x d
  second g = bimap id g

This lets you write things like the following (from Data.Bifunctor.Join ): 这使您可以编写如下内容(从Data.Bifunctor.Join ):

  newtype Join p a =
    Join { runJoin :: p a a }

  instance Bifunctor p => Functor (Join p) where
    fmap f = Join . bimap f f . runJoin

Join (,) is then essentially the same as Pair , where 然后Join (,)Pair基本上相同,其中

data Pair a = Pair a a

Of course, you can also just use the Bifunctor instance to work with pairs directly. 当然,您也可以只使用Bifunctor实例直接处理配对。

The Functor instance is actually from the GHC.Base module which is imported by Control.Applicative . Functor实例实际上来自Control.Applicative导入的GHC.Base模块。

Trying to write the instance I want, I can see that it won't work, given the definition of tuples; 尝试编写我想要的实例,鉴于元组的定义,我可以看到它不起作用。 the instance requires just one type parameter, while the 2-tuple has two. 该实例仅需要一个类型参数,而2元组则具有两个。

A valid Functor instance would at least have to be on tuples, (a,a) that have the same type for each element, but you cannot do anything sneaky, like define the instance on: 一个有效的Functor实例至少必须位于每个元素具有相同类型的元组(a,a)上,但是您不能做任何偷偷摸摸的事情,例如在以下实例上定义该实例:

 type T2 a = (a,a)

because instance types aren't permitted to be synonyms. 因为实例类型不允许为同义词。

The above restricted 2-tuple synonym is logically the same as the type: 上面的受限2元组同义词在逻辑上与类型相同:

data T2 a = T2 a a

which can have a Functor instance: 可以有一个Functor实例:

instance Functor T2 where
    fmap f (T2 x y) = T2 (f x) (f y)

As Gabriel remarked in the comments, this can be useful for branching structures or concurrency. 正如Gabriel在评论中指出的那样,这对于分支结构或并发很有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM