はてブとIFTTTを使ったブックマークの集約方法

ブックマークの集約方法がこんがらがってきたので整理しようと思う。

いろんなタイミングでいろんなアプリケーションからブックマークするのでどうも分散してしまっている。 これではいろんなところを探しに行く必要がある。 どうにか1箇所に集約する方法がないものかと。

ブックマークするタイミングとしては主に以下3つ

  1. PCブラウザ
  2. iPhoneで「Flipboard
  3. iPhoneで「Gunosy

PCブラウザはブックマークレットかプラグインで「はてブ」一択だが、他が問題。 「Flipboard」も「Gunosy」も直接「はてブ」にもっていけない。。。 はてブに登録するためにブラウザで開き直して...とかやってられん。

ここで会社の先輩がいいことを言っていたのを思い出した。

最近気になる記事はTwitterに流してんねん。

「Flipboard」も「Gunosy」もTwitterに直接投稿する能力がある。

はてブTwitterからurlを吸い上げる能力を持っている。

これだ!!

というわけでいろいろ考えた結果以下のようになった。

  1. PCブラウザ
  2. iPhoneで「Flipboard」
  3. iPhoneで「Gunosy
    • 気になった記事はツウィート
    • 後で読む記事は「後で読む」的なハッシュタグ付きでツウィート(詳細後述
      • IFTTTでタグ付きツイートをPocketへ登録

これでひとまず全てのブックマークが「はてブ」に集約される。

IFTTTを活用して「後で読む」を整理

「後で読む」記事もうまいこと集約したい。

希望は以下2つ

  1. 後で読む記事だけまとめてみたい
  2. 総じて一箇所で検索したい

ここで登場するのが「IFTTT」というWebサービス。

自分で好きなようにWebサービス同士を連携することができる。素晴らしい。

こんな風に使ってみた。

  • Twitterの「後で読む」的タグ付きツウィートをPocketに保存
  • Pocketに保存された記事をはてブ
    • はてブメール投稿を利用して連携

これで希望は満たされた。

  1. 後で読む記事だけまとめてみたい
    • 「Pocket」で一覧表示&既読管理
  2. 総じて一箇所で検索したい
    • 「Pocket」と「はてブ」の同期がとれているので「はてブ」を検索すればオールOK

よし。これで情報の海へダイブ。

「静的型付けが出来るJavaScriptの上位言語」を触ってみよう。パート5 - Dart

このシリーズもパート5。

静的型付けができJavaScriptへコンパイルできる言語をちょろりと触ってみようシリーズの最終章です。

とうとうやって参りました。Dart。

なぜ私がDartを推すかは最後に記載します。

じゃいってみましょ

Dart

http://www.dartlang.org/

ナニモノかね?

そもそもDartはDartVM上で動作する言語であることが主体 JavaScriptへのコンパイルはおまけ(?)的位置付け

"おまけ"はちょっと言い過ぎな気がしますが、後述する内容のポイントなので大げさに表現しています。

本日の見出し

コンパイラインストール

  1. sdkダウンロード
  2. 解凍してパスを通す

以上、DL&詳しくはこちら

  • MacOS以外でも同じような感じだと思う(たぶん)
  • MacOSの場合brewからもインストールできるが、バージョンが少し古かったので手動インストール
  • brew使う場合、brew install dartでOK

今回も頑なに「エディタ + コンパイラ」です。本来はDart EditorなるIDEを使うほうがよろしいかと

コンパイルしてみる

今回のテーマはあくまでJavaScriptとして使用することなのでJavaScriptへの変換を試みる。

DartVMが搭載されているブラウザであればコンパイルは必要ないのだが、現状DartVMを搭載しているブラウザはDartiumしかなく、このブラウザをメインで使っているユーザは恐らくいない。。 必然的にブラウザ上で動作させるのであれば「JavaScriptへ変換」せざるを得ない。

.dartJavaScriptへ変換するためにはDartSDKに同梱されているdart2jsというコンパイラを使用する

DartもHaxe同様JavaScriptコードはコンパイルできない

  • Haxeとの違いとしてMainクラスの定義は必要ない模様

sample.dart

main() {
  print('Hello, Dart!');
}

コンパイル

$ dart2js sample.dart
Dart file sample.dart compiled to JavaScript: out.js

オプションとしてなにも設定しない場合は、out.jsと言う名前のJavaScriptファイルに結果を出力する

  • 出力結果JavaScriptは壮絶なものとなる為割愛
    • --minifyオプションもあるがmainメソッドに関してはあまり変わらない
    • dart2jsを使用して出力するJavaScriptはもはや後からJavaScriptとして改修できるものではない
  • 出力したJavaScriptはnode.jsでの直接実行不可
    • html上で読み込まれることを想定したJavaScriptが出力される
    • 自身は動作確認用のhtmlファイルを用意して確認した。

クラス定義

class Sample {
    String name;    // プロパティ

    Sample(String name) {   // コンストラクタ
        this.name = name;
    }

    void say(String greet) {    // 引数型指定
        print('${greet} ${this.name}');
    }

    String getName() {      // 戻り値型指定
        return this.name;
    }
}

main() {
    Sample sample = new Sample('dart');
    sample.say('Hello');
    print('name: ${sample.getName()}');
}
  • 変数名の前に型を定義するあたりがJavaっぽい
  • やっぱり同名メソッドでのオーバーロードはサポートされていない
  • コンストラクタは単純なプロパティ初期化であれば以下のように定義を省略できる
Sample(this.name);
  • 同一型の変数はまとめて宣言可能
num x, y, z;

クラス定義とは関係ないが文字列内の${...}は実行時に展開される

継承はもちろんのこと、Javaライクなインターフェースやジェネリクスの継承関係も表現できる。

abstract class Client {     // インターフェース
    int execute(String arg);
}

class MockClient implements Client {
    int execute(String arg) {
        print('execuet: ${arg}');
        return 0;
    }
}

class User<C extends Client> {    // 継承関係を持ったジェネリクス

    C client;

    User(this.client);

    void action() {
        client.execute('hogehoge');
    }
}

main() {
    new User<MockClient>(new MockClient()).action();
}

↑にも書いたが、TypeScript, HaxeとくらべてJavaっぽい。 Javaになれたエンジニアなら馴染みやすそうだ。

Haxe同様、JavaScriptを書いていると言う感覚は皆無。 この点に関しては同様のメリット・デメリットがありそう。 (Haxeについては前回の記事参照)

その他Dartの言語仕様はこちら

外部JavaScriptライブラリの使用

いろいろ調べたが、外部ライブラリの使用方法は見当たらなかった。 機能としてJavaScriptライブラリの読み込みは提供されていない模様。

もし使用したいライブラリがあれば完全にDartコードに書き起こす必要がある。

Dartはデフォルトで様々なAPIが提供されており、これを使用することでだいたいのことはまかなえる(??)

Dart API Reference

Dartium

冒頭で紹介した通り、DartはDartVM上で動作する言語、 ChromiumにDartVMを搭載したブラウザがDartiumである。

Wiki等によると、今後Google ChromeにもDartVMが組み込まれる予定らいしい。

Dartium上ではDartコードはJavaScriptに変換することなく実行できる。

html上でDartコードを読み込む為には以下のようなタグを定義する

<script type="application/dart" src="time.dart"></script>

また、併せて以下のタグも定義する必要がある

<script src="https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/browser/lib/dart.js"></script>

折角なのでどのくらい実行速度が違うのかdart2jsで出力したJavaScriptと比較してみます。

time.dart

main() {
    String text = '';
    DateTime now = new DateTime.now();
    for(int i = 0; i < 10000; i++) {
        text = '$text$i';
    }

    print(new DateTime.now().millisecondsSinceEpoch - now.millisecondsSinceEpoch);
}
  • 10000回文字列結合を繰り返した後のスタートエンドのタイムスタンプ差分出力

結果

  • dart: 71ms
  • dart2js: 3ms

うん。JavaScriptの方が早い。

この結果をもってどうこう言う気はない

ちなみにdartコマンドで実行した場合は65ms


Dartを簡単に紹介しました。が、今回の内容はあまりDartのメリットを感じて頂けなかったかと思います。。。

しかし、自身はDartに注目しています。

なぜかと言うと。

ここまでJavaScriptへ変換できる別言語をざっと触ってきましたが、実開発を想定した場合、コンパイル後のJavaScriptを何かの都合で少しでも触ってしまう(手を加えてしまう)ようなことがあれば、その時点で変換と言うプロセスは破綻します。

なぜ触ってしまうのか、それはさわれてしまうからです。 これまで紹介したアプローチは全て別言語からJavaScriptを出力する方法です。 JavaScriptはもちろんヒトが編集することを想定したプログラミング言語です。 だからさわれてしまうのです。

(もちろんプロジェクトの厳格なルールとして触らせなければいいのですが、それは置いといて)

Dartのアプローチは、そもそもJavaScriptを出力することを目的としていません。 JavaScriptの代わりになることが目的です。

JavaScriptが大変素晴らしい言語であることはもちろんのことです。

しかし昨今、JavaScriptは利用シーンや開発規模の変化から様々な問題も発生しています。

このシリーズで紹介したアプローチもその問題の解決方法の一部ですが、更にDartはこれらの問題を根本的に(言語仕様から)解決しようとする試みであると感じています。

静的言語からJavaScriptを生み出す仕組みではなく、静的言語をブラウザ上で実行してしまおうという試みなのです。

この試みが正しいかどうか、また成功するかどうかはわかりませんが、単純な話、上述した通り、結局編集できないのなら出力結果はプログラミング言語である必要はありません。

このことを考えればDartのアプローチはすごく自然な考え方で、それを実現しようとしているところに面白みを感じています。

本日参考にさせていただいたサイト

今回も本文には掲載しておりませんが、とても参考になりました。Dartの魅力をより詳しく紹介されています。

今回シリーズを通して「静的型付けが出来るJavaScriptの上位言語」(の本当に断片)を紹介してきました。

今後クライアントサイドのプログラムはますます増加します。

大量のソースコードを大人数で実装するような場合、静的な型解決がうむメリットは絶大です。

このシリーズで挙げたような言語は、今後かなり大きな意義を持つようになると自身は感じています。

「クライアントサイド != JavaScript

時代が変わろうとしているのかもしれませんね。

「静的型付けが出来るJavaScriptの上位言語」を触ってみよう。パート4 - Haxe

第4回までやってまいりましたこのシリーズ

「静的型付けが出来るJavaScriptの上位言語」を触ってみて自身の感想をまとめています。

今回はHaxeです。

Haxe

http://haxe.org/?lang=jp

ってなに?

  • 「ヘックス」と発音
  • 歴史がある
    • 2005〜
  • ソースファイル(.hx)をコンパイルして様々な言語(↓Wiki参照)のソースファイルを出力
    • これが一番の特徴ではないかと思う
  • ActionScriptに似た構文

前回までのTypeScriptとは異なりJavaScript以外の言語へのコンパイルをサポートしている点がものすご~く気になる。が、今回は「JavaScriptの...」と銘打っているのでJavaScriptのみ。

その他詳細はWiki等で

本日の見出し

コンパイラインストール

Mac/WinはDLサイトにインストーラがあるのでダウンロードしてインストールする。

コマンドラインからインストールする方法は見つかりませんでした... ちなみにbrewにはない模様

コンパイルしてみる

TypeScriptのノリでコンパイルしようとしたらいろいろ躓いたので違いを列挙

  • Mainクラスが必要
    • コンパイラにMainクラスを指定しなといけないので
    • Mainクラスの名前は任意
  • クラス名とファイル名は合わせる
  • Mainクラスにはmainメソッドが必要
  • 素のJavaScriptコードはコンパイラが受け付けない

と言うわけでまずはMainクラス作成

Sample.hx

class Sample {
    static function main() {    // mainメソッド
        trace("Hello World !"); // ←コンソールへの出力はこんな風に表現する
    }
}

そしてコンパイル

$ haxe -main Sample -js sample.js

結果

sample.js

(function () { "use strict";
var Sample = function() { }
Sample.main = function() {
    console.log("Hello World !");
}
Sample.main();
})();

Haxeはコンパイルに必要な情報を1つのビルドファイルにまとめて定義することができる

こういうの好きなのでやってみる

build.hxml

-main Sample
-js sample.js
  • ファイルの内容はコンパイルオプションの列挙

コンパイルする。

$ haxe build.hxml

これでコンパイルオプションが複雑になっても面倒くさくない

クラス定義

こんな感じ

class Sample {
    static function main() {    // mainメソッド
        trace("Hello World !");
    }

    var num1 : Int;                         // プロパティ
    private var num2 = 100;                 //Constantsなら宣言と同時に初期化が可能
    // public var sample = new Sample();    //Constantsではない値での初期化はコンパイルエラー

    public function new() {     // コンストラクタ
        trace("constructor");
    }

    public function setNum2(num2: Int) {   // 引数型指定
        this.num2 = num2;
    }

    public function getNum2(): Int {    // 戻り値型指定
        // return 'hoge';   // ←はString型を返却している為コンパイルエラー
        return this.num2;   // 宣言と同時に初期化したフィールドは型が自動解決されている
    }

    /* 同名メソッドのオーバーロード定義はできない
    public function new(num1 : Int) {
        this.num1 = num1;
    }
    */
}
  • varfunctionが必ず必要なことぐらいでTypeScriptと大差無い
  • もはやLL言語とは言わせない感がある

継承

class SubSample extends Sample {
    override public function getNum2(): Int {
        return this.num2 * 2;
    }
}
  • オーバーライドする場合overrideキーワードが必ず必要

外部JavaScriptライブラリの使用

TypeScriptの宣言ソースファイルのようにexternと言う仕組みがある。

externが用意されたライブラリはもちろん型を考慮してすぐにHaxeコードに取り込める。

externが無い場合、自分で用意するか、untypedキーワードを使ってライブラリが読み込まれていることを前提としたコードを書くことになる。

上記「コンパイルしてみる」の通り、Haxeは素のJavaScriptのコンパイルは行えない。 このことからuntypedキーワードを使ってのコーディングはHaxe上ではあまり多用するものではないように感じる。

その他いろいろ

TypeScript #2で紹介したEnumsジェネリクスももちろんある。

Enum要素をオブジェクトのように扱うことができたり、先日のバージョンアップで追加された抽象型は魅力的。

詳しくは以下等を参照


ひとつの言語を語るのには全くもって情報が少なすぎるように思いますが、一旦まとめます。

一言でいうと「重厚感」。これにつきます。

TypeScriptからの流れで対応した経緯からかもしれませんが、もはやJavaScriptとは一線を画した言語であると感じました。

様々な言語へコンパイルすることが目的であれば全く問題ありませんが、今回のテーマのように「JavaScriptを静的型付けする」こと(JavaScriptの代替)が目的であれば、やや重い気もします。

特に、多くのメンバーがJavaScriptに慣れ親しんでいるようなプロジェクトだと、Haxeの重さインパクトがかなり大きいように思います。

この重さが効果を発揮するような状況ももちろんあります。

Haxeのメリットを最大限享受できるよう使い処をしっかり考えて使いたいと思います。

次回

自身の大本命Dartです。

本日参考にさせていただいたサイト

本文には載せておりませんが、いろいろ参考にさせていただきました。ありがとうございました。

「静的型付けが出来るJavaScriptの上位言語」を触ってみよう。パート3 - TypeScript #3

前々回前回と続けておりますこのシリーズ。 自身の直感を頼りに「触ってみよう」レベルの情報を連載しております。 (コアな情報は記載していません。あしからず。)

今日はTypeScript最終章でいきたいと思います。

本日の見出し

ではいってみましょ

Any

TypeScriptにはanyaは小文字)と言う型があります。

