読者です 読者をやめる 読者になる 読者になる

"なぜshapelessにはGeneric[Boolean]がないの?"と尋ねてみた

Gitterで質問したのでそのメモ*1。 なお私の英語は壊滅的なので片言?なのは許してください。

Hello. Why does Generic[Boolean] instance not exist?

と投稿したところ、milessabinさん(shapeless作者)から

Because there's no obvious sum of product representation of Boolean.
I guess you would want false.type :+: true.type :+: CNil?

と返答があった。なるほど。

@milessabin Thank you. I understand.
I try to port from Haskell code(using GHC.Generic) to Scala code. GHC.Generics derive some instances of Generic a.

とりあえずこういうことしようとしたんだよーと言ったところ

@pocketberserker gotcha.
The idiom here is to hand-write instances for primitives and use Generic for ADTs.
I would be very interested to see a side-by-side comparison of something written with GHC generics and something written with shapeless.
Would you be able to blog about it?

ほえーと思ってたらなんか後日記事かくことになった。

まぁ、最初から無理っていうのもなんだし、やるだけやってみよう…。

*1:という名のバックアップ

「Persimmonでテストを書く」での疑問点に回答してみる

F#

Persimmon でテストを書く — a wandering wolf

上記記事で2点ほどあがっていたものがあるので。

Usingがない

検証さぼってただけです、すみません。pull requestだしたのでそのうち入るかもしれません。

enable use keyword by pocketberserker · Pull Request #85 · persimmon-projects/Persimmon · GitHub

ただ、useで束縛した値をreturnすると思わぬ落とし穴にはまると思うので、Disposableな値は返さないように気を付けたほうが良いと思います。

Zeroがない

Zero、わりと影響範囲が大きいので悩みどころですね。

単純にunitな式を実行したいなら

// hoge: unit -> unit
return hoge ()

もしくは

do hoge ()
return ()

と書けばよいと思います。

面倒くさいなら

type TestBuilder with
  member __.Zero() = ...

と型拡張を書くとかですかね。

あの時を振り返る

ふと、 初回TDDBC名古屋 が開催されてから5年が経過したことに気がついた。

あの時はブログをやっていなかったので感想記事を書けなかったが、良い機会だし、感傷に浸りたい年頃になってしまったので、駄文(になるだろう)を書いてみる。

なお、これを読んでも得られるものはないだろう。

参加したきっかけ

あれは大学4年になるかならないかという時期、指導教員の [twitter:mikantsuki] と相談してTDDに関連する研究にしようと決まった時のこと。

「そういえば、TDDに関する勉強会が北陸であったらしいんだけど、それが次は名古屋で開催されるみたいだね。 遠いから無理かもしれないけれど、行けるなら参加したら?」

勉強会という存在を始めて知った瞬間である。 きっと良い経験になるだろうと思ったので、行く決意を固める。

今考えると、よく思い切ったものだ。

登録した時期

応募がはじまったよ、と教えてもらったので早速登録しようとする。

twitterアカウントを持っているなら記載してもらえると嬉しい、とある。 持っていなかったので作った。

その頃に研究でデータがほしいから「データ取りに協力していただけると〜」みたいなことをつぶやいた記憶がある。 若干黒歴史だが…。

ちなみに id:t-wada さんのtwitterアカウントを始めてみた時の感想は「サッカー好きなのだなー」。 その頃はちょうどW杯が始まるくらい時期だったので、たぶんそれ系のつぶやきが目に入ったのだろう。

id:bleis-tift さんは、もっと年上な人を想像していた。

初日

ここからはちょっと長くなるかもしれない(後書き:そんなに長くならなかった)。

会場到着あたりまで

初名古屋。 若干迷いながら会場に到着する。

会場の1Fにそれらしき人々を見つけるも、怖くてしゃべりかけることができない。 黙々と開場まで待つ。 (どうでもいいが、この時の愛機はIS01だった)

初勉強会なので勝手がわからず、てきとーに空いてる席に座る。

午前

飛び交っている単語がわからないものばかりだ。

OCaml?なんだそれは?*1

不安が募る。 ただ、タイミングよくイベントが開始されたので講演に集中する。

