前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >从 Java 和 JavaScript 来学习 Haskell 和 Groovy(DSL)

从 Java 和 JavaScript 来学习 Haskell 和 Groovy(DSL)

作者头像
四火
发布于 2022-07-19 05:49:05
发布于 2022-07-19 05:49:05
57100
代码可运行
举报
文章被收录于专栏:四火的唠叨四火的唠叨
运行总次数:0
代码可运行

这是《从 Java 和 JavaScript 来学习 Haskell 和 Groovy》系列的第四篇。

首先来理解 DSL。

DSL(Domain Specific Language)指的是一定应用领域内的计算机语言,它可以不强大,它可以只能在一定的领域内生效(和 GPL 相比,GPL 是 General Purpose Language),表达仅限于该领域,但是它对于特定领域简洁、清晰,包含针对特定领域的优化。

当我们面对各种各样的特定需求的时候,一个通用的语言往往不能高效地提供解决问题的路径,相应的 DSL 并不是并非要解决所有的问题,但是它只关注于某一个小领域,以便解决那一个小领域的问题就好了。比如 HTML,只用于网页渲染,出了这个圈子它什么都不做,但是用来表达网页的内容却很擅长,有很多内置的标签来表达有预定义含义的内容;再比如 SQL,只能写数据库相关的操作语句,但是很适合用来描述要查询什么样的一个数据集合,要对数据集合中的元素做什么样的操作。

先来看 Java。用 Java 写 DSL 是可能的,但是写高效和简洁的 DSL 是困难的。原因在于它的语法限制,必须严谨的括号组合,不支持脚本方式执行代码等等。

首先讲讲链式调用。这也不是 Java 特有的东西,只不过 Java 的限制太多,能帮助 DSL 的特性很少,第一个能想到的就是它而已。比如这样的代码,组织起 html 文本来显得有层次、有条理:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
document
  .html()
    .body()
      .p()
        .text("context 1")
      .end()
      .p()
        .text("context 2")
      .end()
    .end()
  .end()
.creat();

链式调用还有一个令人愉快的特性是泛型传递,我在这篇文章中介绍过,可以约束写 DSL 的人使用正确的类型。

其次是嵌套函数,这也不是 Java 特有的东西,它和链式调用组成了 DSL 最基本的实现形式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
new Map(
  city("Beijing", x1, y1),
  city("Shanghai", x2, y2),
  city("Guangzhou", x3, y3)
);

值得一提的是 Java 的闭包,可以说闭包是融合了管道操作和集合操作美感的,谈 DSL 不能不谈闭包。但是,直到 014 年 4 月 JSR-335 才正式 final release,不能不说这个来得有点晚。有了闭包,有了 Lambda 表达式(其实本质就是匿名函数),也就有了使用函数式编程方式在 Java 中思考的可能。

考虑一下排序的经典例子,可以自定义 Comparator<T> 接口的实现,从而对特定对象列表进行排序。对于这样的类 T:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class T {
	public Integer val;
}

可以使用匿名的 Comparable 实现类来简化代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Collections.sort(list, new Comparator<T>() {
	@Override
	public int compare(T o1, T o2) {
		return o1.val.compareTo(o2.val);
	}
});

但是如果使用 JDK8 的 Lambda 表达式,代码就简化为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Collections.sort(list, (x, y) -> y - x);

更加直观,简洁。

那么为什么 (x,y) -> y-x 这样的 Lambda 表达式可以被识别为实现了 Comparator 接口呢?

原来这个接口的定义利用了这样的语法糖:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@FunctionalInterface
public interface Comparator<T> {
    ...
}

这个 @FunctionalInterface 的注解,是可以用来修饰 “函数接口” 的,函数接口要求整个接口中只有一个非 java.lang.Object 中定义过的抽象的方法(就是没有具体实现的方法,且方法签名没有在 java.lang.Object 类中出现过,因为所有类都会实现自 java.lang.Object 的,那么该类中已定义的方法可以认为已经有默认实现,接口中再出现就不是抽象方法了)。

