"ValueWithName"や"Auto-Quotation of Arguments at Method Calls"を使って変数名や関数名を取得する #FsAdvent

この記事はF# Advent Calendar 2015の一日目の記事です。

Expr.ValueWithNameの話

以下のようなコードがあるとします。

let f a = <@ a @>
let hoge = "Hi!"
f hoge

これはどう表示されるでしょうか?

F# 3系までは以下のとおり(VS2013のF# interactiveで確認したのでちょっと正確な言い方ではないかも)。

Value ("Hi!") {CustomAttributes = [];
               Raw = ...;
               Type = System.String;}

F# 4.0からは次のようになります(VS2015のF# interactiveで確認したのでちょっと正確な言い方ではないかも)。

ValueWithName ("Hi!", a) {CustomAttributes = [];
                          Raw = ...;
                          Type = System.String;}

Expr.ValueWithNameとなり、束縛名が取れるようになりました。

ReflectedDefinitionとメソッド呼び出しでの引数の暗黙的なauto-quotation

https://github.com/fsharp/FSharpLangDesign/blob/5cec1d3f524240f063b6f9dad2f23ca5a9d7b158/FSharp-4.0/AutoQuotationDesignAndSpec.md

上記ドキュメントを読めばわかるとおり

static member Plot([<ReflectedDefinition>] values:Expr<int>) = ...

と定義してあげると

Chart.Plot(f x + f y)

このようなコードは下記の形に暗黙的に変換されます。

Chart.Plot(<@ f x + f y @>)

ReflectedDefinitionのIncludeValueとExpr.WithValue

先ほどのドキュメントにも書かれているとおり

static member Plot([<ReflectedDefinition(true)>] values:Expr<X>) = ...

IncludeValueをtrueにすることで、quotationに変換する際に

Chart.Plot(Expr.WithValue(f x + f y, <@ f x + f y @>))

Expr.WithValue(obj, Expr)の形に変換されるようになります。

Persimmon.Driedによる利用例

いきなり利用例の話…の前に、ちょっとした問題提起から。

今年に入って実装を進めているライブラリの一つにPersimmon.Driedというものがありますが、下記のブログにも述べられているとおり弱点が存在します。

Persimmon.Dried で性質をチェックする — a wandering wolf

今回の話に関連する部分を以下に引用します。

1つは apply した先でテストに失敗した時に、どの apply で失敗したかが明確ではない点です。大体はどこで失敗したか予想はつきますが、1つの property ブロックに複数の apply を入れていると、失敗結果から自明には分かりません。どの Prop が失敗したか、名前が分かるととても捗りますね。

具体的なコードはFuncyというライブラリのテストがわかりやすいと思います。

https://github.com/Gab-km/Funcy/blob/55b9ff30b6508b9089448ea4e54776450fc35af4/Funcy.Test/ApplicativeLawsCheck.fs#L80

Persimmon.DriedのPropnにはラベルをつける機能があるのですが、今回のように変数束縛するのであれば変数名からいい感じに名前を取得して重複させないようにしたいですね? というわけで先ほど紹介した機能の出番となります。

成果物

コードは下記で公開しています。

github.com

ReflectedDefinition、WithValue, NameWithValueを使って名前を取得する

まず、既存のPropertyBuilderには手を入れたくないので別ライブラリとしてコンピュテーション式のビルダーをラップします。 その後、該当部分のメソッド引数にReflectedDefinitionをつけていきます。