これはなんだと言うと、なんでも型です。

これを指定すると全ての型チェックが無効になります。

え、そもそも型指定する為にTypeScript使うんじゃ...

こちらではこのように記載されています。

any は既存の JavaScript コードを移植するようなときや、JSON 形式のデータを扱うような場合に便利なことがあります。
また、any にキャストするとどんな式でもあらゆる型チェックを回避できるので、どうしてもうまく書けない時の回避策としても重要です。

そう。既存コードなど型を意識しないコードになんでも型を使用することで、柔軟に、簡潔に流用することが出来るような仕組みになっています。

thisの解決

JavaScriptでなにが厄介かと言うとこれです。thisの解決。

使用箇所よってthisの中身は変化する為、いちいち気を使わないといけない。

TypeScriptではどのように解決できるかというと

class Foo {
    greet: string;

    say() {
        console.log(this.greet); // 1.
    }
}

var foo: Foo = new Foo();
foo.greet = 'hello';
foo.say();

こんなクラスがあったとして

  • 「1.」でthisを使用している
  • 「1.」をthis.fooとか存在しないプロパティを指定するとコンパイルエラー(thisがFooクラスとして認識されていることがわかる)

実行すると

$ node usethis.js
hello

もちろん「hello」と自身のプロパティが表示される

