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にはある
- フィールドの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のコレクションの基底となるインターフェースは?
- .NETの連想配列の基底となるインターフェースは?
- 比較演算子とか
- Javaの
==
はObject.ReferenceEqual
に、equals
は=
に読み替えるの、わりと難しい - これをミスして何度か嵌った
- Javaの
- equality制約を回避したい
- F#だと
=
を使うと型パラメータがequality
制約を要求する - が、diffライブラリなのでそんな制約は外したい
- ので、比較する部分で
box x
を挟んでobj
で比較するバッドノウハウを駆使して制約を回避している
- F#だと
まとめ
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 では未検証です。
そういえば検証したことはなかったなと思ったので、試してみました。
コード
コンパイルは通るしテストも通る、という代物は下記コードです。 ただし、テストは通ったけれど本当に非同期に動いているかどうかまでは検証していないというレベルです。
Option、Listの実装と異なる部分
OptionやListでの実装は、パターンマッチによって値を分解することで包まれていた値を取り出すことができるのでわりと簡単に実装できていました。
しかしAsync
はパターンマッチで分解できません。
というわけで、とりあえず各メソッドでは関数をAsync
で返すようにします。
そしてRun
メソッドでは値を返したいのでasync.Return
を渡します。
ゼロ値の処遇
今回実装したAsyncBuilder
ではZero
メソッドを提供せず、AsyncWithZeroBuilder
ゼロ値を渡すようにしました。
が、既存のコンピュテーション式よりもAsyncBuilder
の表現力が落ちるためちょっと納得いってません。
本当は「Return
やReturnFrom
が呼び出されなければコンパイルエラー」というビルダークラスを作りたかったのですが、if then ce
というコンピュテーション式の変形においてce
とZero
の型が一致しなければならず、そこを通過しつつRun
メソッドのみコンパイルエラーになるような実装をついに導き出せませんでした…無念。
未検証の作戦として「Run
で返す値をAsync<'T -> Async<'U>>
のままにする」というものがあります。しかし、これはこれでAsync.RunSynchronous
やAsync.Start
などの多くのメソッドをラップする必要があるので悩みどころです。
まとめ
コンピュテーション式について考えると休日が吹き飛ぶの、つらい。
テスティングフレームワークとSemantic Versioningでなんか考えた
タイトルが釣りっぽくなっていてすみません。
寝起きにぼーっと考えていたら疑問に思ったけど、チラシの裏がないのでここに投げておきます。
この記事は疑問を書いて投げっぱなしにするので解などはかかれていません
Semantic Versioning
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はパブリックドメインなので遠慮なく参考にしましょう。
細かい嵌りどころ
- そもそもディレクトリ構成が全然違うものは合わせるのに時間がかかる
frameworkAssembly
にFSharp.Core
が設定されているライブラリを参照すると、思いもよらないFSharp.Core
の重複参照を招く- しかもエラー場所がわかりづらい…
- Paketは今のところ回避手段がない
paket install
やpaket 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さん。 私は消耗したくないのでPowerShellやbashで書けばいいや(それはそれで消耗するのでは、とか突っ込みはなしで)とかずっと思っていましたが、まぁ色々とあるのですよ…。
- 別言語のビルドツールを使っていたら、抵抗するのがむなしくなった
- npm(+gulp)、sbt、gradle、rebar、mix、etc…
- あれ、どれも大差ないじゃんという気持ちに(もちろん人によって異論はあるだろう)
- 各言語のデファクトスタンダードを使っておけば数年は死なないだろうという結論に至った
- Paketによって敷居が若干下がった?
- NuGet時代はどこにpackage.configを置くか悩んだ
- 悩んだ結果、使わなかった
- Gitへのコミットやその他の操作をPowerShellやbashでひたすら頑張ることの限界
- 消耗し始めた
- PowerShellやbashに強いマンではないので…
特に積極的な理由はないという…すみません。
まとめ
デファクトスタンダードには巻かれよう。
そのうち強い人たちがもっといいものを作ってくれる。
そういう未来を信じるんだ!
最近使っている 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
間接的にお世話になっているが、実際に導入したことはなかったりする。 なかなか機会に恵まれない……。
終わりに一言
多分他の人とはラインナップが全然違うだろうなぁ……。