最小のコンピュテーション式

メモ。

使う規則

T(e;, V, C, q) = C(e;b.Zero())

この規則がvalidなコンピュテーション式を作れるはず。 Zeroメソッドのみを用意すれば良いのでBuilderの実装も最小限なはず?*1

コード

// 定義
type A() = member x.Zero()=()
let a = A()

// 実際に試す
a { () }

F# 4.0のfsiで確認。

  • ビルダーのクラス名に制限はない
  • 束縛名にも制限はない

なので一文字でも問題なく判別可能です。

公開後の追記

単にビルダーインスタンスを返すだけなのでこっちが正しいですね。 自分でxにしておきながら気付いてなかったです…失礼しました。

zeclさん、指摘ありがとうございました。

番外編

バッククオート2つで囲めばIdentifierにできるので、(たぶん)絵文字も可能です。

type ``🍣``() = member x.Zero()=()
let ``🍣`` = ``🍣``()

``🍣`` { () }

表示されない場合は環境の問題でしょう。

*1:と思っていたらミスしてました。追記部分を読んでください

.NET Fringe Japan 2016の個人的な振り返り

dotnetfringe-japan.connpass.com

運営及び発表者として参加しました。

運営といっても、勉強会運営に慣れている方が多かったのでやること・やらないことが初期から明確だった気がします。 当日の懇親会周りの運営を手伝えなかったのは申し訳なく思いつつ……。

発表内容

not_fsharp_deep_dive.md · GitHub

発表内容はぎりぎりまで悩みつつ、気がつけば難産だったらしいkekyoさんチキンレースを繰り広げていました。

最初はコンピュテーション式のコンパイラ側の話にしようと考えていましたが、朝からそんな話を聞かされるのはつらいだろうと判断して早々に却下。 次にFSharp.Compiler.Serviceに挑戦しようかな、と考えましたが、別の発表でRoslynがでてきそうな気配があったのでこれも見送り。 では自分らしくどういうライブラリを作ったかについて話すか……と思いつつ、いやいやnueccさんの発表があるからなぁ、みたいな。

そうした紆余曲折があった結果、F# がどういう機能を持ち、持たず、その上で具体的に何が作れるのかについてしゃべることにしました。 が、今考えるとFSDNの話は削ってF#とC#関係について話した方が良かった感ありますね。

F#は今の所、.NET Frameworkや.NET Core、C#とは縁を切れない関係です。 おそらく今後もF#は何かしらの影響を与え、そして与えられる立場であることでしょう。 そういう意味で、F#のRFC機能について紹介しました。 RFCはディープではないはず……と思っていたけど、今考えたら「策定段階の企画書読む」って十分ディープでしたね……。

あと、発表の途中で".NET Framework 2.0ではTaskの独自実装が〜"と口走ったのは完全に間違いで、正しくはCancellationTokenSourceです。 大変失礼しました。

全体

今回の登壇者が同じ場所に揃う機会、ないのではないでしょうか。 Partitionの話とか、なかなか聞けないので面白かったです(なお理解度は怪しいorz)。 あとは、本当にマルチプラットフォームだなーという謎の気持ちが大きかったですね。

時間が長丁場だったのは反省点か……とはいえ、もう少し大きな会場で2部屋使えれば解決できるとは思いますが。 そういう意味では、継続するには協力者を増やす必要がありますね。 まぁ、来年のことは来年考えましょう(とか言っているとすぐ来年になる)。

終わりに

異端と呼ばれているイベントに登壇する機会をいただけて、かなり満足しました。

ueberauth_qiitaとueberauth_hatenaを作った

久々にElixirネタ。

といっても表題がすべてを表していますが……。

https://github.com/pocketberserker/ueberauth_qiita

https://github.com/pocketberserker/ueberauth_hatena

ueberauthはElixir向けの認証ライブラリで、RubyのOmniauthに強い影響を受けているらしいです。 TwitterFacebookなど主要なサービスは一通り実装されているので結構便利です。

とはいえ、さすがに日本向けサービスの実装はなかったので、仕組みを調べるついでにQiitaとはてな用のものを作りました。