「なるほど、そういうことか」とか「ここはみかままが講義で言ってた気がする」 とか思ったりなどしていた。

ペアプロデモはRubyでわからないところがでてきたりしたのでその場で調べていたりした気がする。

ペアプロ

お昼ご飯を食べ、ペアプロ前半。 お題が講義の課題とにていたためその時の記憶に引っ張られてしまったなーと反省した記憶がある。

ペアプロ後半。 Mock の存在を知ったのはこの時だ。

お題が進み、時刻の概念が現れた時にペアの方が「これはMockを使うべきなのかな」と言ったのだ。

初めて聞く単語だったので、何をしていいか全然わからない。 わからないから、「わからないので、使い方を見せてください」とペアプロを放棄してお願いした。 ペアプロ体験が目的のイベントでこれを言っていいのかと迷ったが、興味の方が勝った。

レビューあたり

このあたりで、自分の力のなさに絶望していた。 あのくらいプログラミングできないと就職してから仕事できないのか…みたいな。

懇親会

知らない人ばかりだったため、なかなか話しかけることができずわりとぼっちになってたところ、bleisさんやt-wadaさん、kokubo_yusukeさんが話しかけてくださった。 これがなければ気分が沈みっぱなしだったと思う。 喋っているうちに心が軽くなる。

懇親会後は会場に戻ってnagiseさんのGenerics談義をきいていた。 この話もとても面白かった。

デモ用にレガシーコードを提供したのはこの辺りだ。 bleisさんとt-wadaさんがデモ用のちょうど良いサイズのコードがなくて困っているという話をしていた。 ここで自分のPCに使えそうなコードがあることに気づき、懇親会で話掛けてくださったことへのせめてもの感謝の気持ちとして、恥を捨ててコード提供を提案した。

なお件のレガシーコードは、現在はデモ用として一部構成をいじった上でGitHub上で公開されている。コードに関してはそのままなので、レガシーコード改善の練習として触ってみるのも一興かもしれない。

tddbc/LegacyCodeJava · GitHub

しかしひどいな、これ。 今ならparser combinatorで一発事案だ…制約がある中で書いたコードとはいえ、これはひどい

余談だが、もともとこのコードにはテストコードが存在していた。 探せばでてくるかもしれないが、JUnit3で書いていたし古傷をえぐるだけだろうと思い掘り返さないでいる。

宿泊部屋

修学旅行みたいに何人かで集まってワイワイコード書いてた。 この時に何名かの方が研究データ取りに協力してくださった。

2日目

寝たら気分すっきり、気力が回復する。

朝食の席ではコンパイラについておしゃべりしていた記憶がある。 そして赤味噌うみゃー。

講演とデモ

講演とデモ。 デモでコードがどんどん改善されていくのはとても勉強になった。

昼食はうどん・そば・ごはんという謎コンボ。

グリーンバンドの話が持ち上がっていたのはこの時だっただろうか。 購入者を募っていたが、私は距離が距離だったためさすがになと思って購入を控えた。 …正直言うとちょっと後悔した。

午後

最初は苦戦しつつ、途中から慣れてきてテストを差し込んだりあれこれ相談したりしていた気がする。

ところで、作業中にきがついたことの一つに、OCamlScalaでコードを書いていた方々が感動するくらい良い笑顔だったというのがある((一番記憶に残っているあの顔はきっとmzpさんだ))。 関数型言語=大学で習ったけどよくわからなかったし怖い*2、と思っていた私にはわりと衝撃的だった。 この衝撃が、今の私を形成するに至った要因の一つである。

レビューの際もわからない単語だらけだったが、この時は開き直っていた。 「自分のレベルの低さに絶望したことなら前にもあるじゃん。メモして今から調べてやればいいんだよ」みたいな。 厨二病全開だったりもした。「絶望は人を次のステージへと昇華させる…」

帰りは時間の都合があったため、ささっと退散。

帰り

t-wadaさんの言葉が脳内でリフレイン。

『TDDBC は「やりたい」と手をあげれば始められるイベントです。』

この濃度のものは、今はおそらくこのイベントを開催しなければ体験できない。 彼らを巻き込みたい。 私より数段できる彼らなら、きっとそれを糧にするだろうから。

