Scala 勉強会

http://cappuccino.jp/scala-ja/?Scala%CA%D9%B6%AF%B2%F1%A1%F7%B4%D8%C5%EC-1

いくつか印象に残ったことを。以下なんか勘違いもあるかも。

意外だった。研究者ってのは関数型言語だけだと思ってた。あるいは Smalltalk

  • 後置制御構文の追加

これはすごいなーと思った。任意の式からその式を封じ込めたオブジェクトへの暗黙の変換を定義することによって、その式が実行される前に型変換を行わせることができるらしい。でも暗黙の変換は変換無しですむ場合は常に変換しないので、その式を実行した結果の型にその後置制御構文と同名のメソッドがあったらダメっぽい。

class When(any: => Any) {
  def when(b: Boolean) = if (b) any
}
implicit def toWhen(any: => Any) = new When(any)

println("Hoge-") when true   // 出力される
println("Hoge-") when false  // 出力されない

class C {
  def when(b: Boolean) {}
}
def myprintln(s: String): C = {
  println(s)
  new C
}

myprintln("Hoge-") when true   // 出力される
myprintln("Hoge-") when false  // 出力される

あと Scala では暗黙の変換が2種類できたらエラーになるのだけど、 =>Any への暗黙の変換は普通の暗黙の変換より弱いっぽい気がする。

def myprintln2(s: String): Int = {
  println(s)
  1
}
implicit def intToC(i: Int) = new C

myprintln2("Hoge-") when true   // 出力される
myprintln2("Hoge-") when false  // 出力される

def myprintln3(s: String): Double = {
  println(s)
  1.1
}

myprintln3("Hoge-") when true   // 出力される
myprintln3("Hoge-") when false  // 出力されない

きもくてよろしい。

implicit は Haskell の type class とか C++0x の concept と似たようなことがやりたいんだと思う。

  • extends と with

class がクラスで、 trait が Ruby の module 、 extends が継承で with が mixin なのだけど、明らかにセンスがない文法になっている。

trait A {}
trait B extends A {}
trait C extends A {}
class D extends B with C with A {}

mixin だけがしたい時でも最初は必ず extends と書かなければならず、あとは with でつないでいくらしい。例えば

class X extends Object with Singleton {}

とかせんといかんのだろうね。

  • アンダースコアをつけると関数の実体が取れる

Ruby とかと一緒で、 () をつけなくても関数は実行されようとするみたい。それを回避するには後ろに _ をつける。キモい。

scala> def f() = println("hello")
f: ()Unit

scala> f
hello

scala> f _
res1: () => Unit = <function>

レシーバの bind とかが簡単なのは評価できるなぁ。

scala> 3.toString _
res2: () => java.lang.String = <function>
  • 引数の書き方

2種類ある。

scala> def f(a: Int, b: Int, c: Int) {}
f: (Int,Int,Int)Unit

scala> def f2(a: Int)(b: Int)(c: Int) {}
f2: (Int)(Int)(Int)Unit

scala> f2(1) _
res5: (Int) => (Int) => Unit = <function>

scala> f(1) _
<console>:6: error: wrong number of arguments for method f: (Int,Int,Int)Unit
       f(1) _
       ^

後者だと curry 化しやすいというメリットがあるみたいだがそれもどうなんだ。

後者の書き方で関数から関数が帰った場合、0引数関数が帰った場合はそれもつつがなく実行してくれる。

scala> def f()()()()()() { println("hello") }
f: ()()()()()()Unit

scala> f
hello

scala> f()
hello

scala> f()()()()()()
hello

scala> f()()()
hello

きもい。

  • Existential types

JG で言うところの、なんていうんだっけ…

val a: Array[Int] = Array(1, 2)

とかを Array[Object] に突っ込めなくて困るとかそういう。

val b: Array[T] forSome { type T } = a

で、こう書けば OK 。ささださんがそれどこが予約語、とか突っ込んでたけど、要は forSome なんて予約語があるのがキモい。でも省略記法がある。

val c: Array[_] = a

もうこっちだけでいいやろ…

class Base {}
trait Mixin {}
class Derived extends Base with Mixin {}

val ds: Array[Derived] = Array(new Derived, new Derived)
val objects: Array[_] = ds
val bs: Array[_ <: Base] = ds
val ms: Array[_ with Mixin] = ds

とか書けるわけか。まぁこれはいいんだけど、リファレンスとか見てると、

class Ref[T]
abstract class Outer {type T }
Ref[x.T] forSome { val x: Outer }
Ref[x_type # T] forSome { type x_type <: Outer with Singleton }
Ref[_ <: java.lang.Number]
Ref[(_ <: Outer with Singleton)# T]

とかのキモい例が多くて把握しきれん。

  • 総じて

想像してたよりはるかにキモい言語機能が多くて良かった。シンタクスシュガー定義しすぎててすごい。なんか寝る前の思いつきとかで言語仕様拡張していってるんじゃないかと妄想しました。

  • その他

あああの人が IKeJI さんかという発見。

みずしまさんは他にもそこらへんに激突したりドジっこ属性がすばらしかった。

id:soutaro さんが「サルでもわかる型理論」を書いて下さると言ってた気がする(言質!)。あと「structual subtyping => OCaml の OO 部分」とかそういう難しい単語から具体例の一覧みたいなまとめがあるといいなぁとかいう話をしました。

次回とかあるなら Scala じゃない言語を(みずしまさんが)順に見てくとかの方がいいなーと思いました。

楽しい会をありがとうございました。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h