BT

您是否属于早期采用者或者创新人士?InfoQ正在努力为您设计更多新功能。了解更多

概览CLI之上的新语言——Boo

| 作者 Andy Glover 关注 0 他的粉丝 ,译者 陈黎夫 关注 0 他的粉丝 发布于 2007年4月19日. 估计阅读时间: 27 分钟 | ArchSummit社交架构图谱:Facebook、Snapchat、Tumblr等背后的核心技术

最近几年中,得益于Java和.NET平台良好的可扩展性,在这两个平台上都出现了一大批令人激动的新语言。在Java领域内,人们欣喜地看到了JRuby和Groovy这两门语言的出现,它们均在语法、动作(Action)方面提供了很高程度的灵活性。若合理使用的话,会大大提高开发者的生产效率。.NET平台上也确实出现了不少创造性的新语言,微软公司官方支持的IronPython和F#都让人们对CLI的灵活性和可扩展性充满了信心。

在IronPython和F#受到广泛关注和支持的同时,其他一些同样基于CLI的语言也在默默地争取着自己的生存空间。例如开源社区所推崇的L#(一门CLI上运行的基于Lisp的语言)、IronRuby和Ruby.NET等两个Ruby的实现等。

很多这类创新都是将现有的语言移植到CLI平台上(例如,IronPython就是CLI上的Python语言,就像JRuby是JVM上的Ruby语言一样),但也出现了一些全新的、拥有自己独特语法的语言,虽然这些全新的语言也难免受到目前如日中天的各种流行语言的影响。boo就是其中之一。boo是CLI平台上的一种静态类型的语言,其很多特性都受到了Python的影响,但却又不是Python的简单移植。实际上,boo并不在意代码的缩进,也不强迫我们使用self关键字。另外,boo从根本上来讲还是一种静态类型语言,这也与Python的动态特性不尽相同。

借助于boo所内建的语言特性以及简单的语法结构,加上其静态特性,我们可以用该语言更加高效地编写.NET应用程序,程序的执行效率甚至也能与C#不相上下。另外,在boo中,我们还可以使用任何CLI平台上的现存类库,boo代码同样能够容易地在其他CLI语言中被重用!

使用boo开发非常简单

Rodrigo B. de Oliveira曾经对C#的很多过于严格的编码规则(例如类型的强制转换)及其不能通过shell测试运行代码感到非常郁闷。于是boo语言应运而生,并很快发展成一个非常方便的.NET和Mono通用的平台,基于该平台,我们能够创建GUI原型、单元测试甚至游戏等各类程序。

学习boo的最简单方法就是从boo的交互shell开始,boo的交互shell又名booish。在booish中,我们即可灵活地任意察看代码片断,进而理解boo的语法规则。

对于最常见的hello world程序来讲,boo的视线非常简单——print加上将要输出的字符串即可。

>>> print "Hello Scary World!"
Hello Scary World!

需要说明的是,我们还可以将上述代码保存在代码文件中,然后使用booi命令执行该源文件。

C:\dev\projects\acme>booi src\helloworld.boo
Hello scary world!

我们还可以用booc命令将helloworld.boo脚本编译成为Windows可执行文件,即将boo代码编译成合法的CLI应用程序。

C:\dev\projects\acmi>booc -out:bin/helloworld.exe src/helloworld.boo
Boo Compiler version 0.7.6.2237 (CLR v1.1.4322.2032)
C:\dev\projects\acme\bin>helloworld.exe
Hello scary world!

boo是一种静态类型的语言,每个变量都有自己的类型。例如,我们可以创建一个值为“Hello World”的string类型变量:

>>> var = "Hello world"
'Hello world'
>>> var.GetType()
System.String

注意到这里并没有显式地将var声明为string类型,但由于boo的静态特性并根据该变量被设置的值,var就自动地被设置成了string类型。

boo字符串

与Python类似,boo为字符串、集合等类型添加了很多灵活的内建(built-in)功能支持,让我们举例说明。

boo支持字符串改写(使用${}语法),还支持Python风格的多行字符串。