ここまではわかる。型を指定出来ないクロージャとかでのthisの扱いが問題。

例えばsetTimeoutとかで指定するコールバック関数内でthisを使用した場合

class Foo {
    greet: string;

    say() {
        setTimeout(function() {
            console.log(this.foo); // 1.
            }, 1000);
    }
}

var foo: Foo = new Foo();
foo.greet = 'hello';
foo.say();

実行すると

$ node usethis.js
undefined

undefinedthisが解決されていない...

こういう場合、TypeScriptは↓のように定義することでthisが解決出来る

class Foo {
    greet: string;

    say() {
        setTimeout(() => {
            console.log(this.greet);
            }, 1000);
    }
}

var foo: Foo = new Foo();
foo.greet = 'hello';
foo.say();
  • 実行すると「hello」が表示される

この() => { ... }のような関数定義をアロー関数と呼ぶらしい。詳しくはこちら

アロー関数のコンパイル済JavaScriptはこんな感じ

...
        var _this = this;
        setTimeout(function () {
            console.log(_this.greet);
        }, 1000);
...
  • _this変数へ退避されている

module(namespace)

TypeScriptmoduleキーワードを使って名前空間(パッケージ)を表現することができる模様

export

moduleキーワードを使って定義した名前空間にクラスを追加するにはexportキーワードを使用する

