JSX の紹介

    bleis-tift


November 3 2012
///// 型の紹介
 JSX

    bleis-tift


November 3 2012
自己紹介




id:bleis-tift / @bleis
好きなプログラミング言語は JavaScript /
Scala / F#など
てるるーさんが Haxe の話をするっぽいので、
当初は JSX の話を考えてました
無理っぽいので型と絡めた話をします
注意!




ガチな話はしない (できない) ので
   気軽に聞いてください
ガチな話が聞きたい人は、@keita44 f4 とか、@t6s
とかにどうぞ。
今日話すこと



JS を吐き出す言語と型の話
    型の便利さ
    型の楽しさ
    型の奥深さ
このあたりをちょ  っとでも伝えられたらな、と。
出てくる言語

     JSX・ DeNA 社製。生の JS よりも遅くならな
         ・
         ・
     いらしい (要出典1 )。JS の皮をかぶった Java
     Haxe・・
          ・この中では最古参。JS だけでなく、
     PHP や Flash、Java に C#など、様々な言語に
     変換可能。手続型言語の皮をかぶった関数型
     言語
     TypeScript・
               ・・この中では最後発。C#作った
     人が設計。JS との親和性を重視している、JS
     の皮をかぶった C#
静的型付けの言語オンリー

1
    ベンチマークが少ないので評価保留中
注意そのに!

これら 3 つの言語は設計思想が全く異なる
  JSX は最適化に重点を置いて、ばしばしインラ
  イン化を行う。ベストプラクティスという名の
  バッドノウハウを気にせずにコードが書ける
  TypeScript は JS との親和性を重視し、素直な JS
  を出力する
  Haxe は柔軟性があり、どちらにも倒せる
今回は「型」という観点からこれらの言語を
見てみる
これら 3 つの言語の優劣を決めようとかって
話ではない
目的や状況に合わせて言語を選択しましょう
質問!


Q. みなさん型についてどういうイメージをお持
ちですか?
    マイナスイメージ
    書くのが面倒・・
           ・
    冗長で邪魔!
    考えたくない><
  プラスイメージ
    安心!
    分かりやすい
    ウヒョヒョ、脳汁出るウヒョー
型がある言語ない言語


  型を持っている言語
    C / Java / JSX / TypeScript / Haxe
    PHP / Perl / Ruby / JavaScript / CoffeeScript
  型を持っていない言語
    機械語
    Grass / LazyK / ラムダ計算
静的型付け言語だろうが、動的型付け言語だろう
が、型はある
動的型付けと静的型付けの違い



ざっくり説明:
  動的型付け・・
        ・値にしか型が付かない
  静的型付け・・
        ・変数に型が付く
型を書く必要がある/ないという違いではない点
に注意!
静的型付けの何がうれしいの?

型に関するエラーが実行時に発生しない
  これはテストでは得られない保証
  キャスト?しらんがなー
型がドキュメントになる
  型があればある程度何をするか分かる
  とはいっても、何でもかんでも文字列で表すと
  かは論外なので死ねばいい
最適化の余地が多い
  最適化をどこまでやるかはトレードオフ
開発支援ツールが充実する可能性
  入力補完とか、リファクタリングツールとか
  可能性はあるが用意されてるかどうかは別
型推論
よくある比較の例
架空の静的型付け言語の変数宣言
// 文字列型の変数 s を"hoge"で初期化
String s = "hoge";
// 日付型の変数 d を現在日時で初期化
Date d = new Date();

架空の動的型付け言語の変数宣言
// 変数 s を"hoge"で初期化
var s = "hoge";
// 変数 d を現在日時で初期化
var d = new Date();
これで「静的型付け言語は冗長だ!」となるのは
悲しい

              型推論!
簡単な型推論
架空の静的型付け言語の変数宣言
// 文字列型の変数 s を"hoge"で初期化
String s = "hoge";
// 日付型の変数 d を現在日時で初期化
Date d = new Date();

=の右側が分かれば、左側の型って自明じゃね?
→静的型付け言語なら右側の型は分かる
→じゃぁ書かなくていいじゃん!
架空の簡単な型推論付静的型付け言語の変数宣言
// 変数 s を"hoge"で初期化 (変数の型は文字列)
var s = "hoge";
// 変数 d を現在日時で初期化 (変数の型は日付)
var d = new Date();
ん?
同じや!


架空の動的型付け言語の変数宣言
// 変数 s を"hoge"で初期化
var s = "hoge";
// 変数 d を現在日時で初期化
var d = new Date();

架空の簡単な型推論付静的型付け言語の変数宣言
// 変数 s を"hoge"で初期化 (変数の型は文字列)
var s = "hoge";
// 変数 d を現在日時で初期化 (変数の型は日付)
var d = new Date();
どんなものが来ても推論できるの?
var i = 42; // i は数値型