好,有了这一点知识以后还是回头看这个 Comparator 接口的定义,有这样两个抽象方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int compare(T o1, T o2);
boolean equals(Object obj);

那么按照刚才的说法,其中的 equals 方法是在 java.lang.Object 中出现过的,不算,在考察函数接口的合法性时,其实只有一个 compare 这一个抽象方法。

顺便加一句吐槽。该接口还有几个方法的 default 实现,“接口的默认方法”,为了在增加行为的情况下,考虑向下兼容,总不能把 Comparator 把接口改成抽象类吧,于是搞了这样一个语法糖,但是它是如此地毁曾经建立的三观,接口已经可以不再是纯粹的抽象了。

接着来看 JavaScript 的 DSL。其实就 DSL 的实现而言,Java 和 JavaScript 来实现并没有非常多的区别,最大的区别可能是,JavaScript 中,function 可以成为一等公民,因此能够写更加灵活的形式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
new Wrapper([1, 2, 5, 3, 4])
  .filter(filterFunc)
  .map(mapFunc)
  .sort()
  .zipWith([7, 8, 9, 10, 11]);

再给一个高阶函数(Curry 化)的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var logic = new Logic()
  .whenTrue(exp1)
  .whenFalse(exp2);

console.log(logic.test(3>2));

动态语言和丰富语法糖的关系,Groovy 是非常适合用来写 DSL 的。一方面是因为语法糖的关系,万物皆对象,省掉不少累赘的括号,代码看起来比较自然,接近自然语言;另一方面是有不少语言特性,比如 MethodMissing,帮助写出简洁的 DSL。下面分别说明,例子大多来自这个官网页面

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// equivalent to: take(2.pills).of(chloroquinine).after(6.hours)
take 2.pills of chloroquinine after 6.hours

看到上面这个,因为简简单单的语法糖,就使得代码如此接近自然语言,是否有很心旷神怡的感觉?

这个是个更复杂一些的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
show = { println it }
square_root = { Math.sqrt(it) }

def please(action) {
  [the: { what ->
    [of: { n -> action(what(n)) }]
  }]
}

// equivalent to: please(show).the(square_root).of(100)
please show the square_root of 100
// ==> 10.0

上面定义了 show 和 square_root 的闭包,然后在 please 方法中,调用返回了一个对象,可以继续调用 the 方法,其结果可以继续调用 of 方法。action 是 please 方法的闭包参数,square_root 是 the 方法的闭包参数。挺有趣的,好好品味一下。

再有这个我曾经举过的例子,生成 HTML 树,利用的就是 MethodMissing(执行某一个方法的时候,如果该方法不存在,就可以跳到特定的统一的某个方法上面去),这样避免了写一大堆无聊方法的问题:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def page = new MarkupBuilder()
page.html {
  head { title 'Hello' }
  body {
    a ( href:'http://...' ) { 'this is a link' }
  }
}

当然了,Groovy 已经内置了一大堆常用的 builder,比如这个 JsonBuilder:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
JsonBuilder builder = new JsonBuilder()
builder.records {
  car {
        name 'HSV Maloo'
        make 'Holden'
        year 2006
        country 'Australia'
        record {
            type 'speed'
            description 'production pickup truck with speed of 271kph'
        }
  }
}
String json = JsonOutput.prettyPrint(builder.toString())

利用元编程的一些特性,也可以让一些本来没有的方法和功能,出现在特定的对象上面,从而支持 DSL。比如 Categories,这个,我在前面一篇 《元编程》中已经介绍过了。

最后来说 Haskell。

作为语言特性的一部分,利用(1)模式匹配的守护语句和(2)List Comprehension 带来的条件分类,免去了 if-else 的累赘,对于逻辑的表达,可以极其简约。

关于上面(1)模式匹配的部分,《元编程》中已经有过介绍,下面给一个(2)List Comprehension 的经典例子,快排:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
  let smallerSorted = quicksort [a | a <- xs, a <= x]
       biggerSorted = quicksort [a | a <- xs, a > x]
  in smallerSorted ++ [x] ++ biggerSorted

