diff-match-patchを F# に移植してみた

世の中にはdiff-match-patchと呼ばれる、Google製の便利な文字列diffライブラリが存在します。

そんなdiff-match-patchですが、C#版はコードは存在するもののnugetにpublishされていません。 また、コードは.NET Framework 3.5以上である必要があるため、諸事情*1.NET Framework 2.0系である必要がある場合には使えません。 具体的にはLINQとかHashSetとかですね。

幸いなことに、F# を使えば.NET Framework 2.0をターゲットにしつつLINQにあるような関数やSetが使えます。 というわけで移植しました。

https://github.com/pocketberserker/Diff.Match.Patch

immutableに改変するのも大変つらそうだったのでmutableのままです。 どうしてこうなった…的なコードもたくさん出てきます。 実装に関してはそっ閉じすべき事案でしょう。

使い方は大体C#版と同じように使えるのではないでしょうか。

*1:実際に使いたかったPersimmon.Assertionでは.NET 2.0を最低ラインにしている

java-object-diff を F# に移植

久しぶりの移植芸です。

移植理由

Persimmonの開発に必要みたいな話になり、かつ移植する時間のありそうな人間が他にいなかったとかそういう。

先達

https://github.com/SQiShER/java-object-diff

さすがJavaさん、探せばだいたいでてくる。

成果物

https://github.com/pocketberserker/FSharp.Object.Diff

3月中旬に始めたので、まともに動かせるようになるまで二か月かかった計算に。 まぁ、FSDNやら業務のあれそれやら遊びといったことを考えると妥当な数値っぽい感じがしますね?

あれそれ

移植元はJavaなので、Javaを前提に実装されているわけですね。 なので、当然仕様の差が出てきます。

  • Javaにプロパティはないが、.NETにはある
    • Javaメソッドから値を取得する形になっていたので、そこをだいたいプロパティに置き換え
  • フィールドのdiff取得機能はomit
    • F#では必要ないと思うんだ

このあたりは仕様の話。

ここから実装の時に遭遇した(または現在進行形)の問題。

  • Javaの一部のMapだとkeyにnullが突っ込める
    • .NETのDictionaryは突っ込めないんだよねーとなってちょっと悩んだ
  • Map.getでnullが返ってくる
    • そういえばそんなんだったなーとか
  • null nullしている
    • Javaだもんね、仕方ないね
    • 移植を優先した関係で F# 版もnull nullしてます…
    • Some nullさん…
  • F#のレコードがほぼ使えない
    • 移植元がmutable前提なのでしょうがない
    • abstract classが出てくるとレコード使えないしね
  • .NETのコレクションの基底となるインターフェースは?
    • read onlyで良いならIEnumerable(ジェネリックじゃないやつ)
    • じゃあ、書き込みしたい場合は?
    • 今回は仕方なくIList(ジェネリックじゃないやつ)をwrite部分での基底に採用している
  • .NETの連想配列の基底となるインターフェースは?
    • IDictionary(ジェネリックじゃないやつ)はあまり役に立たない。けど楽だったのでいったんこれを採用している。
    • IDictinary<'T, 'U>を基底にしたいところだが、type eraseされないのとkeyやvalueの型情報がその場にない関係でパターンマッチで型チェックみたいなことはできない。
    • もしかして:ひたすらリフレクションで頑張るしかない…?
    • JavaさんはCollection<?>とかMap<?>とかやってて「ワイルドカードええ子や」みたいな心境になった
  • 比較演算子とか
    • Java==Object.ReferenceEqualに、equals=に読み替えるの、わりと難しい
    • これをミスして何度か嵌った
  • equality制約を回避したい
    • F#だと=を使うと型パラメータがequality制約を要求する
    • が、diffライブラリなのでそんな制約は外したい
    • ので、比較する部分でbox xを挟んでobjで比較するバッドノウハウを駆使して制約を回避している

まとめ

