( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

java-ja.dddの最後の「関数型言語Dis(?)」について

java-ja.ddd - connpass
2013/03/22 java-ja.ddd #java_ja #java_ja_ddd - Togetter


java-ja.ddd面白かったです。色々と得るものがありました。発表者の方々、有り難うございました。
会場を提供して頂いたGREEさんありがとう。運営のみなさまお疲れ様。


で、一番最後に、「関数型言語をDisって逃げる」ということで発表者の一人である増田亨さんがごく短い時間お話しされました。
その内容があまりにも自分にとって納得しかねるものだったので、ブログにアウトプットします。


関数型言語Dis」の中で、増田さんがされた主張は以下2点です。

関数型言語が、どの言語を指しているのかがまず不明ですが、そこは置いておいて。


まず一点目、「関数型言語ユーザーは業務の話ができない」について。


関数型言語ユーザー全員を指して、「業務の話ができない」という主張の根拠はそもそもどこにあるのでしょうか?
逆に「オブジェクト指向言語のユーザーは全員が業務についても堪能である」と一般化して主張してもよいのですか?
また、話の中で「関数型言語使いが書いた素晴らしいロジックを見たことがある」ともおっしゃっており、主張が一貫していません。


たまたま増田さんの周りの関数型言語ユーザーが業務について(そもそも業務ってなによ?ってのもあるけど)話ができないとしても、それを一般化して主張することの是非については、論ずるまでもないと思われます。


次に、「関数型言語は記号を多用しすぎる」について。


これについては、記号を多用することの弊害について踏み込んで語られておらず、単に「記号使いすぎるからねーハハハ」で終わっていて、「ああ、この人は記号が嫌いなんだな」ということは理解できても、そもそも関数型言語に対するDisにすらなっていないのが残念です。


記号を多用する言語(おそらくHaskellScalaあたりの言語を指していると思われる)のユーザーにとっては、
記号で記述した方が自然で、同じ言語ユーザーに対してコードの意図を示しやすいことがあるという一面があります。


論理演算子(&&や||)は記号ですが、それを指して「記号だからダメだ!andとかorとかで書け!」という主張をする人は稀でしょう。
にも関わらず、関数型言語を使わない人がその記号の使い方について問題を感じるとしたら、そのような感覚を身につけているユーザーの母数が少ないとか、初学者に対して敷居が高いとか、ググラビリティが低いとかいう指摘になるでしょう。そこまでつっこんで話を聞くことができれば良かったのですが。


ともかく、短い時間でのお話で、議論にまで昇華できなかったのが残念でなりません。
機会があれば、双方の主張を議論しあえる場で、非関数型言語ユーザーから見た現状の関数型言語に対する問題点を提示して頂ければ、と思います。

一時的にブランチを切り替えてコマンドを実行するgit-switchっての作った話

gitで、一瞬だけ他のブランチに切り替えてコマンド実行したいときに毎回checkoutとかすんのタルいし、workspaceに変更があったらgit-nowしたりstashしたりしないとブランチ切り替えられなくてタルいので、一時的に他のブランチに切り替えて指定したコマンドを実行するラッパーを書いた。書いた。


dotfiles/bin/git-switch at master · yuroyoro/dotfiles · GitHub

git switch [--repository=<path>] [--new-workdir=<path>] <branch> <command...>


`git switch foo_master git show`ってやると、fooブランチに切り替えて`git show`を実行する。コマンドは別になんでもいいので、`git switch hogehoge rspec spec/hoge_spec.rb`のように、他のブランチでrspec流したり可能です。


以下は、一時的にcomposable_procに移動してgit show -sを実行した様子。

ozaki@mbp-4 ( ꒪⌓꒪) $ git br
  composable_proc
* trunk

ozaki@mbp-4 ( ꒪⌓꒪) $ git switch composable_proc git show -s
Already on 'composable_proc'
commit 20c1b806490233db0467ac4332ba073edca7889f
Author: Tomohito Ozaki <ozaki@yuroyoro.com>
Date:   Sun Mar 10 02:34:12 2013 +0900

    proc.c: Add Proc#flip


内部的に、git-new-workdirを使っているので、以下を参考にgit-new-workdirが使えるようにしておく必要があるです。

git-new-workdir が便利 - #生存戦略 、それは - subtech


デフォルトの動作では、/tmp以下に現在のリポジトリと同じディレクトリ名でgit-new-workdirして、そこでコマンドを実行するようになっている。すでにnew-workdirする先が存在する場合は再利用する。
workdirはオプション'--new-workdir='で変更可能だし。


注意点として、gitのコマンド実行とシグナルハンドラの関係上、シグナルをもらうプロセスがforkされるようなコマンドの実行には注意する必要がある。例えば、pryはCTRL-CによるSIGINTをtrapするが、fork元のgitプロセスはSIGINTでプロセスを終了する。そのため、git-switchではSIGINTをトラップして、forkしたコマンドの子プロセスを頑張ってぬっころすように実装している。このへんうまいことやる方法知りたい。

ワークライフバランス()向上のために定時すぎたらダイアログが表示されるようにした

仕事してると、ついつい「このバグ直した帰ろう」とか「このテストが通ったら帰ろう」とかやってるうちにいつの間にか終電近くまで作業していて( ꒪⌓꒪)ってなることが多いので、定時すぎたら一時間毎にムカつく心温まるメッセージを表示するようにAppleScript書いた。

20時から24時までの一時間毎に、こんな感じで唐突にメッセージが表示されてフォーカスを奪っていき、ワークライフバランス()の改善を促し精神に負荷をかける。

f:id:yuroyoro:20130318204855p:plain
f:id:yuroyoro:20130318203717p:plain
f:id:yuroyoro:20130318203728p:plain
f:id:yuroyoro:20130318203737p:plain
f:id:yuroyoro:20130318203744p:plain

set hour to hours of (current date)

try
	tell application (path to frontmost application as text)
		if hour = 20 then
			set msg to "( ꒪⌓꒪) もういい時間だしさっさと帰れよカスwww"
			display dialog msg
		else if hour = 21 then
			set msg to "( ꒪⌓꒪)おいダラダラ作業してんじゃねーよwwwクズがwww"
			display dialog msg
		else if hour = 22 then
			set msg to "こんな時間まで作業してるとかwwww生産性m9( ꒪⌓꒪)"
			display dialog msg
		else if hour = 23 then
			set msg to "( ꒪⌓꒪)そろそろ帰宅難民なんじゃね?社蓄乙www"
			display dialog msg
		else if hour = 24 then
			set msg to "( ꒪⌓꒪)なにムキになって仕事してんの?社蓄www"
			display dialog msg
		end if
	end tell
end try

Script Editor.appでこんなの書いて、~/bin/fxxk_it_alert.scpt ってファイルで保存。次に~/Library/LaunchAgents/fxxk_it_alert.plistを以下の内容で作成した。
cronだと簡単に設定できるところをクソみたいな長大なxmlを書かねばならず、launchd の StartCalendarInterval はクソだという結論を得た。

fxxk_it_alert.plist

設計を型にエンコードするということ

動的型付け vs 静的型漬けのアレでもんにょりしてたのをついったーに放出して会話してたらなんとなく自分なりの考えがまとまったので貼っておく。



まとめると

  • コンパイラがテストしてくれるように型を書きたい
  • 型に対して、データ型のラベル以上の意味(設計の意図とか)を持たせたい
  • そうすると、自分以外の人が自分が書いたコードを使うときに、正しい使い方を自分やドキュメントに代わって、コンパイラが教えてくれるようになる
  • そういうことが簡単にできるように言語とか進化するといいよね


現実的になんでもかんでもコンパイラにやらせようとすると難しいところもあると思う。
「アカデミックだ」「現場では使えない」とか単に否定するのは簡単だ。
静的な型漬けの言語でコード書いたからって、上記のようにすべてのロジックを型レベルにエンコードすることなど不可能だし、制約が強すぎると感じるかも知れないし、この制約が逆に足かせになる性質のプロダクトがあることもわかっている。


でも、型推論とかGenericsとかは、アカデミックな研究と実用のニーズが相まって、
現実のプログラミング言語に取り入れられつつあるし、
静的な型漬けを好む人達が見据えているプログラミング言語の進化の方向性として、こういう考え方がありますよ、って知って欲しい。

git symbolic-ref を利用して、ブランチに対して別名をつけることができて墓ドル

何が便利かというと、今git-flowで運用しているのだけど、リリースブランチを常に同じブランチ名で参照できるようになってうれしい。


例えば、次回にリリースブランチが'release/1.17.0'だとすると、'git symbolic-ref rc release/1.17.0'とすることで、'rc'という名前で参照できる。
エイリアス先の'release/1.17.0'のHEADが先に進んでも、rcも常に同じHEADを参照するので、一貫して'rc'という名前で扱える。

  $ git symbolic-ref rc refs/heads/release/1.17.0


これだけだとありがたみがない。が、無事に1.17.0がリリースされて、その次のリリースブランチがrelease/1.18.0になったときでも、'git symbolic-ref rc release/1.18.0'でエイリアスを張り直せばいい。リリースブランチは、常に一貫して'rc'という名前でアクセスできる。


この機能、git-flowに入れたい。

svn - Is it possible to alias a branch in Git? - Stack Overflow

httpstatus コマンドで、HTTP のステータスコードをすばやくしらべる!

一般的な Web Programmer ならば、HTTP Status code はすべて暗記していると聞きました。

しかし、僕は初心者なので、なかなか覚えきれていないので、HTTPのステータスコードをさがすのに便利なツールを用意しました。
httpstatus.hs です。インストール方法は 適当にコンパイルしてください。


yuroyoro / httpstatus.hs

Scala2.10.0のDependent method typesと型クラスを組み合わせた『The Magnet Pattern』がヤバい件

これが……型の力かッ……!!

f:id:yuroyoro:20130123192116j:plain

spray | Blog » The Magnet Patternという記事で、「The Magnet Pattern」というデザインパターンが紹介されている。


これは、メソッドオーバーロードで解決していた問題を、型クラスとDependent method typesを組み合わせて置き換えることで、オーバーロードの際の様々な制約(Type Erasureなど)を突破し、より柔軟な拡張性を得ることができるというもの。このパターンでは、引数の型に応じて異なる結果型を返すようにできる。


この記事で、今まで何のために使われるのかわからんかったDependent method typesの有効性が理解でき、あらためて型の力を思い知った。
以前に"Generalized type constraints"(Scalaで&lt;:&lt;とか=:=を使ったgeneralized type constraintsがスゴすぎて感動した話 - ( ꒪⌓꒪) ゆるよろ日記) を知った時以来の感動だったので、勢いで書いてみた。

Dependent method types ってなんぞ?

簡単に言うと、引数の値に応じて結果型が変わるメソッドを定義できるということ。

Scala 2.10 に dependent method types というのが入るらしいよ - scalaとか・・・


以下の例では、fメソッドは、引数のFooトレイトのResult型に応じて結果型が変わる

trait Foo{
  type Result
  def bar:Result
}

// 引数のFooトレイトのResult型に応じて結果型が変わる
def f(obj:Foo):obj.Result = obj.bar


ResultをStringで定義してるstringFooオブジェクトと、Intで定義しているintFooオブジェクトを用意し、

val stringFoo = new Foo {
  type Result = String
  def bar:Result = "foo"
}

val intFoo = new Foo {
  type Result = Int
  def bar:Result = 99
}


fメソッドにそれぞれ渡すと、引数の値に応じて結果型が変わっていることがわかる。もちろん型安全なので、全てコンパイル時に型チェックが行われる。

scala> f(stringFoo)
res0: stringFoo.Result = foo

scala> f(intFoo)
res1: intFoo.Result = 99

scala> f(stringFoo).getClass
res2: Class[_ <: stringFoo.Result] = class java.lang.String

scala> f(intFoo).getClass
res3: Class[intFoo.Result] = int

The Magnet Patternが解決する問題

引数の型に応じてメソッドの振る舞いを変えたい場合、一つの手段としてメソッドをオーバーロードする、という手がある。 しかし 元記事では、オーバーロードでは以下のような問題が発生する、と述べている。

  • type erasureにより引数の型が衝突する場合がある
  • メソッドからFunctionオブジェクトに"lift"できない(println _のような)
  • package objectでは使えない(2.9以前)
  • 似たようなコードが多発
  • デフォルト引数の利用に制限がある
  • 引数の型推論に制限がある


The Magnet Patternは、この問題のいくつかを解決し、さらなるメリットをもたらす

  • type erasureによる型の衝突は解決
  • Functionオブジェクトへのliftは、全ての結果型が同一であれば可能
  • package objectでも定義できる
  • 実装をDRYにできる
  • 危険なimplicit conversionの定義を避けることが出来る
  • 引数の型に応じて異なる結果型を返すことが可能


このパターンは、拡張性に柔軟をもたらす一方で、DrakSideもあると。

  • オーバーロードに比べて実装が細かく見通しが悪くなる
  • 名前付きパラメータは利用できない
  • by-name(名前渡し)パラメータとimplict conversionの組み合わせで重複してby-nameパラメータが評価されることがある
  • Magnet Parttenで定義するメソッドは引数宣言が必須
  • デフォルト引数は定義できない
  • 引数に対しての型推論はできない(引数に無名関数を渡す場合などで)

The Magnet Patternの例

元記事では、例としてSprayにおけるURLルーティングを定義するDSLの実装をあげている。 非常にわかりやすいのでそっちを見てもらうのがいいのだけど、それだとあんまりなので例を書いてみる。


ここでの例は、3個のIntまたはStringから日付変換を行う関数toDateを考える。

通常のオーバーロードでの実装

object M {
  def toDate(year:Int, month:Int, date:Int): java.util.Date = {
    val c = java.util.Calendar.getInstance
    c.set(year, month - 1, date, 0, 0, 0)
    c.getTime
  }

  def toDate(s:String): java.util.Date =
    (new java.text.SimpleDateFormat("yyyy/MM/dd")).parse(s)
}


まぁ見たとおりです。

scala> M.toDate(2013,1,21)
res40: java.util.Date = Mon Jan 21 00:00:00 JST 2013

scala> M.toDate("2013/01/21")
res41: java.util.Date = Mon Jan 21 00:00:00 JST 2013

The Magnet Patternで書き換えてみる

The Magnet Patternでは、オーバーロードを行う代わりに、メソッドの引数にある型(仮にmagnet型と呼ぶ)を一つだけとるようにする。 そして、そのmagnet型へのimplicit convesionを定義することで、オーバーロードと同様に引数の型に応じた振る舞いを定義できる。


さらに、The Magnet Patternでは引数の型に応じて結果型を変えることが可能である。 "magnet型"に抽象型で結果型を持たせ、Depenent method typesを利用して、引数がIntの場合はDateではなくCalndarを返すようにしている。

scala> :paste
// Entering paste mode (ctrl-D to finish)

// toDateは引数に"magnet型"を取るようにする。変換の実装はconvertメソッドに任せる
// 結果型は、"magnet型"が持つ抽象型Resultに依存させている
def toDate(magnet:DateMagnet):magnet.Result = magnet.convert

// toDate関数で使われる"magnet型"のtrait
trait DateMagnet{
  type Result           // 変換結果の結果型は抽象型で持つ
  def convert():Result  // 変換を行うメソッド
}

// "magnet型"のコンパニオンオブジェクトに、
// それぞれの引数の型に合わせた"magnet型インスタンス"を返すimplicit defを
// 定義しておく
object DateMagnet {

  // 3つのIntからDateMagnetのインスタンスへ
  implicit def fromInt(tuple:(Int, Int, Int)) = new DateMagnet {
    type Result = java.util.Calendar
    def convert():Result  = {
      val (year, month, date) = tuple
      val c = java.util.Calendar.getInstance
      c.set(year, month - 1, date, 0, 0, 0)
      c
    }
  }

  // StringからDateMagnetのインスタンスへ
  implicit def fromString(s:String) = new DateMagnet {
    type Result = java.util.Date
    def convert():Result  =
      (new java.text.SimpleDateFormat("yyyy/MM/dd")).parse(s)
  }
}

// Exiting paste mode, now interpreting.

warning: there were 2 feature warnings; re-run with -feature for details
toDate: (magnet: DateMagnet)magnet.Result
defined trait DateMagnet
defined module DateMagnet

scala> toDate(2013, 1, 21)
res0: java.util.Calendar = java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2013,MONTH=0,WEEK_OF_YEAR=4,WEEK_OF_MONTH=4,DAY_OF_MONTH=21,DAY_OF_YEAR=25,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=10,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=88,ZONE_OFFSET=32400000,DST_OFFSET=0]

scala> toDate("2013/01/21")
res1: java.util.Date = Mon Jan 21 00:00:00 JST 2013


実装のポイントは、

  • 引数は"magnet型"を一つとるようにする
  • implicit conversionで引数の型に応じた"magnet型"のインスタンスへ変換する
  • 型に応じた振る舞いは、"magnet型"のメソッドに実装する
  • Dependent method typesを利用することで、"magnet型"の抽象型を差し替えて結果型を変えることができる

上記4点である。ひとことでいうと、オーバーロードで実装されている型毎の処理を型クラスに移した、といえる。


まとめ

メソッドのオーバーロードでは、Type Erasureによる制約などがあるが、 The Magnet Patternではimplicit convesionを定義することであとで対応する型を増やしたり、 結果型を変えたりと、様々な柔軟性を得ることができる。 一方で、実装が複雑になる、コードの見通しが悪くなる、などのデメリットもある。


興味深いのは、このThe Magnet Patternは型クラスやDepenent method typesを利用した、従来のOOPでは実現できない新しいプログラミングパラダイムにおけるデザインパターンであるという事実だ。
デザインパターンGoFからまだまだ進化していると言える。


これが……型の力だッッッ!!!!