module utils {
    export class Foo {
        greet: string;

        say() {
            setTimeout(() => {
                console.log(this.greet);
            }, 1000);
        }
    }
}

new utils.Foo();
// new Foo();   // ←これはコンパイルエラー

import

名前空間が長い場合、importキーワードを使用して省略できる

module com.hatenablog.koizuss.utils {
    export class Foo {
        ...
    }
}

import utils = com.hatenablog.koizuss.utils;

new utils.Foo();
  • Javaのようなimport com.hatenablog.koizuss.utils.Fooimport com.hatenablog.koizuss.utils.*のようなインポートはNG

ファイルインポート

exportした別ファイルをインポートすることもできる

import utils = module("utils");

これは↓と等価

var utils = require("./utils");
  • 別ファイルとしてのexportrequireについては前回参照

*.d.ts

宣言ソースファイルというらしい。既存JavaScriptを元にTypeScriptとしての定義のみを書き出したファイル。

宣言ソースファイルを使うことで、例えば自分で実装していない既存JavaScriptライブラリの定義のみを参照し、TypeScriptとして実装できる

  • 参照するだけで使用したライブラリが勝手にインポートされるわけではないので注意

外部ライブラリの参照

有名どころライブラリの宣言ソースファイルはこちら

宣言ソースファイルは↓のようなコメントを記載することで参照できる

/// <reference path="hoge.d.ts" />

宣言ソースファイルのコンパイル

勘違いをしておりまして。過去の記事で「jsファイルからのコンパイル」と書いていましたが、JavaScriptファイルから宣言ソースファイルを生成する方法はありませんでした。すいません。

TypeScriptJavaScriptをライブラリとして展開する場合に、宣言ソースファイルを実装した.tsファイルから生成することができます。

↓のようにコンパイラ--declaration(or -d)オプションを加えてコンパイルすることで宣言ソースファイルを出力できます。

$ tsc --declaration sample.ts
  • sample.tsで実装した型定義がsample.d.tsファイルとして出力されます

IDE

ここまで自身は全てテキストエディタで実装し、コマンドでコンパイルしてきました。

実際にTypeScriptを使ってアプリケーションを作成するのであれば、IDEを使うことが理想的です。

TypeScriptの型解決はIDE上で更に効果を発揮します。

