将函数放在Clojure映射中是不是一个坏主意,就像在JavaScript中一样?

时间:2011-02-10 19:45:13

标签: functional-programming clojure

我是Clojure的新手并找到了我的海腿。我想知道从功能编程的角度来看,将函数放在Clojure映射中,然后将这些映射像准对象一样传递,就像在JavaScript中经常做的那样,是否认为它是好的还是坏的。解释也将不胜感激。

6 个答案:

答案 0 :(得分:8)

这样做,如果它使您的代码更短或更容易理解,或测试或调试。

或者如果你愿意的话。相信你的判断。

答案 1 :(得分:7)

Clojure的多种方法本质上是函数的映射,所以不,这根本不是一个坏主意。

答案 2 :(得分:6)

由于以下几个原因,这会很糟糕:

  1. 没有必要 - 在JavaScript中,我们这样做是为了将简单的地图用作类似Java的对象。在Clojure中,如果您真的想这样做,可以通过Java互操作使用实际的Java对象。
  2. 无论是将函数放在地图中还是使用真正的Java对象,您都在为函数式编程设计的语言环境中进行面向对象编程。随之而来的第一个问题是你的代码只是不适合。它在Clojure中看起来很奇怪,笨拙甚至可能太复杂。在这一点上,我并不是说OOP必然是坏的,只是最好用为其设计的语言编写OOP。
  3. 反对这种编码风格的最重要的一点是,类似OOP的程序将不可避免地依赖于具有某种状态的对象,以及它们改变该状态的成员函数(有点像方法)。如果您正在使用状态,那么您的功能并不纯粹,您无法使用所有功能优点,如简单的并行化。
  4. 长话短说,如果你使用Clojure做OOP,你只会生气。你可以用很多其他语言轻松地做OOP,比如说Groovy。如果你确实想要使用Clojure,那么就可以使用它。

    到目前为止,我写道不要这样做,为什么不这样做。现在您可能会问我:那么,我应该如何在Clojure中编写函数?

    编写将数据结构(即地图,列表,对象......等)作为参数的函数。所以,而不是:

     foo.bar();
    

    您可以这样定义:

     (defn bar [foo]
       ;stuff
       ) 
    

    并称之为:

      (bar foo)
    

    现在,对于纯函数barfoo对象在函数求值后保持不变,如果您决定并行化代码,则不需要担心对象的共享状态如果你以OOP的方式做事,你会有的。

    此外,它可能看起来很小,但请注意bar函数定义是一个完全独立于数据结构foo的实体。此外,foo仅包含数据,而不包含行为 - 所有行为都在函数中。这种分离在编码时为您提供了更大的自由。

答案 3 :(得分:4)

它有效 - 从某种意义上说,如果你将函数视为语言中的第一类对象(正如所有函数式程序员所应该的那样),这是很自然的。)

然而 - 它需要真正关心,因为你基本上做的是用数据交错代码。这就像将数据模型与MVC中的表示代码混合在一起。是的,可能在某些情况下它是有意义的,但一般原则是避免它。

经过大约一年的Clojure,我慢慢收敛的风格是:

  • 一些顶级参考/原子/ vars,用于清晰管理可变状态(感谢Rich Hickey!)
  • 顶级参考中的大型嵌套不可变数据结构
  • 处理几乎所有处理的纯函数
  • 一些函数以!结尾!对于那些令人讨厌的副作用,谨慎使用
  • 很多很多测试!这非常重要,因为通过动态类型和非常灵活的数据结构,您需要测试几乎所有的假设
  • Java(遗憾的是!)用于优化性能敏感的代码段,通常通过定义一个在Clojure数据结构中运行良好的轻量级,不可变的Java类来完成

这解决了太阳下的大多数事情,我还想弄清楚的一件事是创建具有高度多态行为的对象的最佳方法。我能看到的主要选项是:

  • 使用带有deftype / defrecord的协议 - 高性能,惯用的Clojure,但你只能获得单一的调度类型,这对于某些高度多态的情况是不够的
  • 创建通过大量宏/高阶函数组合以多态方式运行的函数 - 工作,但可以非常复杂/复杂维护
  • 将功能放在地图中!是的,感觉不对,但它确实有效!

还没有找到哪条路.....但我有预感,功能可能会在未来潜入地图......

答案 4 :(得分:2)

如果你真的需要在Clojure中进行面向对象编程,有几种方法可以做到。

第一种方法是使用deftypedefrecorddefprotocol系列宏。

第二种方法是使用多方法,结合地图或记录类型进行数据存储。

第三种方法是使用Steve Yegge概述的通用设计模式,或者在Chris Houser和Michael Fogus的书The Joy of Clojure中找到更多Clojure特定的介绍。

第三种方法与您在JavaScript中所期望的非常类似,第一种方法虽然没有被认为是传统意义上的OOP,但却是“现代”惯用语中最常见的方法。

答案 5 :(得分:1)

命名空间实际上是函数的映射。使用它们来组织你的功能要简单得多。但是,如果您遇到用例的命名空间限制,可以考虑将函数放在映射中。

像往常一样,当你走出人迹罕至的地方时,一定要认真思考为什么你不会走这条明显的路线。