気がついたらtwitterで「佐賀でもやりたいなー」と書き込んでいた。

そして新幹線内で爆睡し、特急電車に乗り換えてさらに爆睡し、帰宅後は熟睡した。

振り返って

思い出しながら書いていたら「若かったなー」と思ってしまうあたり、おっさん度が上がっている。

得たことはたくさんあるが、

  • 知らなかったことを後悔するよりさっさと調べる
  • やりたいと思ったらやれ
  • 動いていれば何かにつながる

あたりは特に今も自分の生活や勉強会開催のモチベーションにつながっている。

ありがとう、かつての私。 あの時参加していなければ、絶望していなければ、ここには居ないし今も技術に対して足掻いていることはなかっだろう。

*1:当時はまだ存在を知らなかった

*2:今となっては食わず嫌いなだけだったと反省するばかりだ

JavaScriptのパーサコンビネータparsimmonをTypeScriptで使ってみる

時代の流れに逆らえなくなってきた感があるので、TypeScriptの練習。

とはいえ、最初は慣れ親しんだものから攻めるのが現実的だと思われるので、パーサコンビネータを題材にする。

jneen/parsimmon · GitHub

ライブラリはこれを使う。 自作するかとも考えたが、特に車輪の再発明する理由がなかったので採用。

お題

FParsecで遊ぶ - 2つのアンコール

このオフサイドルールな入力を解析する。

出来上がり

https://github.com/pocketberserker/sample-parser/tree/3c3f2ea57e0dc647dc88dedb32e6cf1c113af5cb

思ったこと

  • tuple を使うと型が推論されてもよさそうな部分で推論されなくなることがあった気がするので、あまり使用しないほうがいいのか?
  • 現時点のDefinitelyTyped に登録されている d.ts が最新に対応していないのでちょっと自分で定義してあげる必要がある
  • ひたすらメソッドチェーンなので気を付けないとインデントが…
  • array.sliceしまくっているけど、これいいのだろうか…?

余談

実は最初のほうはJavaScriptでも書いていたのだが、テスト実行時に「hogefuga not found」を連発されて降参した。 やっぱりコンパイルがないと無理だったよ…。

7月以降に開催するイベントの告知

こんにちは、もみあげです。

最近ブログではイベントの告知をあまりしていなかったのですが、今回はいくつか企画が走っているので紹介しておきます。

FSharp談話室(20) ゲストもくるよ!

F#談話室(20) - connpass

Yan Cuiさんの来日に合わせての開催です。 現状参加者がちょっと少ないので、Yanさんの twitter bioにビビッときた方はぜひお越しください。

関数型Scalaの集い

関数型Scalaの集い - connpass

昨年開催されたScalaz勉強会の範囲拡大版ですね。

こちらは発表者を募集しております。 (参加枠は埋まってしまってます…が、運が良ければニコニコ生放送があるかも?)

余談ですが、公開前の仮イベント名はわりとひどかったです。

関数プログラミング交流会

こちらは今のところ発表者のみ募集中です。

関数プログラミング交流会 発表者募集要項

枠以上の応募があった場合は選考方式のため、発表できない可能性も」ありますが、枠から漏れた方の参加枠はあらかじめ確保するのでご安心ください。

参加枠は後日募集開始となります。

関数プログラミング交流会(参加者用) - connpass

connpassにフォローブックマーク機能があるみたいなので、忘れそうな方はブクマしておくと良いかもしれません。

ちなみに、前日にはProof Summitが開催されるみたいなので、こちらにも足を運んでみてはいかがでしょうか。

Functional Java の HList を C# に移植して F# 拡張を作った

前々からFunctional JavaにHListが存在するのは知っていたのですが、なかなか手をだせていませんでした。 で、最近Lensライブラリ移植の息抜きにちょっと時間があったので移植してみました。

そもそもHListって?

heterogenous list。 日本語ではたまにヘテロリストとも呼ばれてるみたいですね。 "haskell HList"とか"HList shapeless"で検索すれば何かしら情報は手に入ると思います。

というわけで作った

下記リポジトリから取得できいます。 NuGetにも公開しているので簡単に試すこともできます。

pocketberserker/Data.HList · GitHub

移植自体はそんなに難しくなかった…嘘ですごめんなさい。