なぜなら、型が定まっていることで、充実したコード補完リアルタイムにコンパイルエラーを確認できるからです。

デモ動画を見てもらうとその効果がよく分かるのではないかと思います。

で、じゃTypeScriptはどんなIDEに対応しているか。

↑の動画で使用されているVisual Studio 2012を初め、以下のエディタを公式にサポートをしています。

公式サポートエディタ

後、IntelliJはプラグインがある模様(JetBrainsすげぇ!!ということはWebStormでも使える?!)


さてさて。

ここまで3回にわたってTypeScriptをざっくり触ってきたんですが、感想をこれまたざっくりとまとめたいと思います。

  • やっぱり型解決できているって健康的
    • アプリケーションは複数のデータと処理を統合することで動きます。他のデータ・処理がどのような責務を持ってどのように振る舞うかを型によって表現し保証することでスムーズな連携を可能とします。 JavaScript上で他の部品を使用する場合の言い知れぬ不安を、静的に型を定義することで事前に解決することができます。 これは精神衛生上かなり健康的!!
  • オブジェクト指向プログラミング(OOP)に適している
    • 素のJavaScriptでもOOPは可能です(若干異論ありそうな気がしますが...)。でもこれじゃない感が拭えません。 やはりOOPは型と言う概念があっての賜物かなと自身は思います。(OOPのメリットは割愛)
  • IDEって素敵やん
    • 型を定義する = 記述するコード量が増えます。 以前に記載した通り、コンパイル済JavaScriptに型は一切反映されません。と言うことは実際動くプログラム以外のコードを書く必要があります。 しかし!!ここでIDEがいい仕事をします。IDEが型を理解することで↑に挙げたような入力補完やコンパイルエラーのリアルタイム検知ができます。 コードを書く量は増えますが、このようなIDE機能を活用することで、実質的にコードをタイプする量は、型解決しないコードを書く場合と大差ないと思われます。 このようなIDEの機能を使用できるのも型と言う概念があってこそです。
  • 柔軟性に優れている

    • TypeScriptとして自身が一番優れていると感じた点はここです。素のJavaScriptも書ける。 例えば、別の処理が使用する振る舞いは、型を意識してきっちり、 振る舞いの中身等、内部に隠蔽されたスコープでは型を意識せず、さくっと、 みたいな部分的な使い分けが可能です。 造るモノ・ヒトに応じたいい塩梅を実現できます。
  • 既存コードがそのまま動作する

    • ↑でも挙げましたがTypeScriptJavaScriptをそのまま記述できます。と言うことは既存資源をそのまま流用できます。 今JavaScriptでなにかしようとした場合、ライブラリを使用しないことはほぼ皆無です。既存のライブラリをそのまま使用できるメリットは非常に大きいと思います。 (使えるというだけで、素のJavaScriptは型解決できません。型を含めた使用は宣言ソースファイルが必要です)

次回

今度は同じノリでHaxeを試してみたいと思います。

「静的型付けが出来るJavaScriptの上位言語」を触ってみよう。パート2 - TypeScript #2 (TypeScript 0.9の新機能)

えぇとですね。

前回終わりにこんな事を書いてました

次回「TypeScript後半戦」として以下のようなことを試してみます。
Any
thisの解決
...

が、しかし。

マイクロソフト、「TypeScript 0.9」をリリース

いやぁ〜絶妙なタイミング!!!

前回の告知を大いに裏切り(すいません...)「TypeScript 0.9」を早速触ってみようと思います。

「TypeScript 0.9」新機能

折角なんで「TypeScript 0.9」から実装された新機能を試してみます。

今回はTypeScript 0.9 公式アナウンスを参考にします。

Generics

ジェネリクスですねぇ。 総称型。実装時に型指定するあれですねぇ。

ジェネリクスと言えばリスト要素型の指定

var array: Array<string> = ["John", "Sam", "George"];
  • Array<string>で配列の要素型を指定できる
  • 要素の型が保証されるのでarray[0].lengthのように要素を直接string値として使用可能
  • 以下のように指定した型以外の型の要素を入れようとするとコンパイルエラー
var array1: Array<string> = ["John", "Sam", "George", 1];

var array2: Array<string> = [];
array2.push(1);

要素型が指定出来ないと配列はどんなオブジェクトでも入るが、 出した後なんのオブジェクトかわからないから結局型チェック的なことが必要になったり。 これで入れる側・使う側双方スッキリですね。

ジェネリックメソッド

ジェネリックメソッドも使える

interface Array<T> {    // 1.
    // ...
    map<U>(callbackfn: (value: T, index: number, array: T[]) => U): U[];    // 2.
    // ...
}

var array: Array<string> = ["John", "Sam", "George"];   // 3.

var lengths = array.map((val, idx, arr) => val.length); // 4.

サンプルコードのまんまですね。一応解説しておくと

  1. ↑で書いたように要素型をジェネリクスで定義できるArrayインターフェースがある
  2. Arrayにはmapメソッドがある
    1. コールバック関数を引数で受け取る
    2. 全ての要素に対して引数のコールバックを実行
      • コールバックの第1、第3引数は「1.」で指定された型が反映される
    3. 実行結果を配列にして返却
      • コールバックの戻り値型が返却する配列の要素型に反映される

    (「1.」「2.」はこんなAPIが実装されていると言う表現なので、実際には定義不要)

  3. 要素型stringで配列を生成
  4. mapメソッドを実行
    • Array<string>でしていした要素型が反映され為、コールバック引数:valはstring型であることが保証される
    • valはstring型なのでコールバック関数内でval.lengthは実行可能
    • val.lengthはnumber型なのでmapメソッドの戻り値は要素型:numberな配列となる

