函数和非归纳类型

时间:2014-06-05 00:16:41

标签: haskell

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

  

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

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

如果元素数量固定为较小的数字,那么Functors也会显得非常简单。例如,对于Maybe,您只需要关注" Nothing"或者"只是一个" - 两件事。

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


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

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

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

1 个答案:

答案 0 :(得分:8)

我们假设您定义了一个这样的图表

data Graph a = Node a [Graph a]

然后fmap正好按照您的预期定义

instance Functor Graph where
  fmap f (Node a ns) = Node (f a) (map (fmap f) ns)

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

foo = Node 1 [bar]
bar = Node 2 [foo]

现在fmap非常懒惰,您可以在不强制执行其余计算的情况下评估其部分结果,因此它与任何结关联的图形表示一样好!

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

此外,您应该定义fmap vs。随后的其他函数

  1. fmap是一个很好的,众所周知的API,带有规则
  2. 您的容器现在可以很好地处理期待Functor s
  3. 的事情
  4. 您可以抽象出程序中的其他位,以便它们依赖于Functor,而不是您的图形
  5. 一般来说,当我看到某些东西是一个仿函数时,我想"太棒了,我知道如何使用它"当我看到

    superAwesomeTraversal :: (a -> b) -> Foo a -> Foo b
    

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