matarilloの雑記

GitHubに公開したソフトウェアなどについて書きます。

coreclr Book of the Runtime日本語訳プロジェクトに協力中です。

coreclr関係もろもろ - ものがたり

そんなわけで、coreclrやらcorefxやらroslynやらの調べ物をしようと思って日々過ごしているわけですが、最近気付いたもので、coreclrのリポジトリの中に、"Book of the Runtime" (BotR) と呼ばれるドキュメント集があって、.NETランタイムの内部設計についていろいろ解説してあるのです。これはなかなか興味深い。ランタイムの各コンポーネントの担当者がまとめた文章を集めたものであるらしく、コードの詳細までは踏み込まないけど、内部実装の理解がそれなりに得られるものだと思います。

というわけで、少しずつ読み解きながら翻訳してみたいと思っています。翻訳したドキュメントは、このリポジトリに置いておきたいと思います。翻訳を手伝っていただける方は随時募集しています(既にわたしだけのプロジェクトではないです)。

というわけで翻訳を手伝っています。

github.com

とりあえず現在は、型システムのチャプターをやっつけて、スレッドのチャプターに着手しています。

適当な日本人の名前や住所をランダムに作れる gimei を C# に port した。

willnet/gimei - GitHub

gimei は、日本人の名前や、日本の住所をランダムに返すライブラリです。テストの時などに使います。似たようなライブラリにfakerがあります。fakerはとても優れたライブラリで、多言語対応もしていますが、ふりがな(フリガナ)は流石に対応していません。gimei ふりがな(及びフリガナ)に対応しています。

Big Sky :: 適当な日本人の名前や住所をランダムに作れる gimei を golang に port した。

オリジナルは ruby gems です。


というわけで、gimeiおよびgo-gimeiを真似してC#で書いてみました。

matarillo/dot-gimei - GitHub

.NET port of gimei

NuGet Gallery | dot-gimei

PM> Install-Package dot-gimei

gimeiおよびgo-gimeiと違うとこ:

  • データはDLLに埋め込んでしまいました。
  • そうはいっても差し替えたいこともあるだろうから、Gimei.Generatorクラスを作っておきました。こいつのコンストラクタTextReader を食わせてください。
  • IsMaleプロパティとIsFemaleプロパティは bool? 型です。(ざわ……ざわ……)
  • 新しく Gender プロパティを追加しました。型は GenderIdentity 列挙体です。
    • 政治的に使いにくいAPI
using System;
using DotGimei;

class Program
{
    public static void Main(string[] args)
    {
        var name = Gimei.NewName();
        Console.WriteLine(name);                // 斎藤 陽菜
        Console.WriteLine(name.Kanji);          // 斎藤 陽菜
        Console.WriteLine(name.Hiragana);       // さいとう はるな
        Console.WriteLine(name.Katakana);       // サイトウ ハルナ
        Console.WriteLine(name.Last.Kanji);     // 斎藤
        Console.WriteLine(name.Last.Hiragana);  // さいとう
        Console.WriteLine(name.Last.Katakana);  // サイトウ
        Console.WriteLine(name.First.Kanji);    // 陽菜
        Console.WriteLine(name.First.Hiragana); // はるな
        Console.WriteLine(name.First.Katakana); // ハルナ
        Console.WriteLine(name.IsMale);         // false

        var male = Gimei.NewMale();
        Console.WriteLine(male);          // 小林 顕士
        Console.WriteLine(male.IsMale);   // true
        Console.WriteLine(male.IsFemale); // false

        var address = Gimei.NewAddress();
        Console.WriteLine(address);                     // 岡山県大島郡大和村稲木町
        Console.WriteLine(address.Kanji);               // 岡山県大島郡大和村稲木町
        Console.WriteLine(address.Hiragana);            // おかやまけんおおしまぐんやまとそんいなぎちょう
        Console.WriteLine(address.Katakana);            // オカヤマケンオオシマグンヤマトソンイナギチョウ
        Console.WriteLine(address.Prefecture);          // 岡山県
        Console.WriteLine(address.Prefecture.Kanji);    // 岡山県
        Console.WriteLine(address.Prefecture.Hiragana); // おかやまけん
        Console.WriteLine(address.Prefecture.Katakana); // オカヤマケン
        Console.WriteLine(address.Town);                // 大島郡大和村
        Console.WriteLine(address.Town.Kanji);          // 大島郡大和村
        Console.WriteLine(address.Town.Hiragana);       // おおしまぐんやまとそん
        Console.WriteLine(address.Town.Katakana);       // オオシマグンヤマトソン
        Console.WriteLine(address.City);                // 稲木町
        Console.WriteLine(address.City.Kanji);          // 稲木町
        Console.WriteLine(address.City.Hiragana);       // いなぎちょう
        Console.WriteLine(address.City.Katakana);       // イナギチョウ

        var prefecture = Gimei.NewPrefecture();
        Console.WriteLine(prefecture); // 青森県
    }
}