上面这个快排算法,清晰,而且简洁。相比以前用 Java 写的快排,用 Haskell 写真是太酷了。

前文已经介绍过了高阶函数的使用,但是在 Haskell 中,所有的函数都可以理解为,每次调用最多都只接受一个参数,如果有多个参数怎么办?把它化简为多次调用的嵌套,而非最后一次调用,都可视为高阶函数(返回函数的函数)。比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Prelude> :t max
max :: Ord a => a -> a -> a

上面描述的调用本质决定了为什么它的结构是 a->a->a:接受一个类型 a 的参数,再接受一个类型 a 的参数,最终返回的类型和入参相同。

也就是说,这两者是等价的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
max 1 2
(max 1) 2

继续谈论和 DSL 相关的语言特性,尾递归和惰性求值。

对于尾递归不了解的朋友可以先参考维基百科上的解释。如果递归函数的递归调用自己只发生在最后一步,并且程序可以把这一步的入栈操作给优化掉,也就是最终可以使用常量栈空间的,那么就可以说这个程序/语言是支持尾递归的。

它有什么好处?因为可以使用常量栈空间了,这就意味着再也没有递归深度的限制了。

不过话说回来,Haskell 是必须支持尾递归的。因为对于常规语言,如果面临递归工作栈过深的问题,可以优化为循环解决问题;但是在 Haskell 中,是没有循环语法的,这就意味着必须用尾递归来解决这个本来得用循环才能解决的问题。

给一个例子,利用尾递归,我们自己来实现 list 求长度的函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
len :: (Num b) => [a] -> b
len [] = 0
len (_:xs) = 1 + len xs

然后是惰性求值,直到最后一步,非要求值不可前,不做求值的操作。听起来简单,但是只有 Haskell 是真正支持惰性求值的,其他的语言最多是在很局限的范围内,基于优化语言运行性能的目的,运行时部分采用惰性求值而已。

有了惰性求值,可以写出一些和无限集合之间的互操作,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sum (takeWhile (<10) (filter odd (map (^2) [1..])))

这是对于正整数序列(无限集合)中的每个元素,平方以后再判断奇偶性,只取奇数的结果,最后再判断是否小于 10,最后再把满足条件的这些结果全部加起来。

当然,利用语法糖,可以把上面讨厌的嵌套给拉平,从而去掉那些恼人的括号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sum . takeWhile (<10) . filter odd . map (^2) $ [1..]

两者功能上没有任何区别。