ueberauth_qiitaはhexにpublish済みですが、ueberauth_hatenaはとある依存ライブラリをscm形式で依存させている関係でpublishできていません (昔はそれでもhexにpublishできていたのだが、仕様が変わった?)

基本的にはueberauth_facebookやueberauth_twitterと同様の実装になっています。 まぁ、サービスごとに微妙に挙動が異なるので辛い気持ちになりましたが……「もう少し統一感だしてくれー」と叫びたくなりました。

特にこれといった技術的解説点もないので以上。

TypeProviderでFizzBuzzを取得可能な自然数型を生成する

コンパイル時に生成できますね、というだけの話です。

準備: FSharp.TypeProviders.StarterPackのインストール

NuGetからインストールしてください。

注意点としては、ファイルの定義順序がそのままだとコンパイルできない可能性があることでしょうか。

  1. ProvidedTypes.fsi
  2. ProvidedTypes.fs
  3. DebugProvidedTypes.fs

コンパイルできるはず。

型を生成してFizzBuzzを取得できるようにする

https://github.com/pocketberserker/MLStudy/commit/9a0a2795d5f8915cab89a5badf65d3abe17b1cf1

FizzBuzz1から100までと範囲が明確に決まっていますが、それでは面白くないので任意の範囲でとれるようにしましょう。 ProvidedStaticParameterでTypeProvider利用時に引数を渡せるようになるとかなんとか。 定義したProvidedStaticParameter群はProvidedTypeDefinition#DefineStaticParametersに渡すことで登録でき、第2引数で生成の操作を記述できます。

λ式内でやっていることは簡単です。

  1. FizzBuzzを計算し
  2. 自然数用の型を定義し
  3. FizzBuzzの結果を取得するプロパティを設定する(今回はFizzBuzzプロパティ)

使う側ではlet inline dump (value: ^n) = (^n: (member FizzBuzz: string) value) |> printfn "%s"という風に静的に解決された型パラメータを用いてFuzzBuzzを持つ値を出力できるようにします。

足し算がしたい

https://github.com/pocketberserker/MLStudy/commit/aee21c03a3d7e804c48e9c2fc13655c459645e40

数値なのに足し算できないのはおかしい、ということで足し算できるようにします。

  1. 数値と型のマッピングをもっておく
  2. 足し算で得た数値の型が存在するならop_Additionメソッドを定義、そうでないなら何もしない

こう改良することで、生成される範囲の自然数型で足し算ができます。 もちろんFizzBuzzも表示できます。

余談

ここで生成した各自然数型は共通するインターフェースを持ちません。 足し算程度ならインターフェースを用意しなくてもオーバーロードでごり押しできます。

発展

この状態ではリストを取得できません。 HListを用いれば良いはずですが、これに関しては読者の課題とします。

Twitterで流れてきたListReaderコンピュテーション式の解説

先日、Twitterid:n7shi さんが面白いコードを投下していた。

これに対しての私のreplyが以下。

これらのコンピュテーション式はちょっとわかりづらいのでちょっとした解説を試みる。

7shiさん版ListReaderBuilder

7shiさんのコードに少し手を加えたものが以下のコードである。 元コードとの挙動に差はない。

type ListReaderBuilder() =
  member __.Bind(_, f) = function
    | x::xs -> f x xs
    | [] -> ()
  member __.Zero() = fun _ -> ()
let listReader = ListReaderBuilder()
 
let test = listReader {
  let! a = () in printfn "%d" a
  let! a = () in printfn "%d" a
}
 
test [1;2;3]
printfn "---"
test [4]

このコードの出力は以下のようになる。

1
2
---
4

