函数型なんたらの集い 2014 in Tokyo

F#

函数型なんたらの集い 2014 in Tokyoに参加してきました。

発表資料はこちら。

内容は、Excel方眼紙を倒すためにDSLで頑張ったというものです。 この中で紹介しているTableDslは、いろいろと発展を考えているので「アレ使えばいいじゃない」は通じないのです。 あとそのアレ、Excel方眼紙吐けないじゃないですかー。

コメントで「いじられたくないならPDFにしてしまえばいいじゃない」というのがありましたが、Excelで提出することが決まっているのにそういうことできないですからね・・・

12月6日のNGK2014B 昼の部で、DSLとは違ったExcelの話をするので興味のある人はどうぞ。

F# Meetup in Tokyo

F#

今更シリーズその2。8月3日に、F# Meetup in Tokyoに参加してきました。

発表資料はこちら。

内容は、業務でF#を使った事例の紹介です。

F#オンリーのイベントにたくさんの人が参加してくれて、またいろいろな話が聞けてよかったです。 名前出していいかわからないから伏せますけど、LangExtを使っているという声も聞けました。 あんな変態ライブラリ、自分ら以外使わんやろー、と思っていたけど、実際に使われているとわかるとうれしいものですね。

またああいうイベント、やりたい!

クラウド温泉4.0@小樽 - The Return of F#

F#

今更シリーズその1。 7月の25から28日に、 クラウド温泉4.0@小樽 - The Return of F#に参加するために北海道に行ってきました。

発表資料はこちら。

内容はコンピュテーション式のyieldとreturnについての話で、

あたりの流れの一つのまとめになっています。 上であげた、「コンピュテーション式におけるreturnとyield」では状態引数による実装のみを紹介していましたが、 今回の発表資料の中では、

  • 例外による実装
  • 状態変数による実装
  • 状態引数による実装
  • 継続による実装

と、多数の実装方法を紹介しています。

また、FSharpxやExtCoreといったF#の有名ライブラリが、コンピュテーション式を正しく実装できていない点(yieldやreturn以前の問題)も明らかにしました。 詳しくは追っていませんが、実は標準のAsyncコンピュテーション式がそもそもダメという話も・・・ そもそも、コンピュテーション式を展開して考えている実装者はおそらく皆無であり、 ネット上にある間違った情報をもとにコンピュテーション式を実装している場合が多いように思います。 モナドモナドプラス以上の表現力のものを実装する場合は、せめてコンピュテーション式の展開規則を理解したうえで実装しましょう。

沖縄に行ってきました!

etc

社員旅行で沖縄に行ってきたので、その報告です。

f:id:bleis-tift:20140924093102j:plain

http://f.st-hatena.com/images/fotolife/b/bleis-tift/20140924/20140924093102_original.jpg

お分かりいただけただろうか?

f:id:bleis-tift:20140924093128p:plain

_人人人人人人人人人人人人人_
> Javaプログラミング講座 <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

報告は以上です。

Ruby嫌いがアンダースタンディングコンピュテーションを読んで

一番最初にはっきりさせておきますが、Rubyは嫌いな言語です。 が、この本はRubyが嫌いな自分でもいい本だと言える*1本でした。 自分が対象読者に入っているかどうかは実際に読んでみるまで微妙かな、と思っていましたが、とても楽しめました。 以下、書評です。

Rubyという選択

説明用のコードとして本書はRubyを使っていますが、 これに関してはその理由が1章にあります。

私はその明瞭さと柔軟さに魅かれてRubyを選びました

また、続けて

本書にはRuby独自の機能に依存しているところはありません。 そのため、もしあなたにとってわかりやすくなるなら、本書のサンプルコードをお好みの言語、特にPythonJavaScriptといった動的言語に変換してもよいでしょう。

とあります。

ここで注目すべきは「特にPythonJavaScriptといった動的言語」という風に、静的型付き言語を省いている(少なくとも積極的に推奨はしていない)点です。 で、これにはまっとうな理由がある(少なくとも自分は納得できる)のですが、この説明だけだとそれが全然わからないんですよね。 その理由をここに書いてしまうのは、ある意味この本を読む楽しさの一部を削いでしまうことになりかねない気もするのですが、そうなったとしてもこの本は依然として面白いであろうということで、書きます。 もちろん、「こういう理由があるからだ」という風に書きますが、すべて想像です。

