41
import Control.Applicative

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

产生(1,4). 我希望它能够产生(2,4),但该函数仅应用于元组的第二个元素。

更新我基本上马上就明白了这一点。我会在一分钟内发布我自己的答案..

4

3 回答 3

31

让我用一个问题来回答这个问题:您期望哪个输出:

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

可以拥有任何你想要的东西(使用data Pair a = Pair a a或左右),但由于(,)它们的第一个和第二个参数中可能有不同的类型,你不走运。

于 2012-11-18T17:59:20.623 回答
24

对基本上是这样定义的:

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

该类Functor如下所示:

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

由于函数参数和结果的类型必须有类型*(即它们表示值而不是类型函数,可以应用更远或更奇异的东西),我们必须有a :: *, b :: *,并且,对于我们的目的而言,最重要的是,f :: * -> *。既然(,)has kind * -> * -> *,就必须将它应用到 kind 的一个类型上,*才能得到一个适合作为 a 的类型Functor。因此

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

所以实际上没有办法编写一个Functor实例来做任何其他事情。


一个有用的类提供了更多使用对的方法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

这使您可以编写如下内容(来自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 (,)则本质上与 相同Pair,其中

data Pair a = Pair a a

当然,您也可以直接使用Bifunctor实例来处理对。

于 2016-01-05T04:38:31.030 回答
18

Functor实例实际上来自GHC.Base模块,该模块由Control.Applicative.

尝试编写我想要的实例,我可以看到它不会工作,给定元组的定义;该实例只需要一个类型参数,而 2 元组有两个。

一个有效的Functor实例至少必须在元组上,(a,a)每个元素都具有相同的类型,但你不能做任何偷偷摸摸的事情,比如在以下位置定义实例:

 type T2 a = (a,a)

因为不允许实例类型是同义词。

上述受限的 2 元组同义词在逻辑上与类型相同:

data T2 a = T2 a a

可以有一个 Functor 实例:

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

正如 Gabriel 在评论中所说,这对于分支结构或并发很有用。

于 2012-11-18T17:35:50.600 回答