tkawachi Blog

Scala の関数

Scala での関数を自分なりに整理する。 ここでいう関数(以下、広義の関数)は 名前(引数) の形で適用できるものを指す。

途中で出てくるコードは Scala 2.11.4 の REPL で確認した。 間違いを見つけたら教えてほしい。

メソッドか、apply()をもつオブジェクトか

広義の関数には次の2つに大別される。

  • メソッド
  • apply() メソッドをもつオブジェクト

メソッドは def を使って定義される。 例えばこんな感じ。

scala> def f(i: Int): Int = i + 1
scala> f(10)
res0: Int = 11

一方で apply() メソッドをもつオブジェクトはこんな感じ。

scala> object f { def apply(i: Int): Int = i + 1 }
scala> f(10)
res1: Int = 11

apply() メソッドをもてば良いので class のインスタンスでも良い。

scala> class C { def apply(i: Int): Int = i + 1 }
scala> val f = new C
scala> f(10)
res3: Int = 11

違い

メソッドと apply() メソッドをもつオブジェクトで実用上の違いはあるだろうか?

CloudFormation の JSON を書くときの自分的ベスト

数ヶ月前に CloudFormation の JSON を書くにはどうしたらいいか考えた。 そのときはどこにも書かなかったが、今日思い出す機会があったのでメモしておこう。

背景: CloudFormation の JSON を書くのはつらい

AWS の CloudFormation を使おうと思ったら JSON を書かなければならない。 サンプルで提供されているJSONのひとつ を見てみて、自分にはとても書く気にはなれなかった。 理由は2つ。

1つめはコメントが書けないこと。 JSON として Description が書けるようになっているが、自由な行にコメントを書きたい。 コメントがないと後日見た時に意図が伝わらないことがあり、メンテナンスが困難になる。

2つめはJSONであるがゆえにDRYに書けないこと。 たとえばサンプル中に出てくる次の部分をみてみよう。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"InboundHTTPPublicNetworkAclEntry" : {
  "Type" : "AWS::EC2::NetworkAclEntry",
  "Properties" : {
    "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
    "RuleNumber" : "100",
    "Protocol" : "6",
    "RuleAction" : "allow",
    "Egress" : "false",
    "CidrBlock" : "0.0.0.0/0",
    "PortRange" : { "From" : "80", "To" : "80" }
  }
},

"InboundHTTPSPublicNetworkAclEntry" : {
  "Type" : "AWS::EC2::NetworkAclEntry",
  "Properties" : {
    "NetworkAclId" : { "Ref" : "PublicNetworkAcl" },
    "RuleNumber" : "101",
    "Protocol" : "6",
    "RuleAction" : "allow",
    "Egress" : "false",
    "CidrBlock" : "0.0.0.0/0",
    "PortRange" : { "From" : "443", "To" : "443" }
  }
},

げぇー。ほとんど同じ内容が書かれている。 違う部分を探すのが難しい。 プログラマがこれを見たら共通部をくくりだしたくなるのは自然だし、くくりだせないということがわかったら発狂するか無気力になるだろう。 大きな例になればなるほど、このような繰り返しが多数出現する。つらい。

解決策1. DSL

解決策のひとつは DSL を書いて JSON を生成する方法だ。 cloudformation-ruby-dsltroposphere や他にもいろいろ探せば出てくる。 DSLが問題を解決するか否かは、、まあDSL次第だ。 内部DSLの場合にはベースとなるプログラミング言語そのものの機能で問題を解消できる。 ざっと見たところ内部DSLを採用している例ばかりだった。

しかし、DSLを使うことで新たな問題が発生する。 CloudFormation には日々新たな機能が追加されており、DSLが機能をサポートするまでに時間がかかるのだ。 機能追加の速度が遅い場合にはこれは問題にはならないが、AWSはすごい速度で機能を追加してくる。 使いたいものが使いたいときに使えないようでは残念な気持ちになることが想像できる。

もちろんDSLに自分でpatchを当てればいいのだが、特定のDSLにそこまで思い入れがあるわけではない。 自分が求めているのは JSON を便利に書く方法であって、DSLではないということに気づいた。

解決策2. HOCON (Human Optimized Config Object Notation)

私は普段 Play framework を使って開発をしている。 Play で設定を書くときには HOCON というフォーマットを使う。 HOCON は JSON の superset であり、人間が読み書きするのに最適化されている(Human Optimized) 。 JSONを便利に書きたいのだから、HOCONこそ自分が求めるものだ(とその時は思った)。

HOCON を JSON に変換する方法が欲しかったので、コマンドラインツール hocon2json を作った。

問題は解決された(やった)。

敢えてこの方法の問題を挙げるとしたら、同僚に HOCON の書き方を教えなければならない点、コマンドラインツールをインストールしなければならない点だ。

解決策3. JavaScript/CoffeeScript

HOCONで問題は解決したものの、やっぱ HOCON マイナーだし、説明するのちょっとつらいな、という思いがしばらくくすぶっていた。 あるとき、JSONてそもそも JavaScript Object Notation の略だし、JavaScript で書けばいいんじゃないか、と思い立った。 JavaScript であれば知名度抜群でだれでも知っているし、コメントも簡単にかけるし、共通化も簡単だ。

実現方法は、JavaScript 内で JSON として使う Object を作って、 console.log(JSON.stringify(obj)) とするだけだ。 Pretty print したけば console.log(JSON.stringify(obj, null, 2)) とすれば良い。

今どきどこにいっても Node.js は入っている気がするし、もし無いときにも手元には必ずブラウザがあるので、ツールが無くて変換できないということは無いだろう。

CoffeeScript の実行環境も普及していると仮定すれば JavaScript の代わりに CoffeeScript で書いてもいいだろう。 その辺は普及の仮定と好みによる。

