2015/04/30 このエントリーをはてなブックマークに追加 はてなブックマーク - 【Kotlin】AltJS?それ、Kotlinで出来るよ

【Kotlin】AltJS?それ、Kotlinで出来るよ










KotlinはJavaScriptへのコンパイラ(Kotlin to JavaScriptなコンパイラ)を搭載しています。


ただ、これまでKotlinでJavaScriptをやるというのはネガティブな印象が強かったと思います。
実用段階でないとか、機能として不充分とか。
一方で、KotlinがAltJSになるんではないかという注目をされている人も多少なりともいるようです。


で、足りないんだとしたらどこが足りてないのか、とか自分で確かめるしか無いな、と。

試した結果としては結構使えるんじゃないかなと思いました。








メリットが何かと考えると、KotlinのメリットをそのままJavaScriptに持ち込めることだと思います。


具体的には

・静的型付け
・型安全、Null安全
・Kotlinライブラリやシンタックスが使える
・Javaライブラリが使える
・クライアントサイドもサーバーサイドもKotlinで書ける(脳内の切り替えが必要ない)
・JavaScriptで扱うdocumentやwindowなどのオブジェクトもクラスとして扱える






KotlinはM10(マイルストーン10)でKotlinのJavaScriptコンパイラにも結構手を入れました。

javascript-interop - Kotlin Blog


M10からはJavaScriptコンパイル用にdynamicキーワードとjs関数が導入されました。
また、js.noImplというデリゲート機能がnoImplにrenameされました。
それに加えてnative実行とnativeゲッター/セッター実行サポートも行われました。


dynamicキーワードで宣言されたものは実行時に評価されます。
したがって、動的チェックになってしまうのはちょっと残念なところ…!
でもだいぶ便利なんですよね。


多分、あんまり説明になっていないと思いますが、
M10によってJavaScriptのネイティブな部分をKotlinに置き換えるのがだいぶ楽になったと思ってくれれば良いと思います。





大体、核になる部分はここにまとまっています。



https://github.com/JetBrains/kotlin/tree/master/js/js.libraries/src/core



・JavaScriptの標準的なDOM操作
・簡単なnative実行
・synchronyzedとvolatileサポート
・windowオブジェクトの使用
・documentオブジェクトの使用
・consoleオブジェクトの使用
・JSONオブジェクト作成
・JSON文字列作成
・Javaのコレクションフレームワークの利用
・JavaのString操作(未実装部分あり)
・Kotlinライブラリの利用
・JQueryのDOM操作
・localStorage、sessonStorage
・canvas
などなど…



基本的にJavaScriptで出来ることはKotlinのJavaScriptランタイムで出来ると思ってもらっていいです。
(nativeアノテーションとdynamicキーワードとnoImplで実現できます。)