nullよりもコレクションの仕様の差やtype eraseかそうでないかなど、もっと別な場所で精神が削れました。

Microsoft MVP for Visual Studio and Development Technologies(F#)を受賞しました

気がついたらまたカテゴリが変わっていましたが、今年で4回目の受賞となります。 Visual Studio and Development Technologiesとカテゴリ名が長すぎて未だに覚えられません。 覚えられないのでF#と言い張っておきます。

仕事でF#を使わなくなって久しいですが、これからも何かしら発信していけたらと思います。

特にXamarinとかUnityとか、F#の情報が少ない部分に手を入れられたらいいなとは思っていますが、こればかりは調べる体力があるか(そして気が向くかどうか)次第なのでなんとも……。

え、仕事で使わないのかって? 禁則事項です。

というわけで、今年度もよろしくお願いします。

継続を利用してAsyncコンピュテーション式を実装できるか試す

Combine Deep Dives - ぐるぐる~ という記事にこういう文章があります。

このあたりを解決するために、Stateを使ったり継続を使ったりできるかもしれませんが、Async では未検証です。

そういえば検証したことはなかったなと思ったので、試してみました。

コード

コンパイルは通るしテストも通る、という代物は下記コードです。 ただし、テストは通ったけれど本当に非同期に動いているかどうかまでは検証していないというレベルです。

https://github.com/pocketberserker/ComputationExpressions/blob/b4000a3beddfd8d4b5793f0037222c13a4e96b15/src/ComputationExpressions/Async.fs

Option、Listの実装と異なる部分

OptionListでの実装は、パターンマッチによって値を分解することで包まれていた値を取り出すことができるのでわりと簡単に実装できていました。

しかしAsyncはパターンマッチで分解できません。 というわけで、とりあえず各メソッドでは関数をAsyncで返すようにします。 そしてRunメソッドでは値を返したいのでasync.Returnを渡します。

ゼロ値の処遇

今回実装したAsyncBuilderではZeroメソッドを提供せず、AsyncWithZeroBuilderゼロ値を渡すようにしました。 が、既存のコンピュテーション式よりもAsyncBuilderの表現力が落ちるためちょっと納得いってません。

本当は「ReturnReturnFromが呼び出されなければコンパイルエラー」というビルダークラスを作りたかったのですが、if then ceというコンピュテーション式の変形においてceZeroの型が一致しなければならず、そこを通過しつつRunメソッドのみコンパイルエラーになるような実装をついに導き出せませんでした…無念。

未検証の作戦として「Runで返す値をAsync<'T -> Async<'U>>のままにする」というものがあります。しかし、これはこれでAsync.RunSynchronousAsync.Startなどの多くのメソッドをラップする必要があるので悩みどころです。

まとめ

コンピュテーション式について考えると休日が吹き飛ぶの、つらい。

テスティングフレームワークとSemantic Versioningでなんか考えた

タイトルが釣りっぽくなっていてすみません。

寝起きにぼーっと考えていたら疑問に思ったけど、チラシの裏がないのでここに投げておきます。

この記事は疑問を書いて投げっぱなしにするので解などはかかれていません

Semantic Versioning

Semantic Versioning 2.0.0

semver警察という用語もあるとかないとか。

Evolve slowly

最後にこの話をきいたのはTestingFrameworkMeetingのid:t-wadaさんの資料だったはず。

http://twada.herokuapp.com/presentations/testing_framework_meeting/testing_framework_meeting.html#8

  • Evolve slowly (テストコードは利用者の投資の結果そのものなので、その投資が早いサイクルで無駄になることはない、という安心を与えなければならない)

そのままですね。

ユーザはコードを変更しなくてもいいけど…みたいな話?

  • ユーザはコードを変更しなくてもコンパイルが通る変更
  • しかし互換は崩れる

こういう修正をする必要があったとき、semverに従えばメジャーバージョンを上げる必要があると思います。

で、開発者側としてはバグ修正なのでユーザに最新のバージョンを使って欲しいが、ユーザからは破壊的変更をバンバン行っているように見えるのでEvolve slowlyには反しているんじゃないか、みたいに思えなくもないというか。

いやまぁ、Change log読もうの一言で済むといえばその通りなのですが、メジャーバージョンがガンガンあがるってまだまだわりと抵抗あるんじゃないかなと思う次第です。 テスティングフレームワークなら余計にそう思ったりするのでは、とか。

一旦保留

ということを寝起きで考えましたが考えすぎなのかもしれないし眠いので寝ます。 これを読んで「考えすぎだよ」って思った方はそう私に伝えてください。

PaketとFAKEを使うようにした理由とか

おはようございます。 たぶんこの時間帯は寝ているので、この記事はきっと予約投稿(どうでもいい)。

ここ数日、重い腰をあげて開発したりメンテしたりしているF#系ライブラリの一部でPaketとFAKEを使うようにしたり、最近の書き方に合わせた。

今回対象にしたのは下記のライブラリ。

https://github.com/persimmon-projects/Persimmon https://github.com/persimmon-projects/Persimmon.Dried https://github.com/pocketberserker/FAKE.GitBook https://github.com/persimmon-projects/Persimmon.Dried.Quotations https://github.com/pocketberserker/FAKE.GitBook

基本はProjectScaffoldを参考に、各プロジェクトごとに細かい変更をしている。 ProjectScaffoldはパブリックドメインなので遠慮なく参考にしましょう。

細かい嵌りどころ

  • そもそもディレクトリ構成が全然違うものは合わせるのに時間がかかる
  • frameworkAssemblyFSharp.Coreが設定されているライブラリを参照すると、思いもよらないFSharp.Coreの重複参照を招く
    • しかもエラー場所がわかりづらい…
    • Paketは今のところ回避手段がない
    • paket installpaket updateで毎回この問題にぶつかるため、温かみのある手作業をする必要がある
  • nuspecでできることがすべてpaket.templateでできるわけではない
    • バージョン参照とか現状無理
    • とはいえ、nuspecよりもpaket.templateのほうが楽な部分もあるのでどっちもどっち

なぜPaketを使うようにしたか

そこそこ前からF#界隈だとデファクトスタンダードだったこいつを、なんで去年あたりからようやく使い始めたのかというと

  • いきなりVisual Studio上でビルドしてもちゃんと依存パッケージをダウンロードしてくれるようになった
    • 正直これが大きい
    • 以前はコマンド叩いてからVS上でビルド…という工程がつらくて使わなかったというのが原因の7割
  • Paket.VisualStudioができた
    • とはいえGUIを一回も使ったことがない…
  • Visual Studio 2015からnugetからのrestoreを設定してもnuget.exeが入ってこなくなった
    • 移行理由の3割
    • TravisCIとかを考えるとプロジェクト同梱のほうが楽だなと感じている
      • これは人それぞれだと思う

そういえば最近、groupによって依存パッケージを細かく分離できるようになったようだ。 おかげでProjectScaffold内の各種pathも変更されていて、なし崩し的に対応する必要がでたがそこはまた別の話。

とはいえ、NuGetで困らないレベルのライブラリ群は引き続きNuGetでやるつもりである。

なぜFAKEを使うようにしたか

DSLが不評だったり若干もっさりするなど、色々言われていたビルドツールFAKEさん。 私は消耗したくないのでPowerShellbashで書けばいいや(それはそれで消耗するのでは、とか突っ込みはなしで)とかずっと思っていましたが、まぁ色々とあるのですよ…。

  • 別言語のビルドツールを使っていたら、抵抗するのがむなしくなった
    • npm(+gulp)、sbt、gradle、rebar、mix、etc…
    • あれ、どれも大差ないじゃんという気持ちに(もちろん人によって異論はあるだろう)
    • 各言語のデファクトスタンダードを使っておけば数年は死なないだろうという結論に至った
  • Paketによって敷居が若干下がった?
    • NuGet時代はどこにpackage.configを置くか悩んだ
    • 悩んだ結果、使わなかった
  • Gitへのコミットやその他の操作をPowerShellbashでひたすら頑張ることの限界
    • 消耗し始めた
    • PowerShellbashに強いマンではないので…

特に積極的な理由はないという…すみません。

まとめ

デファクトスタンダードには巻かれよう。

そのうち強い人たちがもっといいものを作ってくれる。

そういう未来を信じるんだ!

最近使っている F# のライブラリやツール

下記のF#版です。

最近使っているScalaのライブラリ - pocketberserkerの爆走

ここ1年くらいのものを対象にしています。

といってもそんなに数はない…。

ライブラリ

自分が開発に関わっているものは除外しています。

FSharp.Data

https://github.com/fsharp/FSharp.Data

まぁ、FSharp.Data.JsonTypeProviderばかり使っているわけですが…。

FsRandom

https://github.com/kos59125/FsRandom

Persimmon.DriedがFsRandomに依存している関係上、一番お世話になっているかもしれない。

FsCheck

https://github.com/fscheck/FsCheck

新しく作るときはPersimmonシリーズを使っているのですが、既存のライブラリや他の人が作っているライブラリでは使っているので。

FsUnit

https://github.com/fsprojects/FsUnit

FsCheckとだいたい同じモチベーションで使っている。

FsPickler

https://github.com/nessos/FsPickler

関数のシリアライズのお供に。

FsYaml

https://github.com/bleis-tift/FsYaml

他のライブラリを探すのが面倒なのでなんとなく使っている。

FSharp.TypeProviders.StarterPack

https://github.com/fsprojects/FSharp.TypeProviders.StarterPack

TypeProviderのお供に。

FSharp.Formatting

https://github.com/tpetricek/FSharp.Formatting

ドキュメントでひたすらお世話になっている。

FSharp.Quotations.Evaluator

https://github.com/fsprojects/FSharp.Quotations.Evaluator

使っているというかforkしているというか。。。

ツール

わりとあった。

Paket

https://github.com/fsprojects/Paket

VS2015あたりからnugetの実行ファイルがくっついてこないのでこっちに切り替えた。 FAKEもセットで使うことが多い。

VS拡張の登場によって以前ほど抵抗がなくなった。

https://github.com/fsprojects/Paket.VisualStudio

FAKE

https://github.com/fsharp/FAKE

nugetにpublishする必要があるライブラリでは使っている。 が、あまり好きではない(AutoOpenするスタイルが……)。

FsReveal

https://github.com/fsprojects/FsReveal

F# のコードをのせる必要があるスライドを作成する時にお試しで使ってみている。

VisualFSharpPowerTools

https://github.com/fsprojects/VisualFSharpPowerTools

略してVFPT。Visual Studioで F# 書くならほぼ必須。だが重い。

使ってないけど有名そうなもの

一応紹介。

FsControl, FSharpPlus

https://github.com/gmpl/FsControl

https://github.com/gmpl/FSharpPlus

型クラス系のアレ。

FParsec

https://bitbucket.org/fparsec/main

実はちゃんと使ったことがない(業務でもたまたま遭遇しなかった)。

ProjectScaffold

https://github.com/fsprojects/ProjectScaffold

スクリプトは参考にさせてもらいつつ、しかしこれを使ってプロジェクトを作成したことはない……。

FSharpLint

https://github.com/fsprojects/FSharpLint

VFPTでの設定は切っているのでお世話になっていない……。

FSharp.Compiler.Service

https://github.com/fsharp/FSharp.Compiler.Service

間接的にお世話になっているが、実際に導入したことはなかったりする。 なかなか機会に恵まれない……。

終わりに一言

多分他の人とはラインナップが全然違うだろうなぁ……。