対話環境があり、モンキーパッチングが可能であり、定数が削除できる

この本の素晴らしい点の一つに、実際に対話環境で一つ一つコードを入力していき、動作が確認できるというのがあります。 対話環境がない言語の場合はいちいち実行し直す必要があり、しかもこれまでに見てきた最早不要の出力(ようはゴミ)も表示されてしまいます。 面倒に思うかもしれませんが、きちんと理解したい方はRubyの環境を整えて*2、実際に対話環境でコードを入力して結果を確認しつつ読み進めるのがいいでしょう。

また対話環境前提で構成されているため、「以前定義したクラスのコードに戻ってこのメソッドを追加しましょう」ということができません。 そのため、モンキーパッチングができるような言語でないと実際に試しながら読むのはつらいです。

更には、「以前定義したクラスのコードに戻ってこの部分を書き換えましょう」もできません。 なので、定数を削除(というより、クラスを削除)できる必要があるのです。

もちろん、モンキーパッチングの代わりに別のモジュール内に既存のモジュールと同じものを定義してそれをopenするだとか、定数の削除の代わりにシャドウイングを使う等、このあたりはこの機能そのものがないと厳しいというわけではありません。

evalを持つ

eval的な、渡されたコードを実行する仕組みが欲しくなる場所があります。 本書で変換対象として挙げられていた言語(や、動的言語の多く?)は、eval的なものが用意されています。 これが気軽に使える言語でないと、evalを自分で実装・・・ということになりかねません。 すると当然のことながらすべてを本文に載せることは出来なくなり、本文のコードで試せないものができてしまいます。

もちろんこれにも回避方法はありますが、説明をシンプルに保つ、という点において本書がRubyのような言語を採用したのは正解だったと思います。

ちなみに、ここで挙げた理由を持つPythonJavaScriptが本書の言語として選ばれなかった理由としては、著者の好みもあるでしょうが、一番の理由はP.188からP.191な気がしています。

クラス分割の教材という観点

Rubyメタプログラミング的な技法などを使えば本書のコードはよりDRYに書けるでしょう。 ですが、それをすることなく一般的なOOPLで扱える程度の記述にとどめているため、他のOOPLでもクラス分割のひとつのお手本としてみることができます。

そういう観点でのオススメは3章です。 3章をすべてやると(正確には3.3まで)、基本的な機能をもった正規表現の処理系が手に入ります。 正規表現の処理系を「じゃぁ作って」と言われても、難しいと感じるプログラマは多いと思います。 3章では、それをボトムアップで作っていきます。 この際のクラスの分割方法や命名のセンスは素晴らしいです*3

3章の動機づけの弱さ

3章はコードは素晴らしいのですが、構成にもったいなさを感じました。 最終的には基本的な機能を持った正規表現の処理系が手に入るにもかかわらず、導入部分には

計算する機械というアイディアにある本質を明らかにし、それがどんな用途に使えるのか紹介しながら、単純なコンピュータにできることの制限について調べます。

としかないのです。 3章の最初の方はこの本の対象読者にとって「これが一体何に使えるんだ・・・」という感じの話が続きます。 人によっては退屈に感じて途中でやめてしまうかもしれません。 なので、個人的には最初の方に「この章では単純な例から始め、基本的な正規表現の処理系を作り、単純なコンピュータでも役に立つことを示します」くらいの人参をぶら下げた方がよかったのではないかな、と思います。

6章のバランス

この本は非常に内容の詰まった本であり、技術書の中ではページ数も少な目か普通程度のものです。 これだけの内容をこれだけ分かりやすくこれだけのページ数に収めているのは驚異的だと思うのですが、6章は少しアンバランスな気がしました。 もう数ページ増やして、リストの説明をより詳細に行ってもよかったのではないかな、と思うのです。 これまで丁寧に解説をしていたのに、リストでは解説なしに5つの関数が与えられてあとはその動作の確認にとどまっています。 本書のもったいないと思ったところで一番大きな部分が(言語の話ではなく)ここでした。

日本語としての読みやすさ