あと、Dartのastを内包してるみたいなんですが、これ使えるのかな…(^^;
https://github.com/JetBrains/kotlin/tree/master/js/js.dart-ast






KotlinのJSコンパイラはコンパイル後jsファイルを生成しているようです。
JavaScriptのライブラリにはありがちなのですが、Kotlinというオブジェクトに
functionをはやしていく用な感じです。


どのようにJavaScirptが生成されているか気になる人はtry.kotlinorgというWeb上でKotlinが試せる
サイトをやってみることをオススメします(JetBrains提供)。

実行時にコンソールに標準出力と、コンパイル後のJavaScriptも表示してくれるので
それを見ればなんとなくどういう作りになっているかは分かるはず。


こんな感じ。


















見て分かる通り、このtry.Kotlinorgでは「_」はKotlinのJavaScriptランタイムの予約語っぽい扱いのようですね。
(たぶん。試しに変数として宣言してみたら例外吐いた)




処理を追っていくと、大枠としては
・Kotlinオブジェクトの出力のflush
・Kotlinオブジェクトを引数とする無名関数を宣言し、即実行
・Kotlinオブジェクトの出力のバッファリング

という感じですね。


無名関数の処理として、
・KotlinオブジェクトでdefineRootPackage関数でmain関数を定義したパッケージオブジェクトを生成する
・KotlinオブジェクトのdefineModule関数で先ほど生成したパッケージオブジェクトをロードする
・main処理の実行
まで定義されています。



■標準出力、デベロッパーコンソール出力、alert出力

import kotlin.js.*
fun main(args: Array<String>) {
    val array = listOf(1,2,3,4,5)
    array.filter{it % 2 == 0}.forEach{console.log(it)}
    array.filter{it % 2 == 0}.forEach{js("alert(it);")}   
    array.filter{it % 2 == 0}.forEach{println(it)}
}



js関数は文字列をnativeなJavaScriptとして実行します。
これを使うとまぁ簡単に試すときは楽ですね。evalしてるのかな・・・。
個人的には、スクリプトレット的な邪悪さを感じるので、あんまり使いたくないのですが
alertはなぜか標準に入ってないっぽい
window.alert()出呼び出せて欲しいもんですが。


機能追加してプルリク飛ばせば良いのか。





■JSON操作
import kotlin.js.*
fun main(args: Array<String>) {
    
    // Pairを作る
    val name = Pair("name", "yank")
    val year = Pair("year", "26")
    val address = Pair("city", "tokyo")
    
    // jsonのオブジェクト生成
    val jsonObj = json(name, year, address)
    // for(key in jsonObj){ println("key is ${jsonObj[key]}")}  これは出来ないらしい
    // 参照
    println(jsonObj["name"])
    println(jsonObj["year"])
    println(jsonObj["city"])
    // jsonオブジェクト to String
    val jsonString = JSON.stringify(jsonObj)
    println(jsonString)
}


json関数でPairをjsonのオブジェクトに変換できるのですが、
Kotlin的にこれをiteratableとして扱っていないようでJavaScript的に
ループさせたりするのは一工夫必要です。

Kotlinにはダック・タイピングと呼ばれるものがあるのでそれでうまいこと出来ないかなぁとか…。





KotlinをaltJSとしてみた時、どういった機能があるか、
どういった構文を扱うか、今までどういった変化があったか。
また、サンプルコードを軽く書いてみました。

所感としては、悪くはない。という感じです。


例えば、backborn.jsやKnockout.jsのライブラリを読み込むなども簡単に行えると思いますし、
型があるというのと静的チェックのある安心感は大きいです。


PureなJavaScriptが好きな人には必要ないでしょうが、
altJSとして使いたいという人には選択肢に含まれてもいいかなという印象。
TypeScript、Coffee、Dart、Kotlin…みたいな!




ただ、もうちょっとかなぁという詰めの部分が何とかなってくれば面白くなってくると思います。
化ける可能性はあるぞ!








3 件のコメント:

  1. > ・Javaライブラリが使える
    これ出来ないと思うんですけど、出来るんですか?(できたら確かにかなりスゴイんですが…)

    返信削除
    返信
    1. >あやぴーさん

      言い切っちゃっておいてアレなんですが、
      現状、出来ると言えば出来ますし、出来ないと言えば出来ないようです(^-^;

      現在、KotlinのJavaScriptのランタイムでは
      java.lang
      java.io
      java.util
      の一部のAPIが利用出来ます。

      例えばjava.langならStringBuilder
      java.utilならList、Map、Setインターフェースのクラスなどです。

      最終的にはJavaScriptが生成されますので、JavaScriptとして可能な範疇以上のことは用意してないんだろうなと想像してます。

      なので、あやぴーさんのいう「出来たらスゴイ」は出来ないかなぁと。。。

      削除
    2. おー、凄い微妙な範囲…。
      それ中途半端でよくない気がしますね。 Clojure/ClojureScript だと基本的に import とか require 出来るのはそれぞれのプラットフォームで使えるものだけ( Clojure なら Java のライブラリ、 ClojureScript なら JavaScript のライブラリ)なので、 JS を生成するコードに Java の標準ライブラリを使える(内部的に変換しているというのはさておき)のはちょっと驚きました( Clojure 標準ライブラリであればある程度は相互で使えるように作られています)。
      ちなみに Clojure では cljc (reader conditionals) という機能が 1.7 で入ってきてて、プラットフォームに依存するようなコード( interop )をコンパイル時に分岐させることでポータブルなコードを書けるようにしていたりしますね。

      削除