実は最初は Pure F# で実装していたのですが、型推論がだめな方向に作動してしまって型エラーをなんとかするのが面倒になった経緯があります。 以下残骸。

Pure F# で HList を実装しようとしたが…型推論に阻まれる

Apply.Cons に型を書けば問題なく通るかもですが、それやるのであればいっそもうC#で実装したほうがいいんじゃない、ということでC#にスイッチしました。

使い勝手は?

HaskellScala(shapeless)のそれに比べると使い勝手は悪いかもしれません…というかまず機能の差が。 それに彼らはこちらにはないHTやマクロ、型クラスがありますからね。


気をとりなおして、JavaC#とF#の使い勝手について。

これは完全に型推論の力に依存して使いやすさが変わります。 サンプルコードを見ればその違いがよくわかるので、いかにリンクを並べておきます。

HAppendの例だけなのと、C#の実装がこれでよいのか自信がないのであれですが、C#...。

Javaは変数の型を書くのはだるいものの、型は比較的わかりやすいためそんなに考えなくてもなんとかなります。 が、C#メソッドのほうが推論されないため、4つの型パラメータに何を当てはめれば望みの型になるのか考える必要があります。 F#もC#と同じ道をたどるかと思いきや…この程度のコードだと空気を読んで型を推論してしまいます*1

まとめ

  • C#とF#でもHList使えるよ!
  • 本記事とは若干関係ないけど、HListはジェネリクスの勉強に役立つ(要出典、要検証)
  • FoldrのF#拡張とサンプルを作りたい人生
  • map関数を作りたい

*1:推論に頼りすぎると前述みたいな型エラー事案に遭遇するのでご利用は計画的に

QuickCheck系統のGenのfilter(suchThat)実装比較

Gen#filter · Issue #5 · xuwei-k/scalaprops · GitHub

こういうissueを投げたあとで、オープンではない場所で色々話していた際にちょっと調べてみたのでメモ。

filter(suchThat)の挙動

あるジェネレータから条件を見たす値のみを出力したい場合に使う

-- こんなシグネチャ
Gen a -> (a -> Bool) -> Gen a

処理的には

  1. 値をランダムに生成して
  2. 生成した値が述語を満たすかチェックし
  3. 満たすならその値をGenで包んで返す
  4. そうでない場合はseedを変えてやり直し

問題点

  • 条件を満たさなければ再生性を行わなければならない都合上、再帰関数にするかwhileでぶん回す必要がある
  • 実装次第で無限ループに陥る
  • 無限ループしない場合でも処理の遅延につながる

対策

  • 生成回数の上限を決める
  • 生成失敗を組み込む
  • 別の機能で置き換える
  • 覚悟を決めてぶん回す

lazyかどうかは関係ないはず。

上限を決める

https://github.com/clojure/test.check/blob/7ed9f927047366d14f160a3a23fde5e6bab629f2/src/main/clojure/clojure/test/check/generators.clj#L299

思い処理でなければ現実的な時間に収まる。 ただし、生成方法によってはかならずExhaustedになる。 また、テストがエラーとして扱われる。

生成失敗を組み込む

https://github.com/rickynils/scalacheck/blob/333307dc1b7dd022641191c5085222583cfc3994/src/main/scala/org/scalacheck/Gen.scala#L33

ただし、この方針を採用すると他の機能にかなりの影響を与える。

  • Genの生成結果を取得して新たなGenを作る際に失敗処理を組み込む必要がある
  • とくにCoArbitraryとの相性が悪い

別機能で置き換える

https://github.com/BurntSushi/quickcheck/blob/d282c811a4c419ae4cd997b1093636d88d5c573a/examples/reverse_single.rs#L15

定型処理になるしテスト失敗になる確率が増えるが、安定はする。

気合いでぶん回す

https://github.com/fsharp/FsCheck/blob/91ebaf7b7c2453212e80b9d193f343e26975c356/src/FsCheck/Gen.fs#L317

  • タイムアウト機能必須(なかったら無限ループに陥る)
  • 条件を満たす値をみつけるまで繰り返すため遅い
  • ユーザは気楽にフィルタリングできる

まとめ

妥協できるかどうかはユーザ次第。

以下追記