「Web系企業が教えてくれないWindowsではじめるWebプログラミング」の想定読者

SlashDotのACさんたちとか、Web系企業のひととかとは、前提が違うと思うんですよ。 スタートとゴールが未定義だと議論が発散するから意味ないんですよね。 なので、いま私が念頭に置いていることをメモっておきますよ。

スタート

  • PCのWebブラウザ(種類は問わない)で不自由なくWebブラウジングできる人
  • ぐぐれる人(Web検索して情報を得ることができ、かつその情報の有効性について何らかの判断ができる)が望ましい
  • 何らかの手続き型言語で何らかのプログラムを書いたことあって、ループとか関数とかコンパイルとか黒い画面とかそういうので怯まない人が望ましいけど、必須ではない(そのあたりから解説してもいい)

ゴール

具体的には、以下のようなことを一通りカバーする

  • ブラウザ(HTTPクライアント)とWebサーバがやっていることがHTTP/1.0くらいのレベルでイメージできる
  • HTMLとXMLの最大公約数的なシンタックス(開始タグ終了タグ属性コメントエスケープあたり、木構造の理解が望ましい)がわかる
  • 静的HTMLとCSSをすげえ簡単なレベルで書ける
  • anchorタグでリンクしている2-3枚のHTMLをlocalhost上の何らかのWebサーバに置いて、ブラウザからアクセスしてリンクをたどって確認できる
  • formタグのある静的HTMLと、それを受け取って動的HTMLを返す何らかのプログラムをlocalhost上の何らかのWebAppサーバに置いて、ブラウザからアクセスしてフォームにポストして確認できる(GET/POST両方やれるべき)
  • DOMがどういうものかイメージできる
  • JavaScriptが要求されてロードされて実行される流れがなんとなく把握できてる
  • bodyのloadとbuttonのclickあたりにJavaScriptイベントハンドラを仕掛けたHTML+JavaScriptを書ける
  • クッキーとセッション状態がどういうときに使われるか、簡単な例を説明できる
  • クッキーとセッション状態を使った単純なWebアプリケーションを書ける

また、以下のことは範囲外とする

  • データ永続化と取得
  • セキュリティ(これはインターネットに向けてサービスやアプリを公開する前に叩き込む必要があるけど、教えることが多くて困る)
  • 特定の言語プラットフォームやフレームワークを前提とする知識
  • HTML5などのモダンWeb標準
  • AltJSやモダンJavaScript

「Web系企業が教えてくれないWindowsではじめるWebプログラミング」という連載を始めます

始めるって書いておけばなかったことにするわけにもいかないだろう。

週一ぐらいのペースでQiitaに記事を書いていこうかなと思います。

それともあれかな?定期勉強会を開いて、そっちから記事なりなんなりに起こしたほうがいいかな?

とりあえず、SlashDotにぽろっと書いたら変な怨嗟コメントがついたけど転載しておきますね。

Web系のプログラミングをWindowsで覚えようとすると嫌になってプログラミングを断念するからさっさとWindowsは捨てたほうがいいというあれ。たぶんWAMPとかXAMPP for Windowsとかそういうあれとか、gitとか、そういうのをイメージして言ってんだろうし、それは一理あるけども。 とはいえ、プログラミング初心者がVisual Studio+ASP.NET MVC+C#でプログラミングを覚えて、その後MacでもLinuxでも使うようになったって別にいいじゃんと思うわけだ。