var xs = [1, 2, 3]; // xs は数値の配列型

// f: Regex[] を返すなら、x は Regex 型
var x = f("hoge")[2];
// g は文字列を受け取って Regex の配列を返す関数
var g = f;

var h = function(a) { return a + 1; };
// JSX : コンパイルエラー
// TypeScript : any を受け取り any を返す関数
// Haxe : Int を受け取り Int を返す関数
Haxe 素敵。TypeScript は any に落ちる。JSX は型
推論自体が後付けなので残念。
ちなみに・  ・
       ・
関数式 (ラムダ式)

JS
var f = function(i) { return i + 1 }
これを Haxe / JSX / TypeScript で書くと・
                                ・・
Haxe
var f = function(i) return i + 1;

JSX
var f = (i: int): int -> i + 1;

TypeScript
var f = (i: number) => i + 1
TypeScript が一番短く書けました
オーバーロード
JavaScript で



  文字列を検索して、ヒットした index が欲
  しい!
  それ indexOf で
  文字列だけじゃなくて、正規表現でも指定し
  たい!
とかそんな場合にどうするか。
そこでオーバーロードですよ!


JS でオーバーロードもどき
function idxOf(str, target) {
  switch (target.constructor.name) {
  case ’String’: // target が文字列の場合の実装
  case ’RegExp’: // target が正規表現の場合の実装
  }
}
他にも、コンストラクタはオーバーロード欲しい
ことが多いかも。
実は JS にも一部オーバーロードがある


演算子はオーバーロードされてる
1 + 2   // number + number = number
’3’ + 4 // string + number = string
// Object + number = number
{
  valueOf: function() { return 10 }
} + 20
// Object + number = string
{
  toString: function() { return ’x’ }
} + 20
JSX / Haxe / TypeScript の場合



  JSX・・
      ・オーバーロード対応
  TS・・・オーバーロード対応 (メソッドはでき
  ないっぽ)
  Haxe・
      ・・オーバーロード非対応
オーバーロードは一見便利だが・
              ・・
オーバーロードが型推論を邪魔する

オーバーロードがあると・         ・・
function f(i: Int) { return 0 }
function f(s: String) { return 1 }

// x の型は・ ?・
           ・
function g(x) { return f(x) }

オーバーロードを導入すると、強力な型推論はあ
きらめたほうがよい。
それに加え、後で出てくる variant があれば型が違
うだけのオーバーロードはパターンマッチで代用
できるし、数が異なるものはリストで代用できる。
名前を付けない型
ダックタイピング




アヒル (duck) のように歩き、
アヒルのように鳴くらば、
それはアヒルだ!
JS でのダックタイピング

x と y さえあればいい
function f(point) {
  var x = point.x;
  var y = point.y;
  // なにか素敵な処理
}
var p1 = { x: 10, y: 20 };
var p2 = new (function() {
  this.x = 1;
  this.y = 2;
})();

JSX / Haxe / TypeScript でどうやるのかを見てい
きます。
JSX の場合:template
JSX は template を使います。
template
function f.<T>(point: T): 戻り値の型 {
  var x = point.x;
  var y = point.y;
  // なにか素敵な処理
}
var p = { x: 10, y: 20 }; // うっ・ ・・
class Point {
  var x: number;
  var y: number;
  function constructor(x: number, y: number) {
    this.x = x; this.y = y;
  }
}

が、現行バージョンではユーザは関数テンプレー
トが使えません・
       ・・駄目じゃん
Haxe の場合:構造的部分型
Haxe では、構造的部分型を使います。
構造的部分型 (Haxe)
function f(point: { x: Int, y: Int }) {
  var x = point.x;
  var y = point.y;
  // なにか素敵な処理
}
var p = { x: 10, y: 20 };
class Point {
  public var x: Int;
  public var y: Int;
  public function new(x, y) {
    this.x = x; this.y = y;
  }
}
TS の場合:構造的部分型

TypeScript でも、構造的部分型を使います。
構造的部分型 (TypeScript)
function f(point: { x: number; y: number; }) {
  var x = point.x;
  var y = point.y;
  // なにか素敵な処理
}
var p = { x: 10, y: 20 };
class Point {
  constructor(public x: number, public y: number) { }
}

短い!
ダックタイピングと構造的部分型の比較
両者、同じようなことが実現できました。しか
し、プログラムの変更に対して、両者はまったく
異なる特徴を持ちます。
  内側の変更・
       ・・関数 f の中で、他のメンバにア
  クセスしたくなった
  外側の変更・
       ・・渡していたオブジェクトのメ
  ンバ名を、リファクタリングで変更した
