SmlSharpContribについて

これはML Advent Calendar 2015関数プログラミングAdvent Calendar 2015の一日目の記事です。

えすえむえるしゃーぷって?

SML# - SML#のダウンロード

関数プログラミング交流会で調査した感じだと、知っている人は増えつつあるがユーザはまだそんなにといったところなのかもしれません。

SmlSharpContribとは

ざっくりいうと「準標準パッーケージを作ろう」みたいな感じでしょうか。

が現状で提供されている機能です。

「え、その型すら標準にないの?」などと言ってはいけません。

ライブラリを作るという話になった経緯

2015年の2月頃に、大阪の知り合いが仕事の都合で名古屋に来るということで何名かでご飯を食べに行きました。 そこでなぜか(話の流れは忘れた)みずなんとかさんとぶれなんとかさんが「SML#の準標準ライブラリが欲しい」と言い出し、何人かを巻き込む形で始動しました。

なんか開発止まってるようにみえるけど…

みなさんお忙しいですからね、決して興味がなくなったとかいうわけではないはず…ええきっと。

もみあげはなにをしたの?

パーサコンビネータJSONパーサなどを作ってました。 XMLパーサも実装しようとしていましたが体力切れで絶賛放置中…。

おわりに

SML#は楽しい(はずな)ので、みなさんもSmlSharpContribに貢献するという形でSML#を触ってみませんか?

"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使ってるプロジェクト

順不同