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