はいはい、誰も使ってないプロプラ技術(実際にはそんなこともないけど、それを置いておいたとして)にロックインされるといいたいわけね。でもほんとにロックインされるか?プログラミングを「はじめる/おぼえる」のと「つづける」は違うと思うし、「はじめる/おぼえる」のハードルを下げることには意味があると思う。HTTPとか、HTMLとか、CSSとかJSとかを一通り覚えるにあたって、VSだとできないなんてことあるかね。ないよ。

何で多くの人が、MS系技術とFOSS系技術を両方学ぶのは無理とか無駄って思ってるんだろうね。 無理なくできるよ。そして、プログラミングを覚えるハードルを下げるという意味では無駄なんかじゃないよ。

HTML Parser "dcsoup" のバージョンを 0.2.0 に更新しました。

GitHubにもリリースタグを付けました。

NuGet Galleryはこちら。

ライブラリに含まれるクラスのプロパティが、Javaっぽい部分と、jQueryっぽい部分と、.NETっぽい部分がぐちゃぐちゃになってます。いずれきちんとした指針を立てた上で整理したいのですが。

と書いてたあたりをやっつけました。

具体的には

// getter
public string Text()
{
  return text;
}

// fluent setter
public Element Text(string text)
{
  this.text = text;
  return this;
}

みたいになってたところを

public string Text { get; set; }

みたいにプロパティにしてしまった上で、jQueryライクなメソッドチェーンのためには

public static class FluentUtility
{
  public static Element Text(this Element self, string text)
  {
    self.Text = text;
    return self;
  }
}

と、拡張メソッドで対処しました。

これが本当にいいのかというと、ちょっとわからないのですが。

Sharpenの最新版

「dcsoupがテストに通るようになりました」に書いたSharpenですが、MonoプロジェクトのGitHubに置いてあるやつを使ってみました。

  • Eclipseプラグインとして使うのは想定してないっぽい。EclipseJavaパーザーとかそういうのも含めて全部jarにまとめてしまって、コマンドラインから叩けってことみたい。
  • ただコマンドライン引数の解析のやり方がやっつけっぽい。java -jar sharpenの全部入りjar SOURCEPATH でいいとか言ってるんだけど、ソースパスにスラッシュ(/)が入ってることを前提としてたりして、コード読まないとわからん。
  • コマンドライン引数がちゃんとしてればちゃんと動くっぽい。
  • ジャグ配列の初期化 int[][] array = new int[3][4]; で変換がこけた。たぶん古いバージョンでもこける。
  • エンクロージング型の親クラスメソッドへの呼び出し、つまり エンクロージング型名.super.メソッド(); も変換がこけた。たぶん古いバージョンでもこける。
  • タイプセーフenumがどうなったかは確認してない。 以下のような感じになりました。

Javaコード

public enum Strategy {
    Foo {
        String execute() {
            return "this is Foo";
        }
    },
    Bar {
        String execute() {
            return "this is Bar";
        }
    };
    abstract String execute();
}

C#コード

using Sharpen;

[System.Serializable]
public sealed class Strategy
{
    public static readonly Strategy Foo = new Strategy();

    public static readonly Strategy Bar = new Strategy();

    internal abstract string execute();
}

変換ではエラーにならなかったけど、execute() の実装がぜんぶ抜け落ちてるじゃん…… Strategyクラスはabstractメンバー持ってるよね、コンパイル通らないよ……

dcsoupがテストに通るようになりました

というわけで本体コードもテストコードもJavaからのコンバートが片付いたので、知見をメモしておきたいと思います。

Sharpen

JavaのコードをC#に変換するのには Sharpen を使いました。 とはいえ私が実際に使ったのは(ngitのリポジトリにある)古いバージョンのやつでした。なーんだ、こっちはちゃんとメンテされてるじゃん……

というわけで以下に書くのは古いSharpenに基づくものなので、最新版では事情が違うかもしれません。違うといいな。あとで試そう。

Eclipse

Eclipse 3.7 IndigoじゃないとSharpenプラグインがうまく動きませんでした。最新のSharpenなら違うのかな……

Eclipse 3.7 の \plugins フォルダに sharpen.core_1.0.0.jar を放り込んでEclipseを起動したら認識しているはず。

