1. 程式人生 > >一種語言 適合任何時候使用 — Haxe特性雜談

一種語言 適合任何時候使用 — Haxe特性雜談

               

這個世界有很多的語言, 不同的語言適合不同的情境, Ruby因為很適合開發領域語言(DSL), 所以被大衛選擇用於開發Rails, JavaScript因為常用非同步模型和方便的回撥處理, 被Ryan Dah用於開發node, 而JAVA期望通過JVM一次編寫, 隨處執行. 各種語言因為不同的目標場景, 也使得語言本身的特性趨於目標場景, 決定了這些語言應該是什麼樣子, 比如C++因為效率的考慮和相容C的需求, 決定了C++之所以成為現在的C++.同時也有很多語言是為生成另外一種語言而生的, 最典型的就是CoffeeScript, 我知道我的語言要更加優美, 但是我也知道我無法徹底替代你, 這是這個世界中習慣和慣性帶來的悲哀, 所以, 不能替代你, 那我首先通過生成你來取代你. 這就是Coffee這樣語言的思路. 因為JavaScript是如此的不好用和難以簡單替代, 所以成為了這類語言的競技場.但是, Haxe就更加誇張了, 他也是一種通過生成其他語言而生的語言, 只不過, 他可以生成多達7種不同的語言.

Haxe簡單介紹

One language, everywhere.– Haxe’s slogan

Haxe可能是我學習的最冷門的語言了, 不過因為Haxe的特性, 他再冷門, 我在實際中用到他的可能性都應該比Lisp要強的多. 我可以這麼說, Haxe可能是世界上最有野心的語言, 並且它還真的部分做到了. 其實Haxe與我還真有些淵源, 因為我以前的leader出來創業, 最後選擇的就是Haxe. 並且對它的適用性和成熟程度都很認可, 甚至還向我推薦過.Haxe起源於flash社群, 號稱’multiplatform language’, 據說是一個喜歡OCaml但是又知道OCaml實在無法推廣的法國人Nicolas Cannasse開發的, 可以生成的語言如下: (具體的見

Haxe的intro)

  • JavaScript: 直接生成單個的.js檔案, 支援DOM API.
  • Flash: 直接可以生成swf, 相容Flash Players 6到11.
  • NekoVM: 可以直接編譯成NekoVM的位元組碼. NekoVM本來就挺冷門的.
  • PHP: 很難想象哪個真的Phper會準備用Haxe替代掉自己的語言.
  • C++: 生成原始碼並且包含Makefiles, 另外, 提到了一個名為NME的2D跨平臺遊戲庫. 簡單的看了一下, 從特性上看, 似乎是除了Cocos2D-X以外一個較為不錯的選擇, 而從一些介紹看, 這個庫本似乎是一個模仿Flash的庫, 因為使用了Haxe這個神奇的語言, 所以現在已經支援了從iOS到Android到Windows等眾多平臺了.
  • C#和JAVA: 2.1以後開始支援, 但是還在實驗中, 官方稱在3.0時才算成熟.

因為能生成這麼多語言, 所以Haxe可以支援一些我們經常用到的框架和庫, 比如NodeJS, 比如直接生成PHP程式碼結合Apache做後臺, 比如生成C++程式碼以直接支援跨平臺的遊戲開發.以上的事實已經足夠讓我震驚了, 更逆天的是Haxe竟然還有自己的著色器語言解決方法, hxsl, 什麼叫征服世界, 那就是不僅要征服CPU上執行的語言, 還要征服GPU上執行的語言~~~總的來說, Haxe就是希望自己能幹任何事情, 而按它的理念, 它還真的有可能有一天能做到, 語言的世界很殘酷, 各種語言都有他最適合的情景, 沒有一種語言能統一世界, 但是假如真有的話, Haxe一定是最可能的那一個. 事實上, 我說Haxe的野心大並不是貶義, 這是事實上Nicolas腦海中存在的概念, 他自己就做過一個講座, 名字就叫Plans for World Domination(youtube視訊 — 請自備梯子), Haxe就是他佔領世界的計劃~~

在官方的Why user Haxe文件中提到了用Haxe的以下理由:

  • 適用於客戶端, 服務端和桌面程式開發的ECMA風格的程式語言.
  • 極為快速的編譯(這似乎不是理由, 因為不用Haxe都不用多出這道編譯流程, 更何況, 我實際感受是其實也沒有那麼快, 特別是編譯成C++的時候)
  • 型別檢測的好處(事實上喜歡動態語言的人就是因為喜歡沒有型別檢測帶來的自由)
  • 為你的目標平臺增加需要的語言特性.
  • 開源

其中提到的乾貨不多, 其實我覺得支援編譯到多種語言, 所以支援多種平臺, 使得只需要學習一種語言就能適用於幾乎任何環境, 這就是最大並且足夠強大的理由了.

環境

Haxe 2.1, 我會通過生成C++程式碼和JavaScript程式碼來觀察結果. 其中JavaScript我用的不是HTML, 而是我可能稍微熟悉一點的Node, 所以還用了Hx-node這個haxe對nodejs的支援庫.另外, 有Haxe的REPL環境ihx, 和官方提供的在網頁上嘗試Haxe的http://try.haxe.org/

生成C++初體驗

從簡單的HelloWorld開始吧:HelloWorld.hx:

class HelloWorld {  public static function main() {    trace("Hello World!");  }}

從例子中能夠看到, 原來所謂的ECMA風格就是類似C#和JAVA的風格…我用來編譯成cpp的.hxml配置是cpp.hxml:

-main HelloWorld-cpp cpp-debug-D HXCPP_M64

通過haxe cpp.hxml命令給我的生成結果是在cpp目錄下的一堆檔案, 其中cpp/src目錄下看到有100多行C++程式碼, 我甚至都不想現在就全都弄懂. 不過還好的是給我的執行檔案的確輸出了HelloWorld.

