函数和非归纳类型

bro*_*sbp 6 haskell

我正在研究Typeclassopedia中的Functors部分.

一个简单的直觉是Functor代表某种"容器",以及将函数统一应用于容器中的每个元素的能力.

好.因此,对于像列表或树这样的归纳类型,仿函数看起来非常自然.

如果元素的数量固定为较小的数字,Functors也会显得非常简单.例如,使用Maybe你只需要关注"Nothing"或"Just a" - 两件事.

那么,你如何制作类似图形的东西,可能有循环,一个Functor的实例?我认为更普遍的方式是,非归纳类型如何"适应"Functors?


我越是想到它,我就越意识到归纳/非归纳并不重要.归纳类型更容易定义fmap...

如果我想将图形作为Functor的一个实例,我将不得不在fmap中实现图形遍历算法; 例如,它可能必须使用一个辅助函数来跟踪被访问的节点.在这一点上,我现在想知道为什么还要把它定义为Functor而不仅仅是把它作为一个函数本身?例如地图vs fmap列表......?

我希望有经验,有战争故事和伤疤的人可以解释一下.谢谢!

Dan*_*zer 8

好吧,假设您定义了这样的图形

data Graph a = Node a [Graph a]
Run Code Online (Sandbox Code Playgroud)

然后fmap就像你期望的那样精确定义

instance Functor Graph where
  fmap f (Node a ns) = Node (f a) (map (fmap f) ns)
Run Code Online (Sandbox Code Playgroud)

现在,如果有一个循环,那么我们不得不做类似的事情

foo = Node 1 [bar]
bar = Node 2 [foo]
Run Code Online (Sandbox Code Playgroud)

现在fmap已经足够懒惰了,你可以在不强制计算其余部分的情况下评估其部分结果,因此它的工作方式与任何结关联的图形表示一样好!

一般来说,这就是诀窍:fmap懒惰,所以你可以像处理Haskell中的任何非归纳值一样对待它的结果(:仔细).

此外,您应该定义fmapvs随机其他函数

  1. fmap 是一个很好的,众所周知的API规则
  2. 您的容器现在可以很好地满足您Functor的需求
  3. 你可以抽象出你的程序的其他部分Functor,而不是你的图形

一般来说,当我看到某些东西是仿函数时,我觉得"很好,我知道如何使用它",当我看到时

superAwesomeTraversal :: (a -> b) -> Foo a -> Foo b
Run Code Online (Sandbox Code Playgroud)

我有点担心这会做出意想不到的事情.