このビルダーで使われる変換規則は以下の通り。

  1. T(let! p = e in ce, V, C, q) = T(ce, V ⊕ var(p), λv.C(b.Bind(src(e),fun p -> v), q)
  2. T(do e in ce, V, C, q) = T(ce, V, v.C(e; v), q)
  3. T(e;, V, C, q) = C(e;b.Zero())

testを展開すると以下のようになるだろう(実際はもっと異なるかもしれないが、私はコンパイラではないのでわからない)。

// val test: int list -> unit
let test =
  // let! a = () in ... の展開
  listReader.Bind(
    (),
    fun a ->
      // do printfn "%d" a in ... の展開
      printfn "%d" a
      // let! a = () in ... の展開
      listReader.Bind(
        (),
        fun a ->
          // printfn "%d" a; の展開
          printfn "%d" a
          listReader.Zero()
      )
  )
  • Bindメソッドのシグネチャ'T * ('U -> 'U list -> unit) -> ('U list -> unit)
  • Zeroメソッドのシグネチャunit -> ('a -> unit) ((Bindの型パラメータと異なることを強調したかったのであえて'a表記にしている))
    • つまり、分解され残ったlistはZeroで返されるλ式によって捨てられる
    • testでいうと、2回しかBindを呼んでいないため、残った[3]がZeroで返るλ式に渡される
  • printfn部分でaはintに固定される

ことを考えれば、型に問題はないことに気づくだろう。

考察

面白いコードだが、個人的に気になったことがあった。

let! a = () in ...がなんだか冗長に見えるのだ。 do! ...のほうが短くて見やすい気がする。

いじってみた版

type ListReaderBuilder() =
  member __.Bind(g, f) = function
    | x::xs -> f (g x) xs
    | [] -> ()
  member __.Return(_) = fun _ -> ()
let listReader = ListReaderBuilder()
 
let test = listReader {
  do! printfn "%d"
  do! printfn "%d"
}