生成JavaScript With nodejs

首先需要通過haxelib install nodejs命令來安裝haxe的nodejs庫. 然後通過以下配置(我命名為nodejs.hxml)編譯上例中的HelloWorld.hx生成想要的.js檔案.

-main HelloWorld-js helloworld.js-debug-lib nodejs

通過haxe nodejs.hxml命令會生成兩個檔案, 一個是helloworld.js, 一個是helloworld.js.map(用於debug), 此時通過node helloworld.js能看到輸出結果正常. 當然, 首先你得把node安裝好.

Haxe入門

變數和型別

支援以下型別:

  • Integer(int): 整數型別, 不支援更進一步詳細的整數型別,比如short, long啥的, 類似動態語言的做法.
  • Float: 浮點數, 也是一個浮點走天下.
  • Boolean(Bool): 布林, 代表的是true和false.
  • String: 字串, 不解釋.
  • Void: 用於表示沒有型別和值.
  • Dynamic: 類似C#中的dynamic型別, 意圖在靜態語言中加入動態的效果, 個人很欣賞的嘗試.
  • Unknown: 類似javascript中的undefined.

另外, 還有null關鍵字, 型別是Unknown<0>.

定義一個變數的語法和UnityScript(Unity中的JavaScript)類似(或者說JScript), 我甚至懷疑javascript2將來就會是這個樣子.

var i : Int = 5;

不過還是有個區別, 那就是當同樣使用簡化的語法時:

var i = 5;

Haxe使用的是類似C++和C#的型別推導技術, 也就是說型別會在編譯器決定, 不影響執行效率, 而UnityScript則不一樣, 會影響執行效率. 就這點來說, Haxe是足夠先進的.同時, 作為現代語言, String的連線操作符是沒啥問題的.

class HelloWorld {  public static function main() {    var hello = "Hello,";    var world = "World!";    trace(hello + world);  }}

自從發現javascript和C#的扭曲特性後, 我總是喜歡用1 + "1"的操作來實驗一個語言, 很不幸的是, Haxe就是這樣中招的語言, 會無錯誤的輸出11.

函式

用的還是類似UnityScript的方式, 使用function關鍵字, 同時用str:String這樣的方式來表示引數型別, 通過把:Int加到函式定義語句的結尾來表示返回值, 熟悉UnityScript的XD是無障礙了.

class HelloWorld {  static function add(n1 : Int, n2 : Int) : Int {    return n1 + n2;  }  public static function main() {    trace( add(3,4) );  }}

操作符

沒有看到操作符過載的內容, 但是支援常見操作符, 包括>>>這個javascript特有的和C系的++--等.

流程控制

基本上該有的都有, while, do-while, for-in, switch. 不過也沒有啥亮點. 有些詭異的是去掉了常規的for, 即for(int i=0; i<length; ++i)形式的for語句. 不知道是為什麼. 倒是避免了javascript中for塊變數作用域的問題.稍微值得一提的是switch的自動break, case支援常量字串和用逗號分隔的多路匹配, 現在的語言一般都是從C那兒來的, C特別彆扭的switch也就成為了各大語言改進的物件, 有意思的是, 改進的效果都不一樣, Haxe就是這樣改的:

class HelloWorld {  public static function main() {    var choice : String = "";    while (true) {      choice = Sys.stdin().readLine();      switch( choice ) {        case "y", "Y":          Sys.println("You surely want it!");        case "n", "N":          Sys.println("you don't want it?!");          break;        default:          Sys.println("Not a valid choice.");      }    }  }}

我沒有在case "y"後面使用break, 但是不會執行到下面的case, 而事實上, 後面的那個break是用來從while死迴圈中退出的.上面的程式碼沒法在js下執行, 因為讀了命令列, 我們得使用nodejs中讀命令列的process模組, 上面的程式碼經過處理就成了下面這樣了:

import js.Node;class HelloWorld {  public static function main() {    Node.process.stdin.resume();    Node.process.stdin.setEncoding('utf8');    Node.process.stdin.on('data', function (chunk) {      var choice : String = chunk.trim();      switch( choice ) {        case "y", "Y":          Node.console.log("You surely want it!");        case "n", "N":          Node.console.log("you don't want it?!");        default:          Node.console.log("Not a valid choice.");      }    });  }}

首先, 因為沒有了while死迴圈, 所以可能得自己加個條件作為退出(這裡沒有), 其次, 為了使用js的模組, 可能得稍微費點周折.

  1. 在預先設為Haxe lib的地址git clone https://github.com/cloudshift/hx-node.git nodejs
  2. 修改nodejs.hxml, 新增一個-cp YOUR_PATH. 比如我的地址是~haxe_libs, 於是就是-cp ~/haxe_libs/nodejs.

作用域

類作用域和函式作用域不用說了, 好訊息是Haxe有塊作用域, 也就是說, {}範圍內定義的變數只在大括號範圍內有效, 這個很有很有用. 假如不能這樣的話, 需要用javascript扭曲的匿名函式hack, 太扭曲了.

Haxe特性

因為Haxe是如此的冷門, 所以明明本文是講Haxe特性的, 結果也拉入了一堆簡介. 好了, 以下是正題. 我選擇的特性列表直接來自於官方列出的列表Language Features, 沒有什麼比這個更有代表性了.

基於模版(class-based)的類和介面模型(類似JAVA)

Classic Object-Oriented class + interface model (similar to Java)

  1. 類的訪問限制只有public和private兩層, 相對一些語言(比如JAVA)還在C++經典的三許可權中去增加一個預設的package不同, Haxe甚至去掉了通常的private, 而把protected稱為private, 並且預設狀態就是private.