import Control.Applicative
main = print $ fmap (*2) (1,2)
产生(1,4)
. 我希望它能够产生(2,4)
,但该函数仅应用于元组的第二个元素。
更新我基本上马上就明白了这一点。我会在一分钟内发布我自己的答案..
让我用一个问题来回答这个问题:您期望哪个输出:
main = print $ fmap (*2) ("funny",2)
你可以拥有任何你想要的东西(使用data Pair a = Pair a a
或左右),但由于(,)
它们的第一个和第二个参数中可能有不同的类型,你不走运。
对基本上是这样定义的:
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
实例来处理对。
该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 在评论中所说,这对于分支结构或并发很有用。