Haxe 2 から 3 へ
Haxe のバージョンを 2.10 から 3.0.0 に更新した。
Haxe 2 の時のコードが結構動かなくなったので、変更点をメモ。
interface 継承の変更
implements -> extends
- haxe 2
interface Hoge {} interface Piyo implements Hoge {}
- haxe 3
interface Hoge {} interface Piyo extends Hoge {}
複数 interface を実装する場合の指定の変更
implements A, implements B -> implements A implements B
- haxe 2
interface A {} interface B {} class C implements A, implements B {}
- haxe 3
interface A {} interface B {} class C implements A implements B {}
構造的部分型の配列を作る際の初期化子の型が厳密化
構造体と型 で、「えー」っていってたのができなくなった。
- haxe 2
using Lambda; typedef X = { id: Int, name: String, msg: String, hoge: Int } class Hoge { public static function hoge(x: List<X>) {} public static function main() { hoge([{ name: "hoge" }, { msg: "hoge" }].list()); } }
- haxe 3
using Lambda; typedef X = { id: Int, name: String, msg: String, hoge: Int } class Hoge { public static function hoge(x: List<X>) {} public static function main() { hoge([ { id: null, name: "hoge", msg: null, hoge: null }, { id: null, name: null, msg: "hoge", hoge: null }, ].list()); } }
ヘテロな配列を作る際に、Array の指定が必須
- haxe 2
class Hoge { public static function main() { var as = [{ name: "hoge" }, { msg: "hoge" }]; trace(as); } }
- haxe 3
class Hoge { public static function main() { var as: Array<Dynamic> = [{ name: "hoge" }, { msg: "hoge" }]; trace(as); } }
Std.format 廃止
Std.format("~") -> '~'
- haxe 2
class Hoge { public static function main() { var x = 10; var str = Std.format("${x}"); trace(str); } }
- haxe 3
class Hoge { public static function main() { var x = 10; var str = '${x}'; trace(str); } }
window などが、js.Lib -> js.Browser に移動
- haxe 2
class Hoge { public static function main() { var url = js.Lib.window.location.href; trace(url); } }
- haxe 3
class Hoge { public static function main() { var url = js.Browser.window.location.href; trace(url); } }
Array の first メソッド等廃止
- haxe 2
class Hoge { public static function main() { var xs = [1, 2, 3]; var x = xs.first(); trace(x); } }
- haxe 3
class Hoge { public static function main() { var xs = [1, 2, 3]; var x = xs[0]; trace(x); } }
Void 型の扱いが変更
- haxe 2
using Lambda; class Hoge { public static function main() { var f: Void -> Int = function(x) { return 1; }; var xs: List<Void> = [null, null].list(); var ys = xs.map(f); trace(ys); } }
- haxe 3
class Hoge { public static function main() { var f: Void -> Int = function() { return 1; }; var xs: List<Void> = [null, null].list(); //var ys = xs.map(f); // error: Void -> Int should be (Void) -> Unknown<0> //trace(ys); } }
関数型内の Dynamic で Void が受け取れなくなった
- haxe 2
using Lambda; class Hoge { public static function hoge(x: List<Dynamic>) {} public static function piyo(f: Int -> Dynamic) {} public static function main() { var xs: List<Void> = [null, null].list(); hoge(xs); var f: Int -> Void = function(x) {}; piyo(f); } }
- haxe 3
using Lambda; class Hoge { public static function hoge(x: List<Dynamic>) {} public static function piyo<T>(f: Int -> T) {} public static function main() { var xs: List<Void> = [null, null].list(); hoge(xs); var f: Int -> Void = function(x) {}; piyo(f); } }
Hash 廃止
Hash -> Map
- haxe 2
class Hoge { public static function main() { var m: Hash<Int> = new Hash(); m.set("hoge", 1); } }
- haxe 3
class Hoge { public static function main() { var m: Map<String, Int> = new Map(); m.set("hoge", 1); } }
生成されるJSがお行儀よくなった
- "use strict" がつくようになった
- 無名関数にくくられ、グローバルを汚さないようになった
Haxe側にあるパッケージと同じ名前空間をJS側からexportしたい場合にうまく動かない
ローカル変数で実現されているパッケージが、グローバルの名前を隠蔽してしまう。
hoge/aa/AA.hx
package hoge.aa; extern class AA { public static function create(): AA; }
hoge/Hoge.hx
- haxe 2
package hoge; import hoge.aa.AA; class Hoge { public static function main() { var a = AA.create(); trace(a); } }
- haxe 3
package hoge; import hoge.aa.AA; class Hoge { public static function main() { //var a = AA.create(); // runtime error: Cannot read property 'AA' of undefined var a: AA = untyped __js__("window.hoge.aa.AA.create()"); trace(a); } }
prototype でつけた関数をファーストクラスで使うと this がしぬ
はまりかけた。
コンストラクタで設定すればおk。
function Hoge() { var me = this; me.print1 = function() { console.log(me); }; } Hoge.prototype.print2 = function() { console.log(this); } var hoge = new Hoge(); hoge.print1(); // Hoge {print1: function, print2: function} hoge.print2(); // Hoge {print1: function, print2: function} var apply = function(f) { f(); }; apply(hoge.print1); // Hoge {print1: function, print2: function} apply(hoge.print2); // undefined
昔々にこれではまっていたようだ。
型パラメータとミックスイン
- Mix.hx
interface A {} interface B {} class C implements A, implements B { public function new() {} } class Mix { public static function hoge(x: A) {} public static function piyo(x: B) {} }
- Hoge.hx
using Mix; class Hoge { public static function main() { var c = new C(); c.hoge(); c.piyo(); } }
こんな感じで、2つ以上のインタフェースに対してミックスインをつけることもできる。
インターフェースが型引数を持っていても、一つなら大丈夫そう。
- Mix.hx
interface A {} interface X<T> {} class C implements X<A> { public function new() {} } class Mix { public static function hoge(x: X<A>) {} }
- Hoge.hx
using Mix; class Hoge { public static function main() { var c = new C(); c.hoge(); } }
が、下は通らない。
- Mix.hx
interface A {} interface B {} interface X<T> {} class C implements X<A>, implements X<B> { public static function new() {} } class Mix { public static function hoge(x: X<A>) {} public static function piyo(x: X<B>) {} }
- Hoge.hx
using Mix; class Hoge { public static function main() { var c = new C(); c.hoge(); c.piyo(); } }
- 実行
$ haxe hoge.hxml Hoge.hx:5: characters 2-8 : C has no field hoge
piyo はあるらしい。
どうも、最後の implememt だけ有効みたいだが・・・
えー。
構造体と型
typedef X = { id: Int, name: String } class Hoge { public static function hoge(x: X) {} public static function main() { hoge({ id: 0 }); } }
これはエラー。
$ haxe hoge.hxml Hoge.hx:5: characters 7-16 : { id : Int } has no field name Hoge.hx:5: characters 7-16 : For function argument 'x'
フィールド足りないし、まぁ分かる。
これも駄目。
using Lambda; typedef X = { id: Int, name: String, msg: String, hoge: Int } class Hoge { public static function hoge(x: List<X>) {} public static function main() { hoge([{ name: "hoge" , msg: "hoge" }].list()); } }
まぁ、そうだよな。
しかし、下は通る。
using Lambda; typedef X = { id: Int, name: String, msg: String, hoge: Int } class Hoge { public static function hoge(x: List<X>) {} public static function main() { hoge([{ name: "hoge" }, { msg: "hoge" }].list()); } }
えー。
type 演算子?
プログラム中のどこでも,ある表現の型を知るためには,type 演算子を使うことができます。コンパイル時に,type 演算子は除去され,表現だけが残ります :
var x : Int = type(0);
この例ではコンパイル時に Int と表示され,type が使われなかった場合と同じようにコンパイルされます。
Type Inference - Haxe
これは,クラスやドキュメントを参照せずに型を手早く知るためには便利です。
だそうだが、
- hoge.hxml
-js hoge.js -main Hoge
- Hoge.hxml
class Hoge { public static function main() { var x : Int = type(0); } }
- 実行
$ haxe hoge.hxml Hoge.hx:3: characters 16-20 : Unknown identifier : type
えー。
今のバージョンでYesodアプリを動かすまで
なんかうまく動かなくなってたので、メモ。
まずは、Haskell Platform を確認。
気がついたら64bitが入っていた。
多分 2012.4.0.0 にするときに誤って入れたらしい。
一旦今の Haskell 環境を消して、
$ sudo /Library/Haskell/bin/uninstall-hs --remove thru 7.4.2 $ rm -rf ~/.cabal/ ~/.ghc/ ~/cabal-dev/ $ rm -rf ~/Library/Haskell/ $ sudo rm -rf /Library/Haskell/ $ sudo rm -rf /Library/Frameworks/GHC.framework
で、32bitをいんすこした。
次。cabal-dev を入れる。
$ cd ~/Downloads $ git clone git@github.com:creswick/cabal-dev.git $ cd cabal-dev $ cabal install
次。yesod-platform を入れる。
$ cd $ cabal-dev install yesod-platform
次。PATHを当てる。
自分の場合は .bashrc を弄る。
(そのうち zsh に移ろうと言い続けて何年だろう・・・)
export PATH=〜:$HOME/cabal-dev/bin:$HOME/Library/Haskell/bin:〜:$PATH
ここまでで yesod コマンドが使えるはずなので確認。
$ yesod version yesod-core version:1.1.6.1 yesod version:1.1.4.1
適当に yesod アプリを作ってみる。
$ yesod init ... (適当に hoge っていうプロジェクトを作ってみた)
で、ビルド。
$ cd hoge $ cabal-dev install ... Preprocessing library http-conduit-1.8.5.1... [ 1 of 11] Compiling Network.HTTP.Conduit.Util ( Network/HTTP/Conduit/Util.hs, dist/build/Network/HTTP/Conduit/Util.o ) [ 2 of 11] Compiling Network.HTTP.Conduit.Chunk ( Network/HTTP/Conduit/Chunk.hs, dist/build/Network/HTTP/Conduit/Chunk.o ) [ 3 of 11] Compiling Network.HTTP.Conduit.Types ( Network/HTTP/Conduit/Types.hs, dist/build/Network/HTTP/Conduit/Types.o ) [ 4 of 11] Compiling Network.HTTP.Conduit.Parser ( Network/HTTP/Conduit/Parser.hs, dist/build/Network/HTTP/Conduit/Parser.o ) [ 5 of 11] Compiling Network.HTTP.Conduit.ConnInfo ( Network/HTTP/Conduit/ConnInfo.hs, dist/build/Network/HTTP/Conduit/ConnInfo.o ) ***/cabal-dev//lib/Crypto/Random/AESCtr.hi Declaration for $wmakeParams: Bad interface file: ***/cabal-dev//lib/Crypto/Cipher/AES.hi Something is amiss; requested module cipher-aes-0.1.5:Crypto.Cipher.AES differs from name found in the interface file cryptocipher-0.3.7:Crypto.Cipher.AES Cannot continue after interface file error Failed to install http-conduit-1.8.5.1 Configuring classy-prelude-conduit-0.4.2... ... cabal: Error: some packages failed to install: hoge-0.0.0 depends on http-conduit-1.8.5.1 which failed to install. authenticate-1.3.2 depends on http-conduit-1.8.5.1 which failed to install. clientsession-0.8.0.1 failed during the building phase. The exception was: ExitFailure 1 http-conduit-1.8.5.1 failed during the building phase. The exception was: ExitFailure 1 http-reverse-proxy-0.1.0.7 depends on http-conduit-1.8.5.1 which failed to install. yesod-1.1.7 depends on http-conduit-1.8.5.1 which failed to install. yesod-auth-1.1.3 depends on http-conduit-1.8.5.1 which failed to install. yesod-core-1.1.6.1 depends on clientsession-0.8.0.1 which failed to install. yesod-default-1.1.3 depends on clientsession-0.8.0.1 which failed to install. yesod-form-1.2.0.2 depends on clientsession-0.8.0.1 which failed to install. yesod-json-1.1.2 depends on clientsession-0.8.0.1 which failed to install. yesod-persistent-1.1.0.1 depends on clientsession-0.8.0.1 which failed to install. yesod-static-1.1.1.1 depends on clientsession-0.8.0.1 which failed to install.
どうも http-conduit のビルドでこけている。
cipher-aes-0.1.5 の Crypto.Cipher.AES と、cryptocipher-0.3.7 の Crypto.Cipher.AES があって、
モジュール名が同じじゃねーか!的なエラー。
http-conduit は cprng-aes に依存していて、
cprng-aes はデフォルトでは cipher-aes に依存するようになっている。
また、http-conduit は tls に依存していて、
tls は cryptocipher に依存している。
それとは別に、yesod init によって作られたプロジェクトは
clientsession に依存するようになっていて、
clientsession は cryptocipher に依存している。
cprng-aes の設定を見てみると、Flag によって cipher-aes ではなく cryptocipher に依存するようにできるらしい。
ということで、cprng-aes だけ設定を変えて先に入れるようにして
再度ビルドしてみる。
$ rm -rf cabal-dev/ $ cabal-dev install -f -fastaes cprng-aes $ cabal-dev install
無事ビルドできた。
さて 動かしてみる。
$ yesod --dev devel Yesod devel server. Press ENTER to quit Resolving dependencies... Configuring hoge-0.0.0... Rebuilding application... (using cabal-dev) Building hoge-0.0.0... Preprocessing library hoge-0.0.0... In-place registering hoge-0.0.0... ERROR: Could not read BuildInfo file: dist/setup-config Make sure that cabal-install has been compiled with the same GHC version as yesod. and that the Cabal library used by GHC is the same version cannot parse contents yesod: ExitFailure 1
・・・あれ?
$ dist/build/hoge/hoge Development Migrating: CREATE TABLE "user"("id" INTEGER PRIMARY KEY,"ident" VARCHAR NOT NULL,"password" VARCHAR NULL,CONSTRAINT "unique_user" UNIQUE ("ident")) Migrating: CREATE TABLE "email"("id" INTEGER PRIMARY KEY,"email" VARCHAR NOT NULL,"user" INTEGER NULL REFERENCES "user","verkey" VARCHAR NULL,CONSTRAINT "unique_email" UNIQUE ("email")) 127.0.0.1 - - [23/Dec/2012:16:43:43 +0900] "GET / HTTP/1.1" 200 - "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.101 Safari/537.11" ^C
こっちは動く。
ま、とにかく動かせるようになったので、今回はここまで。
同じようなフィールドを持ったレコードへの変換
RecordWildCards を使って、同じようなフィールドを持つレコードを簡単に変換できる。
- RecordA.hs
module RecordA where data A = A { a :: Int, b :: String, c :: Int } deriving Show
- RecordB.hs
module RecordB where data B = B { a :: Int, b :: String, c :: Int } deriving Show
- RecordC.hs
module RecordC where data C = C { a :: Int, b :: String, c :: String } deriving Show
- RecordMain.hs
{-# LANGUAGE RecordWildCards #-} module Main where import qualified RecordA import qualified RecordB import qualified RecordC createA :: RecordA.A createA = RecordA.A {..} where a = 1 b = "b" c = 100 -- A をそのまま B に変換 toB1 :: RecordA.A -> RecordB.B toB1 (RecordA.A {..}) = RecordB.B {..} -- A の一部を上書きして B に変換 toB2 :: RecordA.A -> RecordB.B toB2 (RecordA.A {..}) = RecordB.B {..} where b = "bb" -- A を C に変換 (C.c = "aaa") (A.c は使わない) toC1 :: RecordA.A -> RecordC.C toC1 (RecordA.A {..}) = RecordC.C {..} where c = "aaa" -- A を C に変換 (C.c = show A.c) toC2 :: RecordA.A -> RecordC.C toC2 (RecordA.A { c = ca, ..}) = RecordC.C {..} where c = show ca main :: IO () main = do print $ createA print $ toB1 $ createA print $ toB2 $ createA print $ toC1 $ createA print $ toC2 $ createA
- 実行
$ runhaskell RecordMain.hs A {a = 1, b = "b", c = 100} B {a = 1, b = "b", c = 100} B {a = 1, b = "bb", c = 100} C {a = 1, b = "b", c = "aaa"} C {a = 1, b = "b", c = "100"}
Haskellの言語拡張たち 2
前のやつ の続き。
今回調べた拡張
- レコード系
- RecordWildCards
- 型系
- ExistentialQuantification
RecordWildCards
レコードワイルドカードが使える。
レコードパターン中で .. とすれば、レコード内のフィールドを一気に束縛したり、現在のスコープの変数から与えたりできる。
{-# LANGUAGE RecordWildCards #-} data Ele = Ele { a :: Int, b :: Int, s1 :: String, s2 :: String } -- RecordWildCards 拡張が必要 create :: Ele create = Ele { b = 2, ..} where a = 3 s1 = "hoge" s2 = "piyo" -- RecordWildCards 拡張が必要 str :: Ele -> String str (Ele { a = 1, ..}) = s1 str (Ele { a = 2, ..}) = s2 str (Ele { a = 3, ..}) = s1 ++ s2 str (Ele {..}) = s1 main = putStrLn . str $ create
$ runhaskell record_wildcarts.hs hogepiyo
ExistentialQuantification
存在量化されたデータ構築子が書ける。
型クラスをインタフェースのように使った動的多態のようなことができる。
{-# LANGUAGE ExistentialQuantification #-} -- ExistentialQuantification 拡張が必要 data Ele = forall a . (Show a) => Ele a instance Show Ele where show (Ele x) = show x eles :: [Ele] eles = [Ele "hoge", Ele 1, Ele (3,6), Ele $ Just 2, Ele [4,3], Ele ["a", "b"], Ele 1.23] main = putStrLn . unlines $ show `map` eles
$ runhaskell existential_quantification.hs "hoge" 1 (3,6) Just 2 [4,3] ["a","b"] 1.23