とても読みやすかったです。 「的」をあまり使わない方針なのか、一部ひっかかりを覚えた部分もありますが、好みの問題でしょう。 何か所か原文を確認したくなったところはありましたが、とても少ないです。 意味がよく分からないな、と思った個所は一つだけです。 P.169に、

ブール値を将来のコードが読める決まったデータとして考えるのではなく、2つの選択肢を引数として呼び出し、1番目と2番目の選択肢のどちらかを選ぶコードとして、直接的に実装しましょう。

とありますが、この前半部分の意味が分かりませんでした。 ただ、ここが分からなくても後半部分だけで実際にやりたいことはわかるため、それほど問題とも思いませんでした。

本書を読むために必要なレベル

人は自分がわかっていることに対してそのレベルを低く設定しがちな気がします。 「わかっている人にはわかる説明」というのはいくつもあり、分かってしまえばその説明で確かに十分だ、と思ってしまうことはそれなりにあります。

自分はある程度知識がある状態でこの本を読んでいるため、本書を読むために必要なレベルを本来よりも低く見積もってしまう可能性があります。 という言い訳を置いたうえで、この本を読むために必要なレベルはそこまで高くないと感じています。 ただし、最初の方にも書きましたが「実際にコードを試しながら読む」のを前提として、です。 逆に、実際に試さずに「難しかった」というのは、この本の読み方としては難易度の高い方法を選んだからかもしれませんよ?

汚れ?

P.210ページの右下付近、「partial」の「a」の上にななめ線が入っていました。 自分の周りの3冊を確認したところ、3冊すべてで入っていたので、多くの本で同じようなものが入っているでしょう。 内容には関係ない話なのでこれがあるからと言って本書の価値が下がるわけではありませんが。 むしろ、将来直った場合には自慢ポイントになるかも?

全体

全体を通して、「そういう説明をするのか、なるほど」と感じた個所が多い本でした。 これまで理解できていなかった部分が理解できるようになったり、新たな知識(薀蓄的なもの)が知れたりして勉強にもなりました。 Ruby嫌いは直りそうにありませんが、この本は好きになりました。

追記

サポートページはGithubなんですね。 そして、Issue 1に登録されていました。

次に読む本Wikiとしてまとめられているのも素晴らしいです。

*1:Rubyを使っているというだけですでにマイナスポイントからのスタートであるにも関わらず、いい評価

*2:好きな言語にコンバートしつつ、でもいいでしょうが、その場合でも本文を読み飛ばさずに進めた方が楽しいと思います。

*3:ただし、正規表現の処理系としてのクラス分割や名前付けが素晴らしい、という話ではないです

TypeProviderについて、勝手に補足

F#

昨日行われたF# Meetup in TokyoのPart 1@kos59125さんがTypeProviderに関する素晴らしい発表をしてくれました。

TypeProviderを作りたい場合に、最初の入り口として素晴らしい発表資料だと思います。 何より、日本語で読めるのがすばらしいですね!

この資料について勝手に補足します。

ProvidedTypes.fsについて

ProvidedTypes.fsというのは、TypeProviderを作るうえであると便利なものを提供してくれる素敵なファイルです。 正直、これがない状態でTypeProviderを作るのは至難の業であり、事実上必須のファイルです。

しかし、このファイルは資料にもある通り、F# 3.0 Sample Packの中にあるものをTypeProviderを提供する各ライブラリがコピーして使っているという状況です。 さらに、各々がこのファイルをいじくり、独自の進化を遂げているような状態になっています。 これは、F# 3.0 Sample Packのリポジトリが全然更新されないので仕方ないと言えば仕方ないです。

この状況を打破する可能性となるプロジェクトとして、FSharp.TypeProviders.StarterPackというものがあります。 このプロジェクトではNuGetパッケージを提供しており、NuGet参照としてこれを追加するだけでProvidedTypes.fsが自分のプロジェクトに追加されます。 まだ試してはいませんが、NuGetパッケージの更新をすれば、最新版に置き換えてくれたりするのでしょう。

消去型の使い道

20枚目に載っている「消去型」ですが、発表では「ほとんど使わないのでunitでもいい」という風に説明されていました。 こいつの使い道ですが、例えばExcelHouganshiTypeProviderでは

