试图了解clojure

Jul*_*an7 3 clojure

一位朋友试图让我相信clojure的好处,我正在尽力理解这种语言.我查看了很多网站并浏览了"勇敢与真实的Clojure"一书,但我仍然遗漏了一些东西.

我的背景是面向对象编程,程序对象代表现实世界的对象是我的第二天性.我已经读过clojure不是OO但是我无法理解clojure会做什么.

这是一个例子(非常简化).我正在编写一个用于计划退休收入的应用程序.它有一个储蓄账户类和一个养老金类.储蓄账户有余额和利率.它有一个Withdraw方法,在一年内取出一个指定的数量(传递的参数).余额减少了提取金额,每年增加利息金额.你永远不会超过余额.

养老金类有年收入和年增长率.这也有退出方法,但撤回的金额始终是年收入(每年增加百分比).即它的计算方法与储蓄账户不同.

在我的OO程序中,这两个类都实现了一个定义Withdraw方法的协议,因此我的代码可以以相同的方式处理它们,而不需要考虑它们的差异.

在clojure世界中,什么代表储蓄账户和养老金.即什么结构将实例数据和操作该数据的方法聚集在一起?在OO中,类定义数据和方法.该实例是数据的唯一出现.我定义类的代码文件告诉我有关该类所代表的对象的所有信息.clojure如何做到这一点,或者做什么呢?

m0s*_*it0 5

首先让我告诉你,我完全理解你的感受,我必须经历同样的情况.让我告诉你,学习Clojure(函数式编程)是完全值得的(当然这是个人评估).

Clojure确实有OOP工具,例如你有defrecorddefprotocol.但从本质上讲,它比面向对象更具功能性,如果你开始思考功能和不变性,你只会获得真正的好处.

要编写惯用的Clojure,您需要将思维从对象切换到函数.在函数式语言中,一切都围绕着函数,而不是数据(对象).使用简单的数据结构(例如地图)来表示数据.您不会像在OOP中那样将函数与其数据绑定.在您的情况下,您只需要一个数据结构来表示此数据,而不是类Savings Account和类Pension,例如地图:

(def saving-account {:balance 0.0, :interest-rate 0.0})
(def pension {:annual-income 0.0, :annual-increase 0.0})
Run Code Online (Sandbox Code Playgroud)

Withdraw方法只是每种情况的函数,例如:

(defn withdraw-from-saving-account [sa amount] ( <return updated saving account map> ))
(defn withdraw-from-pension [p] ( <return updated pension map> ))
Run Code Online (Sandbox Code Playgroud)

在Clojure中,您通常希望保持所有内容不可变,因此上述函数将返回新的更新映射,并且不会修改现有映射中的任何现有状态.这需要一些时间来适应来自像Java的OOP这样的重型可变状态范例.惯用语Clojure(作为大多数函数式编程语言)在很大程度上基于函数组合(这意味着尽可能短的单任务函数)和非常有限的变量使用.如果你曾经使用过*nix shell,这与命令管道/链接非常相​​似.

请注意,您还可以通过声明单个Withdraw 方法来简化上述功能.如果您愿意,也可以使用数据类型更严格地定义数据,并使用协议为这些记录实现接口,这与您在Java中的操作更接近.您需要理解的要点是,虽然Java围绕着类,但Clojure围绕着函数和不可变数据,而函数越纯粹越好.

  • 谢谢@ m0skit0.但是,如果这是在clojure中执行此操作的方法,那么在一个较大的程序中使用什么来构造代码以使其可读?如果在OO中我有20个类,每个类有15个方法,我的程序根据类(每个类的一个代码文件)进行划分.从你所写的内容看起来好像在clojure中我会有一个20 def的列表.另一份15名撤回名单的清单.还有15个其他函数的列表,每个函数都有一个单独的版本用于每个def.如果clojure没有强制执行高于功能级别的结构,那么如何实现可读性? (2认同)
  • @ Julian7 clojure有一个独特的名称空间抽象.关于Java的最大抱怨之一就是类的概念过于过载:它是Java为您提供的唯一抽象(在Java 8之前),因此您必须将它用于造成阻抗不匹配的所有内容:类很难替代适当的命名空间.我不知道个人理财是最容易理解的例子,但我会有不同的命名空间,数据和函数来操纵这些数据,核心命名空间将它们拼接成一个外部API. (2认同)
  • @ Julian7查看[Clojure,Made Simple](https://youtu.be/VSdnJDO-xdg?t=2947),尤其是49分钟里Rich Hickey对价值课程的咆哮."这些东西在HTTP中以电子形式传递给文本!我们怎么把它变成[HttpServletRequest]?发生了什么事?" (2认同)

Jar*_*ith 5

对我来说,程序对象代表现实世界的对象是第二天性

我认识的大多数面向对象的程序员都没有这样编程.当我用OO语言编程时,我不会那样编程.随着可能的 gamedev的例外,大多数在面向对象的程序对象只有最薄弱业务领域实体的连接,出于同样的原因,大多数我的SQL表的往往只有最薄弱业务域的实体连接.

商务人士不关心URIProcurerAbstractFactoryManagers或桥接表,因为他们没有真实的模拟.

我提到了上面的内容(以及关于SQL表的内容),因为关键是我们在软件工件方面创建的大部分内容必然与解决问题间接相关.银行和国家慈善机构的完全不相关的HTTP后端看起来非常相似(文件/目录/表名称可能是您正在查看的最佳线索).我们写的更多内容与问题类型的约束更多地相关,而不是问题本身的细节.

Clojure是一种不同的建模问题方法,就像关系表是一种不同的建模问题方式一样.

您可以尝试以对待对象的方式处理clojure数据结构.您可以尝试以对待对象的方式处理关系表.但它不会很好地工作(参见ORM的棘手和困扰的历史).您可以尝试将所有面向对象的程序对象视为真实世界的模拟对象,但这也不会让您走得太远.

你必须学习一种思考问题的新方法,我甚至会说你不会选择最好的问题,除非你至少熟悉他们中的大多数(所以要赞成你试图学习一个新的!).