独自クラスのジェネリクス

class Sample<T> {   // 1.
    val: T;
}

var sample: Sample<string> = new Sample<string>();  // 2.
sample.val = 'sample';          // 3.
var val: string = sample.val;   // 4.

class SubSample extends Sample<string> {    // 5.

}
  1. ジェネリッククラス定義
  2. string型指定でインスタンス生成
  3. sample.valの初期化値はstring型以外だとコンパイルエラー
  4. sample.valは必ずstring型となることが保証される
  5. 継承時のジェネリクス指定

その他

Javaにあるような以下の定義は使えない模様

Overloading on Constants

オーバーロードはこんな感じで定義

function func(val: string): string;
function func(val: string, num: number): string;
function func(val: string, num?: number): string {
    if(num) return val + num;   // 引数numの存在チェック
    return val;
};
// function func(num: number): string;  // ここにオーバーロードメソッドを定義するとコンパイルエラー
  • 関数本体は1箇所に定義して引数パターンをチェック

オーバーロードはv0.8以前にもあった機能ですが、今回のバージョンアップで関数本体以降にオーバーロードが定義できなくなったらしいです。

関数本体が一箇所にしか定義できないのはぐぬぬな感じなのでこのくらいで。

Export =

なんか若干間違った解釈のような気もするが、別ファイルのクラスをファイル指定でクラスとして読み込む機能のようである

client.ts(読み込み元ファイル)

// export対象クラス
class Client {
    constructor(public name: string, public description: string) { }
}

export = Client;    // クラスをexport

app.ts(読み込み先ファイル)

import MyClient = require('./client');  // ファイル名指定で``require``(読み込み)
var myClient = new MyClient("Joe Smith", "My #1 client");
  • exportしたクラスはそのファイルの代表クラス(public class)のようなもの
  • exportは2つ以上定義するとコンパイルエラー
  • require指定する際、拡張子は不要(付けるとコンパイルエラー)
  • importで定義するクラス名は読み込み元クラス名と合わせる必要はない(同名、別名どちらもOK)
  • 読み込み先tsをコンパイルすると読み込み元tsもコンパイルされる

で、これをコンパイルしてみると、exportimportはこんな感じになる

// export
module.exports = Client;

// import
var MyClient = require('./client');

はい。このmodule.exportsrequireはなんなんだと。

これはnode.js等のサーバサイドJavaScriptで実行することを想定した定義です。 ブラウザ上では動作しません

  • node.jsのrequire,module.exportsはこちらで詳しく解説されています。

Enums

列挙型。ある程度のボリュームのJavaScriptを書くと絶対ほしくなる。

サンプルはこんな感じ

enum Color { red, blue, green }

var myColor = Color.red;
console.log(Color[myColor]);

これをコンパイルすると

var Color;
(function (Color) {
    Color[Color["red"] = 0] = "red";
    Color[Color["blue"] = 1] = "blue";
    Color[Color["green"] = 2] = "green";
})(Color || (Color = {}));

var myColor = Color.red;
console.log(Color[myColor]);

コンパイルすると0からのインデックス値がそれぞれのEnum要素に設定されると

じゃ数値との比較はできるのか??

if (myColor === 0) return;

↑のコードはOKです。コンパイルエラーになりません。

じゃあじゃあ列挙体の各要素値を自分で指定できる??

enum Color { red = 10, blue = 20, green = 30 }

これもOKです。ちゃんとコンパイル後のJavaScriptに値が反映されます。

  • 値はnumber型のみ指定可能(文字列等は指定できない)
  • 値の重複はコンパイルエラーにはならない

Declaration Merging

定義済class、functionなどのデフォルト値を別の定義から設定出来る模様

function greet(lang = greet.lang) { // 1.
    console.log('hello ' + lang);
}

module greet {  // 2.
    export var lang = "java";   // 3.
}

greet('typescript');    // 引数を設定した場合の実行
greet();                // 引数を設定しない場合の実行
  1. 同名モジュールgreetlangプロパティを引数のデフォルト値に設定
  2. 別の定義でmoduleを定義
  3. lang値を定義してmoduleへ追加

これを実行してみると

$ node merging.js
hello typescript
hello java

期待通り

  • 引数を指定した場合: greet引数は指定値(typescript)を使用
  • 引数を指定しない場合: greet引数はmodule greetで指定したデフォルト値langjava)を使用

もちろんfunction内で引数判定して切り替えることも可能だが、これは別モジュールとして定義出来ることにメリットがある

  • ↑の場合、module greetを定義しなくてもコンパイルエラーとはならない模様

ちなみにこれのコンパイル結果は以下

function greet(lang) {
    if (typeof lang === "undefined") { lang = greet.lang; }
    console.log('hello ' + lang);
}

var greet;
(function (greet) {
    greet.lang = "java";
})(greet || (greet = {}));

