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

Pet*_*all 34 haskell tuples functor

import Control.Applicative

main = print $ fmap (*2) (1,2)
Run Code Online (Sandbox Code Playgroud)

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

更新我基本上已经基本上直接解决了这个问题.我会在一分钟内发布自己的答案..

Lan*_*dei 31

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

main = print $ fmap (*2) ("funny",2)
Run Code Online (Sandbox Code Playgroud)

可以随心所欲(使用data Pair a = Pair a a左右),但由于(,)第一和第二个参数可能有不同的类型,你运气不好.


dfe*_*uer 20

对,基本上是这样定义的:

data (,) a b = (,) a b
Run Code Online (Sandbox Code Playgroud)

这个Functor类看起来像这样:

class Functor f where
  fmap :: (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

由于类型的函数参数和结果必须有一种*(即其代表的值,而不是可以进一步或更奇特的东西应用型功能),就必须有a :: *,b :: *以及最重要的是我们的目的,f :: * -> *.既然(,)有种类* -> * -> *,就必须应用于某种类型*以获得适合的类型Functor.从而

instance Functor ((,) x) where
  -- fmap :: (a -> b) -> (x,a) -> (x,b)
Run Code Online (Sandbox Code Playgroud)

所以实际上没有办法写一个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
Run Code Online (Sandbox Code Playgroud)

这可以让你写下面的内容(from 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
Run Code Online (Sandbox Code Playgroud)

Join (,)然后基本上是相同的Pair,在哪里

data Pair a = Pair a a
Run Code Online (Sandbox Code Playgroud)

当然,您也可以直接使用Bifunctor实例来配对.


Pet*_*all 15

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

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

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

 type T2 a = (a,a)
Run Code Online (Sandbox Code Playgroud)

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

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

data T2 a = T2 a a
Run Code Online (Sandbox Code Playgroud)

可以有一个Functor实例:

instance Functor T2 where
    fmap f (T2 x y) = T2 (f x) (f y)
Run Code Online (Sandbox Code Playgroud)

正如Gabriel在评论中所说,这对于分支结构或并发性非常有用.

  • 值得一提的是,如果要指定要映射的元组的哪个部分,可以使用`Control.Lens`.`Setter`就像你明确指定的`Functor`实例,所以`over _1(+1)(5,3)`==>`(6,3)`; `over _2(*2)("funny,2)`==>`("funny",4)`;`两个长度("hi","there")`==>`(2,5) `;`over(both._1)(*10)((1,2),(3,4))`==>`((10,2),(30,4))`. (12认同)
  • 实际上,它作为仿函数类的一个实例很有用.例如,我可以将树定义为`type Tree a = Free T2 a`.实际上,该类型的大多数用法(作为函子)都涉及某种分支或并发. (8认同)
  • 如果要在第一个元素或第二个元素上运行它,也可以使用箭头:`first`和`second`. (2认同)