【问题标题】:Why does <$> act only on the second member of a pair?为什么 <$> 只作用于一对中的第二个成员?
【发布时间】:2010-09-22 07:54:59
【问题描述】:

快速浏览一下 GHCi 中的以下交互式会话:

Prelude>导入Control.Applicative Prelude Control.Applicative> (+1) [1,2] [2,3] Prelude Control.Applicative> (+1) (1,2) (1,3)

我想&lt;$&gt; 的行为是有充分理由的,但到目前为止我还没有找到,所以:

为什么将&lt;$&gt;(或fmap)定义为仅作用于一对中的第二个成员而不作用于两个值?

【问题讨论】:

标签: haskell functional-programming


【解决方案1】:

&lt;$&gt;(又名fmap)是 Functor 类的成员,如下所示:

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

因此,无论 f 是什么,都必须是具有一个类型参数的参数化类型。列表就是这样一种类型,当以前缀形式 [] 编写时([] a[a] 相同)。所以列表的实例是:

instance Functor [] where
  -- fmap :: (a -> b) -> [] a -> [] b 
  fmap = map

对也可以写成前缀形式:(,) a b(a, b) 相同。因此,让我们考虑一下如果我们想要一个包含对的 Functor 实例该怎么办。我们不能声明instance Functor (,),因为对构造函数(,) 有两种类型——它们可以是不同的类型!我们可以做的是为(,) a 声明一个实例——这是一个只需要一个类型的类型:

instance Functor ( (,) a ) where
  -- fmap :: (b -> c) -> (,) a b -> (,) a c
  fmap f (x, y) = (x, f y)

希望您能看到 fmap 的定义是我们可以给出的唯一合理的定义。关于为什么函子实例对一对中的第二个项目进行操作的答案是,第二个项目的类型在列表中排在最后!我们不能轻易地声明一个对一对中的第一项进行操作的仿函数实例。顺便说一句,这可以推广到更大的元组,例如四元组(,,,) a b c d(又名(a, b, c, d))也可以在最后一项上有Functor 实例:

instance Functor ( (,,,) a b c) where
  -- fmap :: (d -> e) -> (,,,) a b c d -> (,,,) a b c e
  fmap f (p, q, r, s) = (p, q, r, f s)

希望这有助于解释这一切!

【讨论】:

  • 你打字比我快...啊
  • 错误:示例应该是 instance Functor [] 而不是 class
【解决方案2】:

考虑Functor类型类的定义:

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

显然,f 有一种* -&gt; *。因此,您只能为类型为* -&gt; * 的数据类型声明实例。你能做的就是做一些这样的事情:

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

这适用于部分应用元组,并且非常不方便。所以一个人这样定义实例:

instance Functor ((,) a) where
  fmap :: (b -> c) -> (,) a b -> (,) a c
  fmap f (x,y) = (x,f y)

简而言之:在普通的 Haskell 98 中(尽管我相信对此有语法扩展)不可能将实例定义为

你可以做的是定义你自己的元组:

data T a = T a a

instance Functor T where
  fmap f (T a b) = T (f a) (f b)

然后你就可以为所欲为。你看,因为种类是* -&gt; * 而不是* -&gt; * -&gt; *,所以什么都可以。

【讨论】:

    【解决方案3】:

    我想,元组不需要是同质的,我的意思是两种类型都可以不同。如果你想要一个同构元组,你可以使用一个列表,然后 fmap 就可以了。

    您希望(+1) ("Hello", 2) 怎样工作?

    Prelude> import Control.Applicative
    Prelude Control.Applicative> (+1) <$> ("hello",2)
    ("hello",3)
    

    这只是工作,但是当两种类型相同时没有特殊行为。 顺便说一句,我不知道为什么不使用第二个值而不是第一个值,但无论如何你只能使用一个值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-28
      • 2019-02-09
      • 1970-01-01
      • 1970-01-01
      • 2017-03-08
      • 2018-06-29
      相关资源
      最近更新 更多