greet('typescript');
greet();

以上、ざっと新機能紹介してみました。

さらっと触れる程度しか紹介できてませんでしたが、型に厳密な言語では必須になっているGenericsやEnumが実装されたことで、かなり実用的になった感が増したのは私だけでしょうか。

また、TypeScriptを使用するメリットの一つとして、必ず型を定義する必要はないと言うことが挙げられます。

厳密に型を定義するところと、柔軟にサクサク実装するところを上手く使い分けることで、よりTypeScriptの魅力が引き立つのではないかと考えています。

次回

今度こそほんまに「TypeScript最終回」です。以下のようなことを試してみます。

  • Any
  • thisの解決
  • module(namespace)
    • export
    • import
    • ファイルインポート
  • *.d.ts
    • jsファイルからのコンパイル
  • 外部ライブラリの使用
  • IDE
本日参考にさせていただいたサイト

ありがとうございました。

アリの行動原理をルート検索プログラムに利用する

今テレビでやってたので備忘

アリは目的地に辿り着くルートをどのように判断しているか

  • アリは常にフェロモンを出しながら移動している
  • 目的地にたどり着いたアリはフェロモンを出しながら来たルートを通り巣へ帰る
  • フェロモンは一定時間で消える

これによって、成功ルートのフェロモンは行き・帰りで2倍になり、 失敗ルートのフェロモンは行きの1回しか付けられていない為、一定時間で消える。

後続のアリはフェロモンの濃いルートを選択することで目的にに辿り着く。

後続のアリがまたフェロモンを出すので、最適なルートは常にフェロモンが常に一番濃くなる。

この原理を応用したプログラム

この原理を運送業者のルート検索やNASAの宇宙探査船の運行ルート分析に使っているらしい

運送業者のルート検索

複数の目的地を通りセンターへ戻る為の最適ルートの検索。

NASAの宇宙探査船の運行ルート分析

宇宙探査船の使用するエネルギーを減らす為、衛星軌道を利用している。 どのルートを通ればどの惑星の衛星軌道にのり、よりエネルギーを節約できるかを分析。


へぇ〜である。

今後なにかの際に使えるアイデアかも。

「静的型付けが出来るJavaScriptの上位言語」を触ってみよう。パート1 - TypeScript #1

最近JavaScriptを書くことが多い

JavaScriptは書けば書くほどモヤモヤする(原因のほとんどは自身の力不足だが...)

今後どんどん触れる機会を増すJavaScript達をもっとスッキリ書けないものかと、

考えた結果、やはり型の表現が難しいことが理由のひとつかと思ったりした。

そこで!!!

「静的型付けが出来るJavaScriptの上位言語」を触ってみよう。

今日はその第一弾

なにやんの??

探すといろいろある

どれがいいとかはわからないので、とりあえずちょろりと書いて感覚を味わってみようと思う。

(このシリーズはあくまで導入部分のみを記載、詳細な部分にはふれません。あくまで自身の感覚的なお話)

対象

「静的型付けが出来るJavaScriptの上位言語」で自身が興味のある以下3つを選択

Dartが一番興味があるのだが、Dartの良し悪しを測る為にも他の言語を触れておく Dartについては後ほど

環境

  • OS: Max OS X(10.7.5)
  • Editor: Sublime Text 2
    • おきになのでww
    • 本来IDEを使う方がサクサクなんだろうけど、一旦素のEditorで書いてみる

じゃまずはTypeScriptから

TypeScript

http://www.typescriptlang.org/

ざっくりと

  • Microsoft製
    • IDEはVisualStudioがいい感じ??
    • MS系言語のクライアント実装とかに使うと捗りそう
  • 新手
    • 2012〜
  • ソースファイル(.ts)をコンパイルしてJavaScriptを出力
    • コンパイルする時に型を判定
    • 同じアプローチの言語としてCoffeeScriptとよく比較されてる
  • 既存コードとの親和性が高い
    • 使用されるデータ型JavaScriptと同じなので既存コードと親和性が高い
    • JQueryやBackbone.jsなどの既出ライブラリも合わせ使用可能

かなり詳しく載っているので詳細はこちら

TypeScriptこの動画を元に試してみる

本日の見出し

コンパイラインストール

まずはコンパイラがないと何も出来ないのでインストール

$ npm install -g typescript

これだけ。簡単ですねww

  • npm(node)のインストールはmacなら brew install nodeとか
$ tsc -v
Version 0.8.3.0

コンパイルしてみる

まずはsample.tsをつくって、普通にJavaScriptで関数を書いてみる

function proccess(x) {
    x.name = 'foo';
    var v = x + y;
    console.log(v);
}

このままコンパイルしてみる

$ tsc sample.ts
.../sample.ts(3,16): The name 'y' does not exist in the current scope

yがないっておこられちゃいましたね。

動画のコードにだまされたww

ここでわかるのは素のJavaScriptコードもコンパイルできると言うこと

JavaScriptのバリデータとしても使えるし、JavaScriptコードも混ぜて実装したり、部分的にTypeScriptに移行なんかもできそう

コンパイルが通ると同じディレクトリにsample.jsができる

  • まだ普通のJavaScriptコードしか書いてないのでsample.tssample.jsは全く同じ内容