>>> adj = "scary"
'scary'
>>> print "Hello ${adj} world"
Hello scary world

多行字符串非常易于使用,更不用担心字符转义等问题。

firstname = "Joe"
lastname = "Dirt"
accountid = "A499"
amount = "1.32"

msg = """



"""

与Python和Ruby类似,正则表达式和集合类型(例如list和map)的支持内建到了boo语言本身中,并提供了语法上的快捷使用方式。

正则表达式通过“/”定义,其实际的类型为System.Text.RegularExpressions.Regex。匹配则通过Perl风格的“=~”操作符完成。例如,如下代码即可用来匹配邮政编码信息:

>>> regx = /^\d{5}([-]\d{4})?$/
^\d{5}([-]\d{4})?$
>>> if '22101-4444' =~ regx:
print "yes"

boo的集合类型

boo支持三种内建的集合类型:普通数组(长度固定,且只能包含某一确定类型的数据)、列表(长度可变,能包含不同类型的数据)以及哈希表(用来存储名称/值对)。

数组

数组用来保存一系列相同类型的对象,不能超过某个预定的长度。在boo中的创建语法如下:

>>> myarray = (1,2,4,5)
(1, 2, 4, 5)

我们也不能在数组中添加不同类型的数据。

>>> myarray.SetValue(6, 3)
>>> myarray
(1, 2, 4, 6)
>>> myarray.SetValue('test', 3)
System.InvalidCastException: Object cannot be stored in an array of this type.
at System.Array.InternalSetValue(Object value, Int32 index1, Int32 index2, Int32 index3)
at Input50Module.Main(String[] argv)

列表

列表是一类长度不固定的、可被索引且能够包含多种不同类型的数组。这是一种非常灵活的类型,通过从Python中借来的方括号([])创建,属于Boo.Lang.List类型。

>>> mylist = [1, 2, 3, 4]
[1, 2, 3, 4]
>>> mylist.GetType()
Boo.Lang.List
>>> mylist.Add('test')
[1, 2, 3, 4, 'test']

可以看到,boo中的列表能够包含不同类型的对象,还能够使用Add方法添加新元素。若想了解列表目前包含了什么,我们可以使用Contains方法,甚至还能够通过询问逻辑问题实现:

>>> mylist.Contains(3)
true
>>> 'test' in mylist
true

哈希表

boo中的哈希表是一个保存名称/值对的容器。这些名称/值对均可为不同的类型。

>>> myhash = {".NET":"boo", "Java":"Groovy"}
{'Java': 'Groovy', '.NET': 'boo'}

哈希表通过名称/值对实现,因此,若是输入了名称,那么将得到其值。在下面的代码中,我们输入了".NET",得到了'boo'。

>>> myhash[".NET"]
'boo'

我们还可以使用ContainsKey或ContainsValue方法搜索哈希表。

>>> myhash.ContainsKey('Java')
true
>>> myhash.ContainsValue('Groovy')
true

迭代

与Python和Ruby类似,boo也允许我们容易地对集合类型进行迭代。不过boo不支持Ruby或Groovy中常见的块(block)和闭包(closure)。

列表的迭代

通常情况下,boo中的迭代是使用for语句实现的:

>>> mylist
[1, 2, 3, 4, 'test']
>>> for value in mylist:
... print value
...
1
2
3
4
test
>>> myint
9
>>> myarray
(1, 2, 4, 6)
>>> for i in myarray:
... print 2*i
...
2
4
8
12

哈希表的迭代

我们也可以在哈希表上进行迭代:

>>> myhash
{'Java': 'Groovy', '.NET': 'boo'}
>>> for key in myhash:
... print key.Value
...
Groovy
boo

需要注意的是,因为boo的哈希条目存贮在System.Collections.DictionaryEntry类型中,所以我们可以根据需要访问其Key和Value成员。

>>> for key in myhash:
... print key.Key
...
Java
.NET

boo中的函数

boo允许我们在类定义之外创建函数,这一点非常类似于Python和Groovy。在boo中,函数是“一等公民”(即函数本身也是对象),使用def关键字创建。