これらの状況に対し、
  ダックタイピングは前者は容易だが後者は
  困難
  構造的部分型は前者は困難だが後者は容易
それにしても、だ




JSX マジ頑張れ・
         ・・
直和型
トランプのカード




簡単のために、絵柄を無視するとして・    ・
                      ・
  1∼10 までの数字
  Jack / Queen / King
これを、どう表現するか?
JSX の場合:クラス階層
abstract class Card {
  abstract function toNum(): number;
}
class Num extends Card {
  var num: number;
  function constructor(n: number) {
    this.num = n;
  }
  override function toNum(): number { return this.num; }
}
class Jack extends Card {
  static var _instance = new Jack();
  static function getInstance(): Jack { return Jack._instance; }
  override function toNum(): number { return 11; }
}
class Queen extends Card {
  static var _instance = new Queen();
  static function getInstance(): Queen { return Queen._instance; }
  override function toNum(): number { return 12; }
}
class King extends Card {
  static var _instance = new King();
  static function getInstance(): King { return King._instance; }
  override function toNum(): number { return 13; }
}


長い!
Haxe の場合:enum


enum といっても、C や Java の enum とは違い
ます。
トランプのカードの表現
enum Card {
  num(n: Int); jack; queen; king;
}
num に注目!何と他の列挙子と違い、値を持って
ます。
enum に対する操作

enum に対する操作は、switch を使って行います。
カードを数値化する
function   toInt(c)
  return   switch (c) {
    case   num(n): n; // !
    case   jack: 11;
    case   queen: 12;
    case   king: 13;
  }
パターンマッチ!
ここで仕様変更!


Joker も扱いたくなった
Card に Joker を追加
enum Card {
  num(n: Int); jack; queen; king; joker;
}
これでよい・ ?
     ・・
今まで書いてきた Card を使う処理、どうしま
しょう。
例えばさっき書いたこの処理

カードの数値化 (再掲)
function   toInt(c)
  return   switch (c) {
    case   num(n): n; // !
    case   jack: 11;
    case   queen: 12;
    case   king: 13;
  }
joker の場合の処理が書かれてないです。
こんな感じの関数がすでにいたるところで書かれ
ている・   ・・
パターンの網羅性チェック
でも、Haxe の enum ならパターンの網羅性チェッ
クがあるので、全ての場所でコンパイルエラーと
なります。
パターンが網羅性がチェ         ックされる
function toInt(c)
  // Some constructors are not matched: joker
  return switch (c) {
    case num(n): n;
    case jack: 11;
    case queen: 12;
    case king: 13;
  }
あとはコンパイルエラーをつぶしていくだけ!
安心!
enum によるオーバーロードの代用


例えばこんな enum を用意します。
enum StrOrEReg { str(s: String); regex(r: EReg); }

そしてパターンマッチ!
function   idxOf(s, target)
  return   switch(target) {
    case   str(x):   // target が文字列の場合の実装
    case   regex(x): // target が正規表現の場合の実装
  }
JS との比較
JavaScript
function idxOf(str, target) {
  switch (target.constructor.name) {
  case ’String’: // target が文字列の場合の実装
  case ’RegExp’: // target が正規表現の場合の実装
  }
}

Haxe
function   idxOf(s, target)
  return   switch(target) {
    case   str(x):   // target が文字列の場合の実装
    case   regex(x): // target が正規表現の場合の実装
  }

Haxe 版は、target に数値などを渡すとコンパイル
エラーになってくれる!
TypeScript の場合:enum?


     We expect the final language to
  support enum types, but not in the form
  they are currently implemented.
「最終的な言語には enum のせたいけど、今はま
だないよ」
とのことなので、Haxe の enumっぽいものがのる
ことをみんなで祈りましょう。
まとめ

静的型付け言語といっても、型をいっぱい書
く必要はない (型推論)
オーバーロードは便利だが、いいことばかり
ではない
構造によって型を指定できる (構造的部分型)
Haxe の enum は他の言語の enum とは違う (直
和型)
今回は「型」という観点で各言語を見た
もう一度「注意そのに!」のスライドをよく
読むこと
注意そのに!(再掲)

これら 3 つの言語は設計思想が全く異なる
  JSX は最適化に重点を置いて、ばしばしインラ
  イン化を行う。ベストプラクティスという名の
  バッドノウハウを気にせずにコードが書ける
  TypeScript は JS との親和性を重視し、素直な JS
  を出力する
  Haxe は柔軟性があり、どちらにも倒せる
今回は「型」という観点からこれらの言語を
見てみる
これら 3 つの言語の優劣を決めようとかって
話ではない
目的や状況に合わせて言語を選択しましょう
おしまい

JSX / Haxe / TypeScript