引数型

そろそろ本題

関数の引数に型を定義をできる

引数xstring型で定義

function proccess(x: string) {
    x.name = 'foo';
...

こういうコードをコンパイルすると

$ tsc sample.ts
.../sample.ts(3,6): The property 'name' does not exist on value of type 'string'

ですね。x.name = 'foo';が怒られますね。xstringとして定義したため、stringnameプロパティがないと

しっかりプロパティ判定が行われてます

動画で紹介されていたxnumberや、boolで定義した場合はコンパイル通っちゃったので割愛(IDEなら警告がでる??)

動画でさらっと出て来ましたが、戻り値型を持つ関数を引数として受け取る引数は以下のような定義のよう

function proccess(x: () => string) {
    x().charAt;
}
  • 関数を受け取り実行
  • その関数が戻り値型stringで定義されている為、charAtが使用出来る

型定義されたプロパティを持つオブジェクトを引数型とすることもできる

function proccess(x: {a: number; b: string;}) {
    x.b.charAt;
}
  • プロパティはそれぞれセミコロンが必要(無いとコンパイルエラー、忘れそう...カンマ書きそう...)
  • もちろんcharAtの対象オブジェクトがxでもx.aでもコンパイルエラー

引数のオプション化

↑のコードを実行する場合

proccess({a: 1, b: 'sample'});

となる

でもaは必要ないので省略したい

proccess({b: 'sample'});

これはコンパイルエラー

そこで

function proccess(x: {a?: number; b: string;}) {
    x.b.charAt;
}

引数名に?をつけるとオプション化することができる

引数オブジェクトをインターフェースとして抽出

{a?: number; b: string;}

この部分をインターフェースとして抽出し、再利用できる

interface Thing {
    a?: number;
    b: string;
}

function proccess(x: Thing) {
    return x.b.charAt;
}

こんな感じ、かなりスッキリですね


ここまでコードをコンパイルしたJavaScriptコードは全て引数型定義がなくなったもの

もちろんですがコンパイル後のJavaScriptでは型は無効となる

コンパイル後のJavaScript

function proccess(x) {
    return x.b.charAt;
}

クラス定義

さてさて、じゃクラスを書いてみる

クラスの定義はこんな感じ

class Point {
    x: number;                              // ... 1. プロパティ
    y: number;

    constructor(x: number, y: number) {     // ... 2. コンストラクタ
        this.x = x;
        this.y = y;
    }

    static origin = new Point(0, 0);        // ... 3. スタティックメンバー

    func1() { return this.x; }              // ... 4. メソッド
    private func2() { return this.y; }      // ... 5. アクセス制御
    get func3() { return 'sample'; }        // ... 6. アクセッサ

}

ざっくりと

  1. プロパティ
    • 型が指定できる
  2. コンストラクタ
  3. スタティックメンバー
    • 上記であればPoint.originのように使用
  4. メソッド
    • new Point().func1()
  5. アクセス制御
    • new Point().func2()はコンパイルエラー
  6. アクセッサ
    • new Point().func3のように使う
    • アクセッサを定義する場合はコンパイル時に--target ES5が必要
      • tsc --target ES5 sample.ts
      • デフォルトはES3でコンパイルされる

ゆるくなく、ガチガチでもなく程よい

全くもってしっくりくる!!!

コンパイルすると

var Point = (function () {
    function Point() { }
    Point.prototype.initialize = function (x, y) {
        this.x = x;
        this.y = y;
    };
    Point.origin = new Point(0, 0);
    Point.prototype.func1 = function () {
    };
    Point.prototype.func2 = function () {
    };
    Object.defineProperty(Point.prototype, "func3", {
        get: function () {
            return 'sample';
        },
        enumerable: true,
        configurable: true
    });
    return Point;
})();

アクセッサの部分以外想像通り

アクセス制御はこれまたtsコードの中だけで有効(コンパイルすると消える)

このコードをJavaScriptで書くストレスを考えると、これだけでTypeScriptを使う価値がある

継承

class Point3D extends Point {

    constructor(x: number, y: number, z: number) {
        super(x, y);
    }

    func1() { return this.y }
}

自然だ。

コンパイルすると

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var Point = (function () {
    ...
})();
var Point3D = (function (_super) {
    __extends(Point3D, _super);
    function Point3D(x, y, z) {
        _super.call(this, x, y);
    }
    Point3D.prototype.func1 = function () {
        return this.y;
    };
    return Point3D;
})(Point);

当たり前ですが、ちゃんと継承が実装されてる

継承すると__extendsメソッドが自動生成される

  • 継承を全く使ってなければ__extendsは生成されない
  • Backbone.jsなんかはUnderscoer.jsの_.extendを使うけど、この場合はextendsは使えない??(これは後半で記載します)

んんん、なるほど、もはやこれだけでも素晴らしと思ってしまった。

こっからが面白いんですが、情報量が多くなってしまったので今日はここまで。

次回「TypeScript後半戦」として以下のようなことを試してみます。

  • Any
  • thisの解決
  • module(namespace)
    • export
    • import
    • ファイルインポート
  • *.d.ts
    • jsファイルからのコンパイル
  • IDE
本日参考にさせていただいたサイト

ありがとうございました。