eclipse

設定ファイル

プロジェクト直下に設定ファイルが必要です。run-sharpen.xml (Antタスク定義)、sharpen.properties (パスの設定)、sharpen-all-options (マッピング定義)、header.txt (C#の共通ヘッダテンプレート、Antタスクで参照されてる)に関しては、 この記事その翻訳からリンクされている、Paulさんの設定ファイルテンプレートを元にするといいかも。

ちなみに、sharpen.properties にSharpenの作業ディレクトリが書いてあるけど、そのディレクトリが見つからないとエラーになるから注意。

実行前

最初は極小のサンプルコードみたいなやつを変換して、動くか確認するといいと思います。

ant task

run-sharpen.xml にAntのアイコンが出てると思うので、右クリックして "Run as"を選べば動きます。

思ったより普通に変換してくれましたが、注意点はあります。それは、一部のタイプセーフenumが変換エラーになったことです。 いわく、「enumにはフィールドとかメソッドとか定義できないよ!」とのこと。いや、C#ではできないのはわかるけど、そこはよしなにやってほしかった。

仕方ないので、コンバート前のJavaコードを人力で書き換え。メソッドなどを持つenumをまるっと普通の抽象クラスにしました。

人力プリコンパイル前:

enum Strategy {
    Foo {
        void execute() { ... }
    },
    Bar {
        void execute() { ... }
    };
    abstract void execute();
}

人力プリコンパイル後:

abstract class Strategy {
    public static final Strategy Foo = new Strategy("Foo") {
        void execute() { ... }
    };
    public static final Strategy Bar = new Strategy("Bar") {
        void execute() { ... }
    };
    abstract void execute();

    final String name;
    protected Strategy(String name) { this.name = name; } 
    public String name() { return name; }
    public static Strategy valueOf(String name) { ... }
}

なにこれ面倒くさい。nameの処理とかすげえ面倒くさい。 こんなんだったらSharpenを使わずに手でC#にしようか……とも思ったのだけどそっちのほうがミスが多くなりそうだったから諦めて上みたいに書き換えました。

実行後

さて、変換エラーを手で回避する作業が終われば、無事C#に変換されたコードが手に入ります。 とはいえ、すぐコンパイルが通るかというとそうでもなかったり。

まず気づくのが、using Sharpen; とか Sharpen.Runtime.Substring(...) とか IList<string> list = new AList<string>(); みたいなコード。 結局ね、Javaの標準クラスライブラリを全部.NETの標準クラスライブラリに機械的に置き換えることには無理があるのですよ。なので、拡張メソッドを生やしたり、JavaのクラスをシミュレートするC# クラスを作ったりしているのです。 そのあたりのクラスのソースコード(ただし古い)はこちら。最新のものは最初に書いたリポジトリにあるんじゃないかな。

次に注意するのは命名規則とかそういうやつのあれ。Java

class Foo {
    public static class Bar {}
    public Bar bar() { return new Bar(); }
}

みたいな感じで、メンバーに Barbar() がいたとき、Sharpenにお任せすると

class Foo
{
    public class Bar {}
    public Bar Bar() { return new Bar(); }
}

と、メソッド bar()Bar() になって、そのせいでコンパイルに通らなかったりしました。 そこはレアケースだから許容するんだけど。

3番目のの注意点はジェネリクスワイルドカード

いや、変換はできるんですが

public boolean addAll(Collection<? extends Foo> col) {
    ...
}
public boolean removeAll(Collection<?> col) {
    ...
}

public virtual bool AddAll<_T0>(ICollection<_T0> col)
    where _T0:Foo
{
    ...
}
public virtual bool RemoveAll<_T0>(ICollection<_T0> col)
{
    ...
}

になって嬉しいかは自分で考えなければならないでしょう。 つまり、「その型 _T0 はほんとに必要なのか?」ってことです。

そして最後の注意点。変換後のC# コードには、おそらく virtual とか protected とか、そういう修飾子が山ほど入ってきます。これも、「その修飾子はほんとに必要なのか?」と考えたほうがいいでしょう。とはいえ、要らないからといって削除するのも面倒だったりしますが……

~~~~~~~~

と、長くなったのでこのあたりで。続く!(かも)