このビルダーで使われる変換規則は以下の通り。

  1. T(let! p = e in ce, V, C, q) = T(ce, V ⊕ var(p), λv.C(b.Bind(src(e),fun p -> v), q)
  2. T(do! e in ce, V, C, q) = T(let! () = e in ce, V, C, q)
  3. T(do! e;, V, C, q) = T(let! () = src(e) in b.Return(), V, C, q)

同じようにtestを展開してみる。

// val test: int list -> unit
let test =
  // do! printfn "%d" in ... の展開
  listReader.Bind(
    (printfn "%d"),
    fun () ->
      // do! printfn "%d"; の展開
      listReader.Bind(
        (printfn "%d"),
        fun () -> listReader.Return(())
      )
  )
  • Bindメソッドのシグネチャ('T -> 'U) * ('U -> 'T list -> unit) -> ('T list -> unit)
  • Returnメソッドのシグネチャ'a -> ('b -> unit)
    • 最終的に残ったリストはReturnが返すλ式で捨てられる
  • g xprintfn "%d" xが実行され、その戻り値unitfに渡る

こちらも型は問題ないことがわかるだろう。

やりたいことができるか試す

その後の7shiさんとのやり取りで、以下のようなことがやりたかったというコメントを頂いた。

不揃いなデータをコンピュテーション式で処理 - Qiita

改変後のコードでも同様の挙動にできるか試してみよう。

ビルダーとaddPersonシグネチャの順序、コンピュテーション式を書き換える。

// 変更したコードのみ載せています
type ListReaderBuilder() =
  member __.Bind(g, f) = function
    | x::xs -> f (g x) xs
    | [] -> ()
  member __.Return(_) = fun _ -> ()

...

let addPerson data name = if name <> "" then persons.[name] <- data

...

        row |> listReader {
            let! company = id
            do! addPerson (company, "会長")
            do! addPerson (company, "社長")
            do! addPerson (company, "副社長")
            do! addPerson (company, "専務")
        }

...

最初の値は他の場所で使いたいのでidで取得する。 残りはprintfnの時と同じ要領だ。

実際に試してみると同じ結果を返した。

おわりに

結果は同じでも、ビルダーの定義次第でコンピュテーション式の書き方が変わる。 他言語のマクロのように自由ではない限られた変換で何が表現できるのか、疑問は尽きない。

F# API検索サービス"FSDN"を作りました

公開から少し間が空きましたが、改めて周知ということで。

http://fsdn.azurewebsites.net/

このサービスは現在Azureで運用しています。 運用にあたり、株式会社オンザロード様にスポンサーについていただきました。

私がメンテナンスしていく限りはUNIXLinux環境でも動くようにしておくつもりです。 ただ、メンテナが変われば方針が変わるかもしれません。

検索対象ライブラリ、アセンブリ追加要望は https://github.com/fsdn-projects/FSDN/issues でお願いします。

FSharpApiSearchにべったり依存しているので、機能によってはそちらを先に拡張しないとFSDN側ではどうにもならない可能性が大きいのです。

技術的なお話はまた今度。

Babelの勉強と称してmuscle-assertを作ってみる

私は型がないと死んでしまう(コンパイラと相談しないと考慮漏れが多発して死ぬ)ので、JS系を触る場合は主にTypeScriptなのですが、とはいえ昨今の事情的にBabelを食わず嫌いするのなぁ…と思ったのでライブラリとそれ用のpuluginを作ってみることにした。

勉強前のスキル

  • Babel系の情報は追っていたが使うのは(ほぼ)始めて
    • 既に別の人が構築したものをちょろっと触ったことならある
  • TypeScriptは使える(詳しいわけではない)

方針

こういうのは強い方々の力を借りるに限る。

https://github.com/azu/power-assert-as-tool-demo

Babel pluginの知識は https://github.com/thejameskyle/babel-handbook を斜め読みしつつ、https://github.com/power-assert-js 下にあるbabel pluginを読み漁った。

作るついでに類似品チェック

Persimmon用アサーションライブラリMuscleAssertを作った - pocketberserkerの爆走

この記事の記憶がまだ自分の脳内に残っているうちに移植してみたかったので、今回はこれを題材にした。 一応類似品を紹介しておく。

https://www.npmjs.com/package/assert-diff

やっていることはだいたい同じだが、標準assertをベースにしたかったので自作しようという心持ちである。

あと、似たようなdiff表示はmochaであれば行ってくれる。 だからmochaであれば必要ない…のだが、eaterなどではそういうわけにもいかないと思う。

成果物?

https://github.com/pocketberserker/muscle-assert https://github.com/pocketberserker/babel-plugin-muscle-assert https://github.com/pocketberserker/muscle-assert-demo

Babel初で一日少々にしては私的には頑張ったほうじゃないですね…まぁ、参考にできるものがおおかったからだろうけど。

muscle-assertは標準のassert.deepStrictEqualのみを置き換える方針にした。 こうしたほうがシンプルだろうし、他のライブラリとも干渉しなくなるだろうと思った。 そういうわけでBabel plugin側も最低限の置き換えにとどめている。

使い方はデモプロジェクトをみればわかるはず。 表示がしょぼいのはプロトタイプなので勘弁してください。 バグっていたらそのうち直します。

ドキュメントがなさすぎるのは…本気で実装する気力が起きれば書く。

課題

なるべくpower-assertと干渉しないように作りたい…と思っていたのだが、以下の問題にぶつかった。

babel-plugin-espowerが先に変更してしまうのでテストがおかしくなる。 回避方法は今のところbabel-plugin-espowerのpatternsassert.deepStrictEqualをいれないという場当たり的なもの…なんとかしたいが、なんとかできるのかなぁ。

というわけで、今のところbabel-preset-power-assertとの共存は難しいのではないかと考えている。

おわりに

Babelは入門するだけならそんなに時間がかからないようだ。 power-assertの導入は記事が乱立しすぎているし古い情報がノイズになってTypeScriptのときはつらい気持ちになっていたが、Babel前提だとbabel-preset-power-assertがよろしくやってくれるので良いと思う。 とはいえ、楽になったのはあくまでBabelマンだけなのでTypeScriptの時にもう少しどうにかならないか考えてみたくもある。

babel-plugin-espowerのあれはやりたいことを考えると仕方がないのだと思う。 こういうのは後発ライブラリが頭をひねれば良い問題だと思うので、babel-plugin-espwerさんはそのままの強さで居てほしい。