結論

CloudFormation の JSON を書くには JavaScript/CoffeeScript が自分的ベスト(今のところ)。

DDD読書メモ 1

エリック・エヴァンスのドメイン駆動設計(通称:DDD本)を読み始めたのでメモを残しておこう。

ドメインモデルとは?ドメインエキスパートの知識を厳密に構成、厳選して抽象化したものだ。

モデルと設計の核心が相互に形成しあう。

モデルと実装が剥離しては効果が薄い。実装とモデルを緊密に保つべしとのこと。 モデルの形状として何を想定しているのだろう?プログラムコードかな? プログラムコードをドメインエキスパートに読ませるのはしんどいのではないだろうか。 かといって記述用の擬似言語を設計するのもつらい。

モデルは、チームメンバ全員が使用する言語の基板である。

ドメインエキスパートも開発者もモデルの言葉を使ってコミュニケーションしよう、という話。 同じ言葉を使うことで不要な摩擦を除くことができる。

わかりやすい話ではあるが、日本においては日本語と英語の選択をしなければならない。 モデル言語として英語を使う場合、大半の日本人について、英語ネイティブスピーカーと同程度に英単語を自然に扱う能力は無いことは明白だ。 議論を重ねる上でモデル言語は洗練されていくが、その前提には高い言語能力が仮定されている。 英語を選択した場合、洗練がうまくいかない可能性があるのではないか。 一方で日本語を選択した場合、開発者が同じ単語を使うためには日本語でコードを書くことになるのではないか。 日本語でのプログラミングはプラットフォームによっては許容されるかもしれないが、一般に避けたいものである。

「モデルの構築と表現言語の選択」というのは日本人特有の課題だなあ。

第1章

ドメインエキスパートと議論を重ねることで知識を噛み砕きモデルを構築する話。 ドメインエキスパートと開発者が共同作業することが重要。 ほらほら、新しい言語とかフレームワークとかばっかりみてないで、対象業務がどうなっているか興味を持とうよ、てなことが書いてある。

だれも最初から完璧人間ではないのでモデルの構築は発見的に行われる。 変化を許容する必要がある。

コミュニケーション方法に言及するあたりが本書のスタンスをよく表している。

今日はここまで

Play Framework 2.2 Scala 最初の1週間で困った雑多なこと

Play Framework 2.2.1 + Scala を触り始めて一週間くらいたった。 触りながら困った点(というか「疑問に思った」程度のものが多いが)をメモしていたので晒しておく。 雑なメモで無知を晒すのは恥ずかしいが、同じことではまる人がいるかもしれないもんね。

間違っているところがあったらコメントで教えてもらえると大変嬉しいです。

Play Framework 2.2.1 Scala でユーザ登録

Play framework 2.x を触り始めていて、まず最初にユーザ登録とログインを 扱いたいと思った。 Rails であれば devise で、というところだが、Play ではどうなっているのだろう?

Sample としてついてくる zentask や、 「play scala ユーザ登録」でググって上の方に出てくる を見ると、いずれもパスワードを生で保存している。

パスワードは生で保存していると何かの拍子に痛い目に合うので、真似したくない。 きっと Rails の devise みたいに再利用できるコンポーネントがあるだろうから、 それを使いたい。

Google+ のコミュニティで訪ねてみたところSecureSocialplay2-auth を教えてもらった。 SecureSocial を試してみる。

Haxe で Call-by-name したい

先日 Haxe で Option 関連の関数を定義した時、 getOrElse() は次のような定義にしていた。

public static function getOrElse<A>(opt: Option<A>, els: A) {
  return switch (opt) {
  case Some(v): v;
  case None: els;
  }
}

scala.Option の getOrElse() ぽく使いたかったのだが、実際に使ってみるとなんか違う。

opt.getOrElse(sideEffectFunction());

上で sideEffectFunction() は何らかの副作用をもつ関数だ。 Scala の場合、 opt に値が入っていた場合には sideEffectFunction() は評価されない。 副作用も発生しない。 上で定義した Haxe 用 getOrElse() では opt の内容によらず sideEffectFunction() が評価され、副作用が発生する。

関数を呼び出すときには、まず引数を評価してから関数の実行に移るので、当たり前といえば当たり前だった。

Haxe で Option用の関数を定義してみた話

最近 JavaScript で何かを書くことになって困っている。 JavaScript はそれなりに仕事で触れて、それなりに理解はしているが、どうにも好きになれない。 CoffeeScript もそれなりに触れたが、短く書けて式中心の記法ができるものの、それ以外は JavaScript と同じ。 何も変わっていない。

僕の問題(苦手)意識は The JavaScript Problem によく表されている。

  1. JavaScript sucks.
  2. We need JavaScript.

もうちょっとマシな言語で書きたいと常々思っていた。

AltJS のなかで TypeScript は触ったが、Haxe は触っていなかった。 理由は「Haxe が吐き出す JavaScript はとても人間が読めるものじゃない」とどこかで聞いていたから。 いざって時には生成された JavaScript が読める形じゃないと困る。

今回ふとしたきっかけで Haxe を触ってみた。 さわってみるとかなり好感触。 出力された JavaScript はそんなに変なことになってなくて普通に読める。 やはり自分で触ってみないとダメだな。 代数的データ型があるなど TypeScript より型機能が強い感じ。

randomForestで重要な説明変数を見つける

Random forest といえば決定木を何本ももつアンサンブル学習で高い精度を持つ。 けど、どの説明変数が効いているかは説明が難しいものだと思いこんでいた。 ご近所のデータサイエンティストが R で効いている説明変数を出す方法を教えてくれたのでメモ。