ProvidedTypeDefinition(asm, ns, typeName, Some typeof<ExcelFile>, HideObjectMethods = true)

引用: Houganshi.fs

のように、ExcelFileという型を指定しています。 こうすると何が嬉しいかと言うと、

type MyHouganshi = Houganshi<"Def.fs">

let h = MyHouganshi("sample.xls", NPOI.NpoiBook.Load)
(* hを使った操作 *)
h.Save()

と書けます。 最後の行のSaveメソッド呼出しは、ExcelFileが持っているメソッドです。 全てをProvidedMethodで定義するのではなく、生成する必要のないものは消去型に指定した型に定義すると便利です。

他にも、既存の型を元にしたTypeProviderを作る場合、既存の型を受け取る関数にTypeProviderによって提供された型のインスタンスも渡せると便利ですよね。 そういう場合には、既存の型を消去型として指定することになります。

生成型について

TypeProviderには消去型と生成型という2つの種類があります。 発表されていたのは、消去型についてでした。 自分も最近までは消去型のTypeProviderしか作っていなかったのですが、 最近生成型の有用さに気付いたので書いておきます*1

消去型の欠点

多くのケースでは、消去型で問題ありません。 しかし、消去型は「型が提供される」といってもILレベルではその型は消えているので、 「提供された型」自体やその型情報が欲しい場合は消去型では対応できません。

そこで使えるのが生成型のTypeProviderです。

生成型のTypeProviderの作り方

参考になるのは、F#の標準ライブラリとして提供されているEdmxFileや、SqlDataConnectionの元となる、 DataProvidersです。 このTypeProviderは、一つのTypeProviderで複数の似たTypeProviderを提供するような構成になっており、このテクニックは消去型でも参考になると思います*2

生成型のTypeProviderで提供される型は、実際にどこかのアセンブリに存在する必要があります。 DataProvidersでは、最終的にcscを呼出すことで実際にdllを作り、 生成したdllを読み込んでいます

他にも、ProvidedTypeDefinitionIsErasedプロパティをfalseに設定する必要もあります。

生成型のTypeProviderの活用例は、アイディアがあるので近いうちに見せれたらな、と思います。

*1:ちなみにVisual Studio 2012がVisual Studio 11 Developer Previewだったころには、情報がなさ過ぎて生成型の方を使っていました。そのときとはGenerateという属性が型を提供される側で必要だったのですが、現在はこの属性は消えているようです

*2:ただし、その場合ProvidedTypes.fsのTypeProviderForNamespacesを継承せずに、ITypeProviderやIProvidedNamespaceを自分で実装する必要がある

値制限

F#

なんかTLで偶然そんな話題があったので。 コードはF#です。

まずrefについて

値制限の話題に行く前に、refについて説明します。 refは、「変更可能な値」を実現するために使える型で、大体こんな感じに実装されています。

type 'a ref = { mutable contents: 'a }
(* refの生成 *)
let ref x = { contents = x }
(* refが格納している値を参照 *)
let (!) x = x.contents
(* refが格納している値に再代入 *)
let (:=) x y = x.contents <- y

これによって、以下のようなコードが書けます。

let a = ref "hoge"
printfn "%A" !a

a := "piyo"
printfn "%A" !a

これを実行すると、hogeとpiyoが出力されます。 aの指す値が途中で変わっているということですね。

では、こんなコードは許されるでしょうか?

let xs = ref []

これが許されるとするならば、xsの型は'a list refとなるでしょう。 ではさらにコードを追加してみましょう。

(* xsの型が'a list refとするならば・・・ *)
let xs = ref []

let f (ints: int list) =
  (* xsは'a list refなんだから、これは合法 *)
  xs := ints

let g (strs: string list) =
  (* xsは'a list refなんだから、これは合法 *)
  xs := strs

あるぇー?fを実行したらxsの中にはint listが、次にgを実行したらxsの中にはstring listが入っていることになってしまう! 静的な型とはいったい何だったのか・・・

値制限とは

と言うことで、この問題を回避するために値制限と言うものがあります。 要は、

let xs = ref []

を自動ジェネリック化しない、ということですね。

(書きかけ。あとで何か書くかも?)