// 定義部分だけてきとーに抜粋
member __.Apply<'T, 'U when 'U :> Prop>(s: PropertiesState<'T>, [<ReflectedDefinition(true)>] expr: Expr<'U>) =
member __.ApplyReturn(s, [<ReflectedDefinition(true)>] expr: Expr<Prop<'T>>) =

使う側はモジュールを一つオープンするだけで、変数名がラベルとして表示されるようになります。

実際に名前を取得する部分ですが

https://github.com/persimmon-projects/Persimmon.Dried.Quotations/blob/afc8893b0674902b95c8a08e5e70293e284b75d0/src/Persimmon.Dried.Quotations/Quotations.fs#L15

アクティブパターンを使ってひたすら地道に名前を取得しています。 また、引数の型をExpr<'T>にしておけば値は'Tなことが確定するので、安心してWithValueの第一引数やPropertyInfoで取得できるobjを'Tにダウンキャストできます。

おわりに

Expr は 4.0 になってさらなる可能性をもたらしてくれました。 これらの機能とコンピュテーション式を組み合わせると無限に悪巧みができそう有効活用ができそうなので、もっと使い倒してみたいところですね。

おわりにその2(追記)

Persimmonを実装しているプロジェクトのGitter日本語部屋があるので興味のある型はぜひ。

gitter.im

Scala.jsに対応したscodec-msgpackをリリースしました

この記事は予約投稿です。

id:xuwei さんが ささっと作業してくださったのでリリースしました。

https://github.com/pocketberserker/scodec-msgpack/tree/v0.4.1

v0.4.0はプロジェクトの設定ミスにより空のjarが降って来るだけなのでスルーしてください…。

なんでscala-jsに対応できたのか

  • shapeless は Scala.js をサポートしている
  • scodec-bits も Scala.js をサポートしている
  • 依存している二つのライブラリが Scala.js をサポートしたので、 scodec-core も v1.8.3 から Scala.js をサポート開始
  • scodec-msgpack は scodec-core のみに依存する、かつそんなに変なことはしていない
  • よってプロジェクト設定とテスト部分のみコストをかければ問題なく対応可能

といった形です。 shapeless が Scala.js をサポートしているのはなんというか、すごいですね。 マクロ的とか楽に対応できるものなのだろうか…?

TypeClassを使うようにした

ライブラリを作っておいてアレですが、私自身がscodec-msgpackを使っているというわけではないのでテキトーでいいかなと思ってメンテナンスをさぼり気味でした。

しかし、下記issueがきたので

Make primitive `Serialize`s implicit · Issue #8 · pocketberserker/scodec-msgpack · GitHub

重い腰をあげてimplicitな形式に変更しました。 変更ついでにshapeless.TypeClassも使うようにしたのでそれなりに自動生成できます。 インスタンス増やさないといけないのは…ぼちぼちやろうと思います。

その他

  • Extended -> Extensionにリネーム(破壊的変更なのでついでにやった的な)
  • FastOptJsでメモリをドカ食いするのでsbtoptsのmemをちゃんと設定しないとテストでstackoverflowします
  • Scala.jsのプロジェクト設定、特にクロスビルド設定をやったことがなかったのでちょっとしたところではまって難しい…
  • 次にメジャーバージョンにあげるあたりで1.0.0にすると思います

Elm の automaton を F# に移植した

おはようございます、ライブラリ移植するマンです。

つい先日、関数型ストリーム処理勉強会 - connpassという勉強会がありましたねというタイミングで、ElmのFRPライブラリが情報網にひっかかったので移植しました。

https://github.com/evancz/automaton

automaton is 何

ここでは "Arrowized FRP の実装の一つ"、という一言で終わらせてしまいます。 詳しくはリンク先のコード見たり論文読みましょう。

移植コード

https://github.com/pocketberserker/FSharp.Automaton

もう少し名前ひねるべき、という話もありますがまーそこはリスペクトということで。

雑多な話

  • なんで Elm のライブラリ?
    • higher kinded types をサポートしていないので移植しやすい
    • Arrow を明示しているわけではないので移植しやすい
    • Elm の Signal を .NET の System.IObservable で読み替えれば移植が楽そうだった
  • 別に移植しなくても System.IObservable とか Rx でいいじゃない
    • 趣味の問題
  • テストは?
    • あとでかく(ダメじゃん)
  • サンプルコードは?
    • あとでかく

Elixir入門するついでにelixir-supervisordというライブラリを作った

だいたいのことはErlangでいいんじゃないかなと思いつつ、まー触ってみないことには何とも言えないよねというのもあり、今回重い腰をようやくあげた。

触り始めて一週間。

ついでにライブラリ作った

https://hex.pm/packages/supervisord

supervisordのXMLRPC叩くだけの簡単なライブラリを作った。 supervisorという名前の影響で検索しづらい…

Elixir とか Phoenixとかの雑感

まだ一週間だからなんともではあるが

  • パイプライン演算子、第一引数が対象なのかー的な
    • F#erとしては最後の引数を対象にしてほしかった
    • けど、統一的という意味では第一引数なのだろうなぁ
    • まぁ、パイプライン演算子便利ですね
  • dialyzerなしで使ったらやはりつらい
    • パターンマッチがある分いくらかRubyとかより安心感あるけど、それでもね
    • でもdialyzer重いんだよなーあーうーとか言ってみる
  • PhoenixRailsぽいだけはあるなーと
    • ElixirとPhoenix触り始めて一日くらいで最低限の目的を達成できたので
    • とはいえ外れたことやろうとすると苦労するんだろうなぁ…
  • Phoenixが標準にしてるbrunchつらい(JavaScriptじゃないか!という突っ込みはなしで)
  • Erlang OTPべんきょーしておいてよかった
    • relxからのexrmとか、rebar3触ってからのmixとか、わりとさらっと入れる気がする

次の目標はマクロかな…どの言語でも黒魔術系苦手なので手になじむかびみょーだけど…

Persimmonはユニットテストを分散実行する夢をみるか?

  1. みるかもしれない。

どういうこと?

かいつまんで問題を説明すると

という話。

今回採用したもの: 案1

コードというか題材というか

https://github.com/persimmon-projects/Persimmon.Ripe/tree/fee25ceabae567cb21e110fe2df2b4f07b72286c

テストオブジェクトのシリアライズ、assemblyロード

別サーバでテストを実行するためには、テストオブジェクトに関連するassemblyをロードする必要がある。 仮にロードしないでテストを実行した場合は例外投げてテストに失敗する。

しかしどうやってロードするかね、という話の段階でVagabondの出番となる。

https://github.com/nessos/Vagabond

このライブラリはリモート環境でAssemblyをロードしたり色々できる優れものである。 また、通信することを想定してFsPicklerというシリアライザ*1を用いてオブジェクトをシリアライズする。

現時点でのPersimmn.Ripeは、Vagabondを使ってAssemblyやテストをRabbitMQ越しにやり取りするライブラリを提供している。

  1. 入力のdll群をロードしてテストオブジェクト一覧を取得する
    • ここまでは通常のPersimmonと同じ
  2. テストオブジェクトに関連するAssemblyをVagabondで取得、RabbitMQに投げる
    • サーバ側はテキトーに受け取ってAssemblyをロードする
  3. テストオブジェクトをシリアライズしてRabbitMQに投げる
    • インターフェースだとキャストに失敗して原因もよくわからなかったのでTextWriter -> objでやり取りしてる…
    • TextWriterを渡す理由は実行したテスト名を出力したいから
  4. 各サーバがメッセージをconsumeしてテスト実行、結果をRabbitMQに投げる
    • 失敗したらエラーをメッセージとしてなげる
  5. 全体統括者がテスト結果を収集して、全部取得できたら結果表示
    • エラーだったら規定回数リトライ、それでもダメなら自分で実行するとかもやってる

この方式のキモ

  • Vagabondが行っているAssemblyの解析とロード
  • FsPicklerが行っている、IL Emitを前提としたシリアライズ

この二つのようなことができれば他の中間言語を持つやつでもわりとなんとかできそう。

課題

Vagabondが対応できる範囲でしか実行できない。 実際、RabbitMQではなくAzure Service Busに切り替えて試してみようとしたところ、VagabondがAssemblyをロードできず死ぬという現象が発生した。

案2を採用しなかった理由

  • コンパイルする環境整えるのつらくない?
  • 全体統括者がWorkerとなるサーバ群を知らないといけないとかそこそこ面倒
  • プロジェクトファイルのsyncだけでリソースもってかれそう

今後の予定とか

  • VagabondでロードできるAssemblyを増やせないか試す
  • AMQP以外のものにも対応させる
    • 例えばアクターとか
  • ちょっと気になっているRubyのtest_queueさんについて調べる
  • Scalaで似たようなことができないか考える

*1:こっちもnessos製

#fsharp 用テスティングフレームワーク Persimmon 1.0.0 をリリースしました

https://github.com/persimmon-projects/Persimmon/releases/tag/1.0.0

https://github.com/persimmon-projects/Persimmon.Dried/releases/tag/1.0.0

https://github.com/persimmon-projects/FAKE.Persimmon/releases/tag/1.0.0

Testing Framework Meetingで話にだしてたやつの F# のほうです。

first commit 一年からそろそろ一年だし、取り急ぎ入れたいものとかもなかったし、いつまでもbetaなのは微妙な気持ちになるので思い切ってリリースしました。

Persimmon 自体には デモプロジェクトがあったり Persimmon.Dried にはリポジトリ内にサンプルがあるので、使い方はそっち見てくださいということで。

姉妹ライブラリ

今後

  • VS拡張ェ…
  • Persimmon.Dried
    • いくつかの型がnullを生成していないので、nullも生成するようにする
    • そのうえで、NonNullに楽に切り替えられるようにする

Persimmon使ってるプロジェクト

順不同

java-ja.OSSに参加

初のjava-ja参加でした。

java-ja.OSS - connpass

なんというかよく分からないテンションなので書きなぐってみます。

(遊びで書いた)コードの保管場所としてのGitHub

PC移行するときに面倒臭いしGitHubに置いてそういうリポジトリの通知を無視する勇気をもつ、とかそういう話が途中に発生していた。

これは私もある種同意見というか、私はもっとアレな表現として"GitHubはある種のゴミ箱"という考え方を持っている。 (会場でその発言したのも私なので謝る必要がある。アレな表現でごめんなさい)

私は仕事以外のコードはだいたい暇つぶしとか遊びで書いている感覚で*1、まぁPCを交換するかもしれないしなんか捨てるのももったいないので、どこかに退避させたいときにGitHubやBitbucketが楽だからpushするわけですね。 でまぁ、ここまでならアーカイブとかかっこいい言い方をすればいいのかもしれないですけど、他人が見るわけでもない、自分が見返すこともほとんどない、そんなコードが放り込まれている。 このただの投棄をカッコよくいう気は私はしないという話なだけです。

2015/10/06 13:00頃の追記

私にとっては、なので別に他の人にその定義を押し付けるつもりはありません。

GitHubでのコミュニケーション云々

したくないというか、英語できないマンとしてはno descriptionでpull request送ったり5単語以内に収めるとかよくわからないことしてます。

当日聞きたかったけど聞けなかったこととしては

no descriptionのpull requestが飛んでくるとどんな気持ちになりますか?

というのがあったが、まー本題ではないしいいや。

コミュ障とかパートナーとか

コミュ障というものにはいくつかあると思っていて、今回の場合だと

  • 見知った人にはそれなりに受け答えできる
  • 興味があること以外に関する会話の組み立てができない
  • (初対面だとだいたい「あー…」とか「えーと…」という反応になりがち)

とかそういう意味でのコミュ障ではないかと想像している。 だから勉強会運営などは知り合いメンバーで固めればそれなりになんとかなるし、ある程度定型文での受け答え+興味ある話での受け答えでなんとかなるのではないか、と。


パートナー云々の話は、パートナーという存在がいない身としては「ふーん、大変そうですね」以上の反応しかできなさそうです…。 いやまぁ、この自由こそがパートナーがいないことと引き換えの何かなのだろうとは思いますけど。

枯れたOSSの件

戦略的にはまだいくつかあると思いました。たとえば

  • 「私はつよい。ので、このOSSは完成している。疑問があるならここで質問どうぞ」と書く
  • 信頼度の問題?
    • 例えば定理証明支援器(Coqとか)で証明されたリポジトリに対して「最終更新が昔だから」とかの理由で使わないってあるのかな、みたいな

週末に自分のプロダクトを頑張る

目的があってやってるとこういうことになるのだなーということがしれてよかった。

目的なく気が向いたら自分のプロダクト触っているマンなのでたぶん縁のない話でしょう…。

自己鍛錬としてのOSS

なんか、つよいなーという感想(小並感

前述の通り私は休日は暇つぶしととか気が向いたらコードを書いている面が強いので、たぶん筋トレ的な開発は向いていないだろう。 気が向かないのにやろうとしてもストレスにしかなりそうにないと言い訳しつつ。

プログラマとしてOSSと関わりながら生き残るためにとった生存戦略 - scalaとか・・・

そういえば、上記記事も似たような理由でつよいなーと思ったのでした。 まぁ、私はOSS開発者という括りには入っていないと思われるので生存できてないわけですが。

余談: 暇つぶし道具としてのGitHub notifications眺め

昔からmixiのなにかとか、Yahooニュースのコメント欄とか、twitterとかを数時間(長ければ丸一日)飽きもせず(かといって楽しむわけでもなく)ぼーっと眺めて人生の何割(?)かを過ごした身としては、ある意味GitHub notificationsは暇つぶし手段として良いと思っています。

ただリアルタイムに追っているわけではなく、メールで飛んできているnotificationを、時系列の新しいものから順に気の向くまま目的もなく眺め続けるだけなので、結果として役に立っているかどうかは不明ですね。 ザ・時間の無駄遣い。

感情とOSS(?)

目的あって開発している人はパワーが違うなと思ったり、というか感情爆発させたあとでもきちんとリカバリーできているのすごいなと思いました。 私だったら間違いなく後悔で2週間ほど引きずるので感情で動けない…。

独裁とOSS

仮にscalazコミュニティ崩壊の元となった方にLinus氏やDHH氏のようなカリスマ?(他に単語を思いつかなかった)みたいなものがあれば、ことなる結果を歩んでいたのだろうか。 いやでも、Scalaってそういう優しい(?)独裁者をなかなかみない気がするのでScalaというコミュニティ的に避けられなかった話なのだろうか?

*1:pull requestは例外