锁定老帖子 主题:jaskell script
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2005-01-09
这是我在abp上贴的介绍: 用jparsec作为parser, 做了一个jaskell script interpreter。 这是一个类似haskell的在java中运行的functional scripting language. (怎么样,俺起的jaskell这个名字酷吧?) 支持高阶函数,currying,call-by-name。用来在java中处理一些动态公式计算是挺不错的。(比如说从数据库中读取公式字符串,然后计算) 这个语言是无类型的,如果运行时类型发生错误,在动态抛出异常。 注意,如果你只熟悉JAVA, 有一点区别要清楚。 java里面的函数调用是用括号,如: f(a,b,c); jaskell则遵循haskell的语法,函数调用没有括号,参数之间则仅仅用空格隔开。 f(a,b,c)在这个语法下表示则是: f a b c。 括号在jaskell中的唯一作用就是grouping。改变优先级,把一个表达式作为一个整体。 f a b c也就可以写成,(f a b c)或者((f a b (c))), ((f) (a) b c)等等等等。 运算符除了一个前缀负号之外,其它的都是中缀的。 因为call-by-name和无类型,可以做很多有趣的事情。比如: 定点: fixpoint f = f (fixpoint f) 甚至lamda形式的定点: fact 4 where fact' f i = if i==0 then 1 else i*f (i-1); ; fact = Y fact'; Y = \h-> (\x->h (x x);); (\x->h (x x););; .. 这个fact 4将会返回4!,也就是24. 更有趣的是,虽然语言暂时没有提供list, switch-case的功能,用函数可以相当好地模拟: 这是模拟list的代码: build (+); 0 1 2 3 4 5 nil where build f a i = if i==nil then a else build f (f a i);; nil = \x->x; .. (+)是一个表示递加的函数。 头一个0是种子, 后面的1 2 3 4 5是这个list的内容。 nil是自定义的一个函数,用来作为结尾的标志。 因为我们是无类型的,build函数可以在输入为nil的时候返回计算结果, 而在其它的时候返回函数。 下面是有趣的switch-case的代码: translate 3 where translate x = switch x 1 "one" 2 "two" 3 "three" default "unknown" nil; switch x v c = if v==default or x==v then return c else switch x; return x a= if a==nil then x else return x; default = \x->x; nil = \x->x; .. switch函数用来模拟switch-case。 default这个特殊值用来标示缺省值, return函数负责把一个值最终返回出去。 这个语言本身比较简单,但是,它可以借用java的力量,因为任何的java对象都可以被用在script内部。而script的值也可以被java程序使用。 这样,通过java语言我们可以定义一个强大的外围库给script调用。 我本来是想加入case-of的支持的,但是现在考虑到用库可以实现,大概不用加了. haskell是个静态类型的语言.它的强大的静态类型,类型推导,泛形都非常优秀. 但是这样的牛刀对于一个脚本语言来说不免有些累赘. 而且, haskell对OO的支持有限, 即使是haskell的tuple也有些不方便.每个field的名字都是全局的, 而不象在OO语言中, 每个类有自己的名字空间. jaskell支持了更友好的field.每个tuple拥有自己的名字空间. 另外, jaskell还支持OO风格的this变量. 这可以用来实现OO中的override的功能. 举个例子: {val=1, use_static=val+1, use_dynamic=this.val+1}.{val=2} 头一对花括号内定义了一个tuple, 它的值是: {val=1, use_static=2, use_dynamic=2} 第二对花括号内部表示的是field update. jaskell的field update是functional的, 也就是说, update并不改变原来tuple的值, 而只是生成一个新的tuple. 这个生成的新tuple的值是: {val=2, use_static=2, use_dynamic=3} 这里, use_static的值没有变化, 因为我们并没有update这个field, 但是use_dynamic得值变了, 这是因为我们用了this.val, 这个this就表示延迟绑定, 实现了override. 回到定义switch-case的问题, 使用tuple, 可以定义一个更容易理解也更干净的switch-case: translate' 2 where switch' x = {when a b = if x==a then return b else this, default = return} where return x = {when _ _ = this, default _ = this, eval=x} .. ; translate' x = switch' x .when 1 "one" .when 2 "two" .when 3 "three" .default "unknown" .eval; .. 这个switch'和前面的switch不同, 它使用了一个tuple, 所以不需要定义一些特殊的标识函数. switch'有两个成员, when成员是一个函数, 它比较这个switch的值, 如果符合目标, 就调用return, 否则就返回自身. default成员也是一个函数, 它直接就调用return. return这个函数也返回一个tuple, 它的when和default成员忽略任何参数, 直接返回自身. 它的eval成员直接返回这个值. 呵呵, 完成了tuple, 下一步应该就是在java里面实现尽量丰富的外围库了. 从某种角度说, jaskell也是一个OO语言. 你可以通过where来达到encapsulation. 比如: {mtd1 = myfld+1, mtd2=myfld*5} where myfld=3 .. 这里面, mtd1, mtd2都是public method. 但是myfld是对外部隐藏的. 你还可以通过field update来达到继承, 比如: inherit {mtd="foo"} where inherit t = t.{newmtd="hello world"} .. 就可以通过inherit函数来达到继承的作用. 还可以通过this变量来达到重载, 这我在前面已经说明过了. 最后, OO中最基本的subsumption, 也就是说子类型可以当作父类型来用, 这在jaskell里面不是一个问题. 因为jaskell根本没有类型, use t1 + use t2 where t1 = {mtd1="hello"}; t2 = inherit t1; inherit t = t.{mtd1=t.mtd1+" "+mtd2, mtd2="world"}; use t = t.mtd1; .. 这段代码, use函数可以用在t1身上, 也可以用在t2上. 如此, 基本的OO特征都具备了. 除此之外, 它本身还是一个函数型的语言. 等我整理一下代码和javadoc, 就可以发布到sourceforge上了. 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2005-01-09
强。
正好我也可以学学 函数式编程,Haskell,jparsec。:-) google到一篇jparsec的文章。 http://www.allaboutprogram.com/blogs/index.php?skin=basic |
|
返回顶楼 | |
发表时间:2005-01-11
前面讲的主要是jaskell的来自母亲haskell方面的特性。下面说说来自父亲java方面的特性。
任何一个public java class都可以被import进jaskell。 这个class在jaskell代码内部被当作一个tuple使用,所有的public field, method, nested class都被当作这个tuple的一个成员。 除此之外,还有几个特殊成员,比如new用来调用构造函数,new'用来生成数组等等。 比如,在运行jaskell的时候,如果我这样写: String source = ...; Jaskell jaskell = new Jaskell(); .addJavaClass(String.class); .addJavaClass(java.util.Date.class);; jaskell.run(source);; 那么,在source里面的jaskell代码就可以这样做: String.toLowerCase $ Date.toString $ Date.new "1/1/2005" 这个$函数,用来改变优先级,否则jaskell会认为String.toLowerCase Date是一个函数了。这里本来也可以用括号, String.toLowerCase (Date.toString (Date.new "1/1/2005");); 但是有时候用$符号更简洁。 正在开发中的特性有: 1。对任何的java object, 都可以不用import class, 直接就把它当作tuple调用它的共有成员。如此, 可以不用Date.toString date,而是直接用更OO的语法date.toString。 2。throw-try-catch-finally功能。 3。foreach等命令式的控制。 4。借助动态代理,用tuple来实现java interface。 等等等等。 这些功能完成后,jaskell就可以很方便地处理java对象,和java自由交互,而不仅仅局限于函数计算和公式计算了。 |
|
返回顶楼 | |
发表时间:2005-01-19
第一版发布。
http://sourceforge.net/project/showfiles.php?group_id=122347&package_id=141190 支持functional和OO两种风格。 所有的java对象都可以直接使用。 java函数的调用方法如下: myobj.mymethod[1,2,"mystring"] 也就是用方括号, 而不是圆括号,来括住所有参数。 new一个对象这样调用: java.lang.XXXClass.new[] //这个调用缺省构造。 com.mycomp.MyClass.new[a,b,c] //这个调用其它的构造函数。 支持throw, try, catch, finally. 支持synchronized。 支持用jaskell的tuple来实现java接口。 支持副作用。 回头我会写出tutorial。 昨天俺的笔记本坏了, 这两天忙着重新安装环境。(幸好老夫cvs的即时,早两天坏,我可就哭都哭不回来了) |
|
返回顶楼 | |
发表时间:2005-01-23
tutorial写出来了。
在http://sourceforge.net/project/showfiles.php?group_id=122347&package_id=141190&release_id=297858 请大家提提意见。 |
|
返回顶楼 | |
发表时间:2005-01-25
引用 还需要一个新的script language吗,为什么不用groovy
好。问得好! 我想类似的问题应该还有 “为什么不用python",”为什么不用ruby“等。 groovy是一个非常优秀的scripting language。我看了它的一些介绍,下面说说我的理解。 1。groovy是一个面向java developer的script语言。它的目的是给java开发者一个方便强大的扩展。它的语法非常接近java. 而jaskell是可以面向不懂编程的操作人员的。一个只要有使用excel能力的人(当然是指excel的公式计算),就应该可以使用jaskell。 jaskell的一个首要目标是,可以让这些数据录入人员,business analyst, project manager等人, 不需要去正襟危坐地学习”编程“,”面向对象“,什么”类“呀,”方法“呀之类的。她们可以直接根据企业不断变化的业务需求,输入非常接近英语的业务逻辑。 比如,数据库里面有一些虚拟数据项,这些项的值由其它的一些数据项决定。但是,这个决定的规则由business用户在日常工作中改变和维护。 此时,可以让一个不懂java的维护人员花一个小时熟悉一下jaskell的基本语法,就可以写各种不同的逻辑规则了。 如: if north_america_sales + asian_sales > central_sales then north_america_sales - 100 else central_sales where north_america_sales = canada_sales+mexico_sales+united_states_sales .. 2. groovy仍然是一个命令式的面向对象的scripting language。这种命令式的语言在处理一些IO或者是效率要求较高的场合很有效。 但是,在处理一些纯粹的声明式逻辑的场合, 它没有基于函数式的语言直观。 而jaskell的core是个纯函数式的语言。没有副作用。一些如pattern match, functional update的函数式语言的功能也让它对一些特定的应用更简单。 3。我没有看到怎么在java中自定义类库给groovy使用。也许可以吧。这点我不清楚。 但是,jaskell语言保持了一个非常小的核心,大多数的功能都可以通过外围的java库来import到语言之中。 比如,jaskell连switch-case都不在语言中,而是是通过库定义的。其它的如for循环,foreach, try, catch, throw等等等等功能都是库定义的函数。 一个企业,完全可以自己用java或者jaskell定义一套符合自己业务要求的库,想多强大有多强大,然后给jaskell的使用者用就行了。 4。groovy有closure, 这可以用jaskell的lamda函数模拟。而jaskell的高阶函数,currying则不是那么容易用OO的语法模拟的。groovy的closure是一个特殊的语言元素。而jaskell中只有函数,什么都是函数,这样,语言简单很多。 5。jaskell中所有东西都是函数,而任何函数都是可以从jaskell输出到java中的。而我不清楚groovy能否把一个接受closure的函数输出到java中调用。 6。jaskell是call-by-name的,也就是lazy的。call-by-name能够产生一些非常有趣的结果,比如实现fixpoint之类的。当然,这种高级用法,普通的非程序员用户是不需要理解的。 |
|
返回顶楼 | |
发表时间:2005-02-03
ajoo老大,偶先来批几个吧:
1. 下载的2个package不知道2者之间有何关系,不管3721,都下载下来以后,才发现jaskell package里面已经包含了jparsec的源代码,只是少了一些sample而以,应该在release notes或者package notes里面说明一下,这样偶就可以只下载想要的东东了。 2. 后缀名不对,如果是jar的话,按照惯例,应该是适用于:java -jar xxx.jar,就能够运行的这种情况,不要把jar当作zip工具来使用。 3. jar里面包含了3个jar,doc, src和test,而test已经被包含在src里面,doc也只是javadoc,如果你想独立出来,应该分成3个文件并加以说明,这样偶也能够有选择的下载了。 4. ant脚本里面用到了lib/junit.jar,但是下载的package没有这个junit lib 5. 文档不应该用mht,这个是IE专用的格式,用别的浏览器就打不开了。可以考虑使用html或者txt文件。 hehe,还没有看代码,就先说这些偶在下载,编译中遇到的不便,等看了代码再说其他东东。 |
|
返回顶楼 | |
发表时间:2005-02-03
Readonly 写道 ajoo老大,偶先来批几个吧:
1. 下载的2个package不知道2者之间有何关系,不管3721,都下载下来以后,才发现jaskell package里面已经包含了jparsec的源代码,只是少了一些sample而以,应该在release notes或者package notes里面说明一下,这样偶就可以只下载想要的东东了。 多谢。我正在抓紧写tutorial。等tutorial写完,我会改进这个的。 Readonly 写道 2. 后缀名不对,如果是jar的话,按照惯例,应该是适用于:java -jar xxx.jar,就能够运行的这种情况,不要把jar当作zip工具来使用。 呵呵。就是觉得jar比较省事。否则用什么呢?gzip还不会使呢。 Readonly 写道 3. jar里面包含了3个jar,doc, src和test,而test已经被包含在src里面,doc也只是javadoc,如果你想独立出来,应该分成3个文件并加以说明,这样偶也能够有选择的下载了。 是有点乱。我也在考虑怎样打包最好。要是都分开,清楚是清楚,但是下载者要运行test还得把两个包都打开,好像有点麻烦。 Readonly 写道 4. ant脚本里面用到了lib/junit.jar,但是下载的package没有这个junit lib 这个。junit我可以随便加在我的包里吗? Readonly 写道 5. 文档不应该用mht,这个是IE专用的格式,用别的浏览器就打不开了。可以考虑使用html或者txt文件。 说的对。我会改成html的。主要是不大会用word,还以为只能存成mht呢。 总之,多谢啦! |
|
返回顶楼 | |
发表时间:2005-02-03
spring嘟嘟 写道 感觉可以用来切换权限模型的时候,来自定义权限模型
怎么说?没大明白你的意思。 |
|
返回顶楼 | |
发表时间:2005-02-03
ajoo 写道 呵呵。就是觉得jar比较省事。否则用什么呢?gzip还不会使呢。 改个后缀名就行了呀,jar jaskell.zip ,hehe junit的协议是CPL,可以加入release package,不过不加也可以,这个东东相信大家都有的,在文档里面说明一下,把junit.jar需要放到lib目录下面,ant脚本才能运行test的target。 至于jaskell的package,偶觉得只要一个就够了,在文档说明一下,要看javadoc的,运行ant的某个target,测试代码才一点点,包含在里面无所谓,顶多再发布一个bin的jar。 偶贴上一个附件,就是加了2个eclipse的工程文件,可以直接导入到eclipse看代码,这样方便其他人一些。 btw,语法看起来蛮有趣的,偶看看能作什么东东玩。 |
|
返回顶楼 | |