在.NET平臺(tái)上使用Scala語言(下):分析
上一篇文章里我們簡單嘗試了在Scala里編寫.NET應(yīng)用程序。這個(gè)過程并不困難,因?yàn)樗坪鮏cala官方已經(jīng)對(duì)此已經(jīng)有較好的支持了。我們要做的只是“獲取工具”,“編譯成IL”,最后再“生成程序集”即可。那么,這些工具究竟做了些什么,Scala究竟又是如何支持.NET平臺(tái)的,它的可用性究竟如何,我們還需要進(jìn)一步的分析及嘗試。
現(xiàn)在看第一個(gè)問題。我們知道從Scala源代碼生成IL文件的腳本是scalac-net.bat。如果需要了解它做的事情,最直接的方法莫過于查看其中的內(nèi)容。如果要看明白它的代碼,可能需要我們對(duì)cmd命令有些了解——不過我也只是略知一二罷了,如果您對(duì)其了解不多其實(shí)也沒有太大關(guān)系。經(jīng)過合理推測,我們知道scalac-net.bat本身不會(huì)有什么功能,它只是調(diào)用編譯器而已。因此,這個(gè)腳本文件的職責(zé),無非是收集參數(shù)并執(zhí)行編譯器。于是我們打開scalac-net.bat,在眾多for/if之中可以發(fā)現(xiàn)它最后執(zhí)行了這樣一個(gè)命令:
- %_JAVACMD% -Xbootclasspath/a:"%_BOOT_CLASSPATH%" %_JAVA_OPTS% %_PROPS% -cp "%_EXTENSION_CLASSPATH%" scala.tools.nsc.Main -target:msil %_ARGS%
那么我們?cè)僬{(diào)用scalac-net.bat的時(shí)候這行命令究竟是什么呢?對(duì)于此類問題,我們可以再它前面加上ECHO命令,即:
- ECHO %_JAVACMD% -Xbootclasspath/a:"%_BOOT_CLASSPATH%" %_JAVA_OPTS% %_PROPS% -cp "%_EXTENSION_CLASSPATH%" scala.tools.nsc.Main -target:msil %_ARGS%
ECHO可以視為cmd的print命令,我們可以用它來觀察和學(xué)習(xí)腳本。再次運(yùn)行,便可以看到編譯器的調(diào)用方式了:
- D:\scala-2.7.7.final\code> ..\bin\scalac-net.bat test.scala
- java -Xbootclasspath/a:"D:\SCALA-~1.FIN\bin\..\lib\scala-library.jar" -Xmx256M -Xms16M -Dscala.home="D:\SCALA-~1.FIN\bin\.." -Denv.classpath="" -Dmsil.libpath="D:\SCALA-~1.FIN\bin\..\lib\predef.dll;D:\SCALA-~1.FIN\bin\..\lib\scalaruntime.dll;D:\SCALA-~1.FIN\bin\..\lib\mscorlib.dll" -Dmsil.ilasm="c:\Windows\Microsoft.NET\Framework\v2.0.50727\ilasm.exe" -cp "D:\SCALA-~1.FIN\bin\..\lib\mscorlib.dll;D:\SCALA-~1.FIN\bin\..\lib\predef.dll;D:\SCALA-~1.FIN\bin\..\lib\sbaz-tests.jar;D:\SCALA-~1.FIN\bin\..\lib\sbaz.jar;D:\SCALA-~1.FIN\bin\..\lib\scala-compiler.jar;D:\SCALA-~1.FIN\bin\..\lib\scala-dbc.jar;D:\SCALA-~1.FIN\bin\..\lib\scala-library.jar;D:\SCALA-~1.FIN\bin\..\lib\scala-swing.jar;D:\SCALA-~1.FIN\bin\..\lib\scalaruntime.dll" scala.tools.nsc.Main -target:msil test.scala
可以看出,這是在運(yùn)行一個(gè)java程序,并且提供了很多參數(shù)。不過參數(shù)很多,內(nèi)容也很亂。不過亂的原因在于其中對(duì)于各式命令或者庫文件的引用都使用的完整路徑。經(jīng)過換行,相對(duì)路徑調(diào)整,并去除一些明顯無用的參數(shù)內(nèi)容(如-cp,即classpath里的dll文件),我們發(fā)現(xiàn)其實(shí)大約這樣的:
- D:\scala-2.7.7.final\code> ..\bin\scalac-net.bat test.scala
- java
- -Xbootclasspath/a:"..\lib\scala-library.jar"
- -Xmx256M
- -Xms16M
- -Dscala.home=".."
- -Denv.classpath=""
- -Dmsil.libpath="..\lib\predef.dll;..\lib\scalaruntime.dll;..\lib\mscorlib.dll"
- -Dmsil.ilasm="c:\Windows\Microsoft.NET\Framework\v2.0.50727\ilasm.exe"
- -cp "..\lib\sbaz-tests.jar;..\lib\sbaz.jar;..\lib\scala-compiler.jar;..\lib\scala-dbc.jar;..\lib\scala-library.jar;..\lib\scala-swing.jar;"
- scala.tools.nsc.Main
- -target:msil
- test.scala
您可以執(zhí)行整理后的命令,效果一致。經(jīng)過一番摸索,再配合scalac.bat -help的輸出,我們可以觀察出命令的具體意義,例如:
- Scala編譯器其實(shí)是一個(gè)Java程序,入口是scala.tools.nsc.Main
- -Dmsil.libpath表明編譯時(shí)所引用的.NET程序集。
- -Dmsil.ilasm表明ilasm.exe文件的路徑,如果需要直接生成程序集則需要進(jìn)行指定。
那么假設(shè)我們已經(jīng)編譯生成了一個(gè)test.exe文件,現(xiàn)在使用.NET Reflector來觀察它的信息:
可見test.exe依賴另外三個(gè)程序集,它們按照依賴關(guān)系分別是:
- mscorlib.dll:定義了一個(gè)程序的基礎(chǔ)需求。
- scalaruntime.dll:依賴mscorlib.dll,定義了Scala語言中的各種基礎(chǔ)類型。
- predef.dll:依賴mscorlib.dll及scalaruntime.dll,定義了scala的基礎(chǔ)類庫。
看上去并沒有什么問題,不是嗎?但是,經(jīng)過簡單的思考,似乎又不是那么一回事情。好比,您是否覺得一個(gè)Scala程序的依賴實(shí)在少了一些?例如您平時(shí)寫程序時(shí)能否僅僅依賴mscorlib.dll,而不使用System.dll或System.Core.dll等其他程序集?那么,為什么Scala便可以僅僅基于mscorlib.dll而構(gòu)建predef.dll呢?為此,我們簡單比較一下predef.dll與Java平臺(tái)上Scala的標(biāo)準(zhǔn)庫——scala-library.jar。首先是predef.dll:
其次是scala-library.jar中的定義:
可以看出,Scala標(biāo)準(zhǔn)庫中定義了比predef.dll中更多的類庫。例如Scala一直引以為傲的Actor類庫,即scala.actors命名空間。換句話說,.NET平臺(tái)上的Scala并不支持Java平臺(tái)上的許多高級(jí)功能——這樣似乎可以理解為什么它只需依賴mscorlib.dll就足夠了。不過“標(biāo)準(zhǔn)類庫少”是壞事還是好事倒也不能輕易下結(jié)論。
如果說這是壞事——類庫少自然是壞事。那么“好事”又從何談起呢?我的理解是:Scala畢竟是為Java平臺(tái)設(shè)計(jì)的語言,它本可不必對(duì).NET提供支持。也就是說,.NET平臺(tái)只是Scala的“副業(yè)”。如果說,因?yàn)镮L和Java Code相近(或者說有很大程度的“包含”關(guān)系),那么編譯器在寫起來相對(duì)問題不大,但“類庫”就無法討巧了。如果.NET類庫跟得太緊,那么我反而要懷疑它的質(zhì)量是否成熟。在使用Scala時(shí),我主要關(guān)注的其實(shí)是“編譯器”及最終生成的IL,我并沒有期望能夠使用Scala在.NET平臺(tái)上編寫程序。對(duì)此,編譯器是否成熟對(duì)我們來說可能更加重要。因此,.NET類庫少也并不是壞事——畢竟.NET Framework已經(jīng)提供了足夠的功能,不是嗎?
是嗎?
如果您比較心細(xì),您應(yīng)該已經(jīng)從第一幅圖中看出問題來了。我特意將焦點(diǎn)放在mscorlib.dll上,目的便是展示它的版本信息,即:
- mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
如果您關(guān)注一下平時(shí)寫程序時(shí)所使用的mscorlib.dll,會(huì)發(fā)現(xiàn)它是這樣的:
- mscorlib, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
為什么會(huì)不一樣?那是因?yàn)镾cala所使用的mscorlib.dll是“自己帶來的”,并不是系統(tǒng)安裝的.NET Framework。那么它究竟是什么呢?展開后便可一目了然:
因?yàn)樗⒉皇俏④浱峁┑?NET Framework,而是Mono平臺(tái)提供的.NET類庫!如果您使用.NET Reflector來查看其中某些類庫的具體實(shí)現(xiàn),會(huì)發(fā)現(xiàn)它和.NET Framework中實(shí)現(xiàn)有很明顯的不同(如字符串的連接操作)。
有朋友可能會(huì)想,這問題應(yīng)該不大,只要在編譯時(shí)提供機(jī)器上安裝的程序集不就可以了嗎?但問題是,微軟發(fā)布的.NET Framework,他們都是依賴于mscorlib.dll——這是每個(gè).NET程序的核心,例如其中定義了一些基礎(chǔ)數(shù)據(jù)類型。想象一下,Scala編譯器使用的是Mono里定義的String類型,那么如何把它傳遞給MS .NET里定義的方法呢?要知道后者使用的可是MS .NET里的String!
經(jīng)過多番嘗試,我無法讓Scala編譯器使用MS .NET里的程序集——即便是再簡單的case。當(dāng)然,目前我還無法確定這是Scala編譯器的問題,亦或的確只是類庫的關(guān)系。不知道修改一下Scala的編譯器或是基于Mono進(jìn)行編譯能否成功,我會(huì)再進(jìn)行更多嘗試——如果某一天您發(fā)現(xiàn)我又寫了一篇“下”,而現(xiàn)在這篇變成了“中”……也是非常正常的事情。:)
原文鏈接:http://www.cnblogs.com/JeffreyZhao/archive/2009/12/21/scala-for-dotnet-2-analysis.html
【編輯推薦】