下一篇,也预计是最后一篇,我想着重介绍一下整体的角度来看时,编程范型的部分。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
固定资产管理系统能给企业带来哪些好处?
曾几何时,行政、IT和固定资产管理员还在为海量固定资产的管理和盘点而惆怅。而今,有了固定资产管理系统,这些问题都可以迎刃而解。在梳理好自己企业内部的固定资产管理需求之后,上一套适合自己企业的固定资产管理软件很有必要。
易点易动固定资产管理系统
2021/07/01
8060
固定资产密集型企业如何有效管理固定资产?
很多固定资产密集型企业往往在固定资产管理上花费了大量的人力和成本。在日常的经营管理中,许多企业依旧采用传统方式管理企业的固定资产,由于实物资产数量大、存放地点分散、职权不清以及粗放式管理,经常会出现权责不明确,固定资产分配不合理,闲置率高,重复购买,维护成本高等问题,从而导致大量固定资产的状态不明,丢失严重,固定资产的实际利用率低下,给企业的发展带来阻碍。
易点易动固定资产管理系统
2021/08/04
9440
如何在固定资产管理的路上乘风破浪?
目前,随着企业发展内部的需求推动,加上外界环境的多变,更多企业越来越重视,不但要降本增效为企业发展助力,而且要加速智能化发展的步伐。然而,要加强企业不同细分领域的智能化发展需要循序渐进,非一朝一夕之功。
易点易动固定资产管理系统
2021/08/13
3170
固定资产管理系统--企业降本增效的必备工具
随着公司业务和规模的日益扩大,公司的固定资产数量和种类也随之扩大和增长。这就给固定资产管理和盘点工作带来了一定的挑战。对于大型的企业而言,由于实物资产数量的庞大,传统的手工盘点和表格管理固定资产的方式已不能满足固定资产管理的需求,管理出错的风险也不断提高。而且由于无法实现信息化管理,固定资产的重复采购率和闲置率也居高不下,这无疑增加了企业的运营成本。
易点易动固定资产管理系统
2021/11/05
4610
固定资产管理系统--企业降本增效的必备工具
固定资产管理系统让企业动态掌握资产情况
很多企业经常会因为固定资产信息分散、查询不便、信息反映实效性差、纸质文档不易保存等问题导致资产流失、重复购买严重等问题,从而导致企业成本大幅度提升。越来越多的企业开始使用专业的固定资产管理系统,它可以将企业的固定资产信息、人员信息、部门信息等进行整合,还可以实现多层级和多组织架构的管理,实现固定资产的统筹管理,从而整合企业资产信息,提升固定资产利用率,减少重复采购并避免资产流失。
易点易动固定资产管理系统
2022/06/29
3820
固定资产管理系统让企业动态掌握资产情况
固定资产管理系统如何简化固定资产管理和盘点工作?
固定资产管理通常是各企业或事业单位的行政和财务以及IT部门的必修工作之一。那么,如何管理好数量庞大的固定资产,提升固定资产的使用效率,降低闲置率和重构率是评估固定资产管理工作做得好坏的重要标准。现实工作中,固定资产的数量多、领用审批凌乱、盘点难度大、固定资产的信息更新不及时等很多问题一直困扰着资产的管理者。
易点易动固定资产管理系统
2020/09/02
1.1K0
互联网行业如何高效地管理固定资产?
互联网行业通常对电脑、桌椅等办公设备的需求量大,而且对于质量要求比较高。互联网行业的扩张也比较迅速,经常需要扩充员工人数。如果不断开拓市场中,市场覆盖的区域都可以设置子公司或办事处以开展当地的业务。这样以来,互联网公司的固定资产往往具有以下特点:电子设备资产量大、人员数量和流动性比较大、异地管理等。
易点易动固定资产管理系统
2021/03/17
6290
互联网行业如何高效地管理固定资产?
8款最好用的固定资产管理软件
固定资产作为企业生产经营的重要部分,越来越被众多企业主重视。之前,大部分公司会选择EXCEL来管理固定资产。在固定资产系统如雨后春笋般涌出的时代,大部分公司会选择一款固定资产管理软件来精细化管理固定资产。尤其是分公司和分支比较多的集团公司,或者资产量比较多的企业和单位。
易点易动固定资产管理系统
2020/07/22
3.9K1
如何提升企业内部的固定资产管理水平?
经过对国内近千家企业的调研发现,很多企业在固定资产管理上依然有很多薄弱环节。大部分企业依然存在如下的问题:1)从固定资产采购到资产报废、处置整个流程缺乏全过程管理。从而导致固定资产重复购置问题严重,利用率低下,造成企业运营成本增加。2)每年一次或者两次的固定资产盘点,通常人力不足,盘点工作很难执行到位,外包盘点成本高。
易点易动固定资产管理系统
2021/07/28
4660
如何提升企业内部的固定资产管理水平?
企业何如选择固定资产管理系统
2020年已经过半,今年对于大多数企业来说都是不平常的一年。有许多企业的业务受阻,甚至生存都出现危机。今年,企业主正好可以放慢前行的脚步去完善今后的战略规划,以寻求内在发展动力。
易点易动固定资产管理系统
2020/07/03
6940
说说IT企业固定资产管理系统的问题和解决方法
IT企业随着业务的扩大、人员的增多,企业的固定资产数量和种类都会随之越来越多。当IT企业发展到一定规模后,内部管理通常会遇到一些问题:
易点易动固定资产管理系统
2021/12/13
6990
说说IT企业固定资产管理系统的问题和解决方法
固定资产云系统如何破解制造业数字化转型升级难题?
近年来,无论是互联网行业还是传统的制造行业,几乎各行各业都在积极拥抱数字化,旨在提升企业的效率,从容地应对瞬息万变的市场变换和竞争。当下,各行各业数字化转型升级的需求依然迫切,制造业亦如是。
易点易动固定资产管理系统
2021/07/23
3450
固定资产管理流程和技巧
企业固定资产是保证企业正常运行的重要基础物质条件,其管理及核算状况影响对企业业务活动的开展,经济效益的提高。从目前实际情况看,企业在固定资产管理,核算等方面存在诸多问题,因此,加强企业固定资产管理成为不容忽视的问题。
易点易动固定资产管理系统
2021/08/16
7090
如何实现固定资产管理智能化?
行政人员、IT人员、固定资产管理员在管理和盘点企业的固定资产时,往往都会出现以下场景:
易点易动固定资产管理系统
2021/07/21
5570
教育行业固定资产管理的解决方案
近年来,随着课外教育机构的迅速发展,办学规模越来越大,很多连锁教育机构对于固定资产管理管理上面还是各有各的痛点。另外,对于很多幼儿园、小学、中学、高中、大学而言,固定资产管理和盘点上工作仍然是一个薄弱环节。这其中有不少因素:从固定资产的数量看,业务活动和机构分支多,加上固定资产数量骤增,管理难度确实日益增大。从管理流程和方法上看,分支机构的调整、学校内部后勤、财务、行政、人事等工作需要协调和规范。
易点易动固定资产管理系统
2021/07/22
6270
为什么说手工表格管理固定资产已经OUT了?
以电子表格、纸质数据为主的传统固定资产管理和盘点的方式已被各种各样的固定资产管理系统所取代。人工管理的弊端和缺陷如下:
易点易动固定资产管理系统
2021/09/18
5810
为什么说手工表格管理固定资产已经OUT了?
企业如何进行高效化固定资产管理?
在信息化的今天,企业飞速发展的路上,如何早日实现企业信息化管理,是每个企业管理者思考的问题。作为企业管理中至关重要的一环,固定资产管理也被越来越多的管理者所重视。因为随着企业规模扩大、业务范围拓宽、人员数量的增多,如何有效地进行固定资产管理已经成为每一个企业管理者重要的一部分。
易点易动固定资产管理系统
2021/11/29
4210
企业如何进行高效化固定资产管理?
零售商超固定资产管理系统解决方案
零售超市在日常的固定资产管理和运营中比较复杂,遇到的各种实际操作问题比较多,为此,易点易动在对多家商业超市进行深入调查和需求梳理之后,提出了符合零售商超行业的固定资产管理方案。
易点易动固定资产管理系统
2021/10/19
4980
零售商超固定资产管理系统解决方案
企业数字化转型, RFID固定资产管理系统不可或缺
在企业普遍加快信息化进程的当下,越来越多的企业开始注重企业内部的管理。比如客户管理、合同管理、固定资产管理等。越来越多的企业摆脱了手工管理模式,开始引入各种系统给企业信息化助力。固定资产管理系统也被引入到企业的管理中。采用现代的云计算技术、条码、二维码技术、RFID等技术与相应的硬件扫描技术相结合,优化了固定资产的全生命周期管理流程,极大地提高了企业实物资产的管理和盘点效率,降低固定资产重复采购和丢失率,提升固定资产的利用率,为企业实现降本增效。
易点易动固定资产管理系统
2021/10/15
4850
企业数字化转型, RFID固定资产管理系统不可或缺
2023年企业固定资产管理怎么破局?
2022年已经在风雨中过去,转眼我们迎来了2023年。过去的一年,固定资产管理的痛依旧历历在目,如何让新的一年中,固定资产管理工作有所突破,不再承受固定资产资产管理的痛处,是每个企业管理者和企业固定资产管理员思考的问题。
易点易动固定资产管理系统
2022/12/23
4330
2023年企业固定资产管理怎么破局?
推荐阅读
相关推荐
固定资产管理系统能给企业带来哪些好处?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验