例如,如下代码定义了一个值为'boo'的字符串。需要注意的是对于字符串类型,boo中没有Python那样内建的lower方法。但创建一个lower方法却相当的简单:使用def关键字定义,并通过“as ”语句指定参数类型即可。

>>> str  = "Boo"
'Boo'
>>> str.GetType()
>>> lower(str)
----^
ERROR: Unknown identifier: 'lower'.
>>> def lower(val as string):
... return val.ToLower()
...
>>> print lower(str)
boo

boo中的IO

通过使用using关键字,boo让文件操作变得非常简单。我们根本不用担心文件处理中的一些常见问题,例如关闭文件等,因为using语句将自动为我们做好这些。

例如,读写某个本地文件将非常容易:

>>> import System.IO
>>> myfile = "SampleFile.txt"
'SampleFile.txt'
>>> using input = File.OpenText(myfile):
... for line in input:
... print line
...
Welcome to an easy
way to read files
using the most unscary language around:
BOO!

通过使用函数,我们可以重新创建Groovy中getText风格的方法:

>>> import System.IO
>>> def GetText(file as string):
... retString = " "
... using input = File.OpenText(file):
... for line in input:
... retString += line
... return retString
...
>>> myfile = "SampleFile.txt"
'SampleFile.txt'
>>> assert GetText(myfile).Equals('Welcome to an easy way to read files using the most unscary language around: BOO! ')
>>>

与NAnt集成

通过boo任务,boo能够与NAnt编译文件协同工作。下面这个例子就演示了boo与NAnt协同工作能够完成的任务——在某个dll上应用FxCop,然后用boo将其中Critical Errors的个数统计出来:

   value="${build.dir}\bin\${build.config}\fxcop.xml"/>










import System.IO
fpath = Project.Properties['fxcop.xml']
numerror = 0
using input = File.OpenText(fpath):
for line in input:
if line =~ /Level="CriticalError"/:
numerror++

print("There were ${numerror} Critical Errors")


在boo中单元测试非常简单

因为boo能够与其他CLI库共同使用,所以boo代码也能够容易地使用NUnit的属性。

例如,如下代码使用了用来测试数据库的NDbUnit,并用NUnit创建了一个简单的测试用例。在该测试用例中,我们使用NDbUnit的API插入一些数据,然后确认这些数据确实保存到了数据库中。

可以看到,与C#相比,boo代码显得简洁许多。因为boo并不使用分号和大括号,且类型声明也少了很多。

import NUnit.Framework
import NDbUnit.Core.OleDb
import NDbUnit.Core
import System.Data
import System
import System.Data.OleDb

[TestFixture]
class WordTest:

final CONN = "Provider=SQLOLEDB...."
final SCHEMA = "Dataset2.xsd"
final XML = "XMLFile2.xml"
fixture as OleDbUnitTest

[SetUp]
def configure():
fixture = OleDbUnitTest(CONN)
fixture.ReadXmlSchema(SCHEMA)
fixture.ReadXml(XML)

[Test]
def VerifyWordTableOle():

fixture.PerformDbOperation(DbOperationFlag.CleanInsert)
select = "select spelling from word where word.word_id = 2"

adapter = OleDbDataAdapter(select , CONN)

dta = DataSet()
adapter.Fill(dta, "word")
table = dta.Tables["word"]

for row as DataRow in table.Rows:
Assert.AreEqual("pugnacious", row[0],
"word spelling wasn't pugnacious")

若你想以最快的速度开发单元测试,那么boo将是一个明智的选择。因为Boo支持属性(IronPython并不支持),因此与NUnit配合起来也没有任何问题。

静态却不失动态特性

虽然我们并不需要显式声明变量的类型,但boo却的的确确是一种静态类型的语言。boo在底层根据变量的值来自动决定变量的类型。例如,如下一段脚本创建了一个string类型,并尝试调用一个并不存在的方法。

var = "BOO"
var.lower()

尝试编译该脚本,将得到如下错误:

C:\dev\projects\acme>booc -out:bin/statictypes.exe src/statictypes.boo
Boo Compiler version 0.7.6.2237 (CLR v1.1.4322.2032)
src/ statictypes.boo(3,5): BCE0019: 'lower' is not a member of 'string'.
1 error(s).

若是直接运行该脚本,也将得到同样的错误:

C:\dev\projects\acme>booi src\statictypes.boo
src\statictypes.boo(3,5): BCE0019: Boo.Lang.Compiler.CompilerError: 'lower' is not a member of 'string'.

尽管如此,boo在类型方面仍提供了很多便利。通过使用duck类型,我们可以推迟编译期类型检查。若某个变量的类型为duck,boo将尝试用反射的方式调用方法。例如,若我们将var声明为duck类型,那么将不会得到编译期错误。而若是直接运行这段脚本,将得到一个不同的错误:

C:\dev\projects\acme>booi src\statictypes.boo
System.MissingMethodException: Method System.String.lower not found.
at System.RuntimeType.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Obje
ct[] args, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParameters)
at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target, Object[] ar
gs)
at Boo.Lang.Runtime.RuntimeServices.Invoke(Object target, String name, Object[] args)
at StatictypesModule.Main(String[] argv)

借助于duck类型,boo能够以非常优雅的方式编写测试程序并控制Internet Explorer:

>>> ie as duck =
System.Type.GetTypeFromProgID("InternetExplorer.Application")()
System.__ComObject
>>> ie.Visible = true
true
>>> ie.Navigate2("http://www.thediscoblog.com")
>>> links = ie.Document.getElementsByTagName("a")
System.__ComObject
>>> site = "http://thediscoblog.com/2007/02/04/currying-maximum-favor-with-groovy/"
'http://thediscoblog.com/2007/02/04/currying-maximum-favor-with-groovy/'
>>> for link in links:
... if(link.href.Equals(site)):
... link.click()
...
mshtml.HTMLAnchorElementClass

注意到变量ie的类型为duck,这样即可更加优雅地将执行消息传递给该实例,而并不会导致任何错误。

找到页面上的某个链接之后,我们可以点击该链接,然后精确验证随后页面中的内容。

>>> h3s = ie.Document.getElementsByTagName("h3")
System.__ComObject
>>> for h3 in h3s:
... if(h3.id.Equals("comments")):
... assert h3.innerText =~ "5 Responses"
...
mshtml.HTMLHeaderElementClass
>>> ie.Quit()

使用boo进行开发将显得非常自然

得益于其宽松的语法规则,boo允许我们以更加轻松快捷的方式完成.NET平台上的任务。若你正在设计程序的原型,或是创建用户界面元素,那么boo将是个绝佳的选择。不仅如此,使用boo创建的所有程序或组件均可以无缝地与其他.NET类库配合使用,无论这些组件是用C#、VB.NET还是托管C++编写的。boo是一种.NET平台上非常友好的语言,让我们能够不受约束地快速编写代码。还躲什么呢——今天就来试试boo吧!


译者简介:陈黎夫(Dflying Chen)是InfoQ中文站的志愿者翻译。他毕业于上海交通大学计算机科学专业,曾在微软公司ASP.NET Ajax创始团队——Windows Live Hotmail担任软件开发工程师,使用ASP.NET Ajax早期版本参与开发了下一代Email系统Windows Live Mail,以及Windows Live Calendar等产品。擅长Web相关技术。作为ASP.NET Ajax在中国的传道者之一,他在个人博客中写过大量相关技术文章,引起了广泛反响,已经成为国内访问量最大的ASP.NET Ajax资源之一。著作/译作有《ASP.NET Ajax程序设计》、《Atlas基础教程》和《CSS禅意花园》等。加入InfoQ中文站志愿者翻译队伍,请邮件至china-editorial@infoq.com

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

这个项目是不是死掉了? by lu wenhua

去boo的主页上看了一下,很长时间没更新了。

Re: 这个项目是不是死掉了? by one kknd

活的挺好的,前两天才出0.7.8版

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

2 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT