kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Scalaスケーラブルプログラミング第3版(電子版も)が発売されます(2016/09/20)

通称コップ本こと、Scalaスケーラブルプログラミングの第3版が今月下旬発売されます。今回のウリはなんといっても、Scala 2.11(2.12のSAM変換も)対応です。また、第3版なので(?)コップの数が3個に増えています。

Scala 2.11対応なので、String Interpolation、Value Classes、implicit classといったScala 2.10の機能についてもちゃんと触れられています。Scala 2.11対応の日本語書籍がしばらくの間存在していなかったのですが、これでそういった状況も改善されそうです。

あと、もう一つ重要なのが電子版も発売されるということです。これは広めてしまって良いということなので書きますが、現在電子書籍版発売に向けて作業中とのことです(電子版発売はだいたい紙版と同じ時期だそうです)。これで、重いコップ本を持たなくても済みますね!(とりあえずPDF版は出るとのことですが、Kindle版が出るかは不明です)

Amazon CAPTCHA

よろしくお願いします。

ScalaとKotlin(と昔のJava)のジェネリクスが壊れている理由

表題の通りです。とりあえず、Kotlin版とScala版のコード貼ります。

gist.github.com

gist.github.com

これらのコードでは、両方とも明示的なダウンキャストやその他の抜け穴を使っていないので、実行しても決してClassCastExceptionが起きてはいけないのですが、実際に実行するとClassCastExceptionが起きてしまいます。

さて、これはコンパイラの実装のバグでもあり、言語仕様のバグでもあるのですが、ちょっと理由を説明してみたいと思います。

class B extends A with Comparable[B] {
  def compareTo(b: B): Int = 0
}

上記のコードをコンパイルすると、下記のコードが生成されます。ここで、int compareTo(java.lang.Object)というメソッドが 定義されているのが味噌です。

Compiled from "C.scala"
public class B extends A implements java.lang.Comparable<B> {
  public int compareTo(B);
    Code:
       0: iconst_0
       1: ireturn

  public int compareTo(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #2                  // class B
       5: invokevirtual #19                 // Method compareTo:(LB;)I
       8: ireturn

  public B();
    Code:
       0: aload_0
       1: invokespecial #25                 // Method A."<init>":()V
       4: return
}

このような、ジェネリクスをうまく動かすためにコンパイラが自動生成するメソッドのことをブリッジメソッド と呼んだりしますが、このブリッジメソッドが曲者です。このブリッジメソッドでは単に引数をB型にキャストして呼び出すだけですが、これはComparable<B>型として取り扱われた型に対して正しくcompareToメソッドを呼び出すために必要なものです。このブリッジメソッド、本来は内部実装であって直接呼び出せてしまうとまずい代物なのですが、ブリッジメソッドと同じシグニチャのメソッドをスーパークラスで定義してやると、このブリッジメソッドをユーザーが呼び出せてしまい、その結果、ClassCastExceptionが起こるのでした。

実はこの問題、Java Genericsの(実装 and/or 仕様)バグとして以前から知られており、

Java Generics Unsound? | A Concurrent Affair

等で言及されています。結果、Java Genericsではこのようなブリッジメソッドをユーザが呼び出し可能になるような穴はふさがれたのですが、ジェネリクスJavaと類似の方式でコンパイルするScalaやKotlinはこの仕様バグをそのまま引き継いでしまったのでした。

なお、Kotlinのissue trackerには既に報告

https://youtrack.jetbrains.com/issue/KT-13712

してあります。

SWoPP2016での発表「PEGのパラメタ付き拡張 Macro PEGの提案」のスライド

先日、8/8-8/10に、長野県の松本で行われたSWoPP2016

sites.google.com

に発表&参加してきました。SWoPP2016は複数の研究会が一同に集まって行われるワークショップですが、自分はその中でもプログラミング研究会(PRO)で発表してきました。その発表スライドが以下です。

kmizu.github.io

まだ正式な論文にするには足りていないものが多過ぎるので、あくまで発表するのみでしたが、今後Macro PEGに関する研究をちゃんとやっていく上で示唆に富む質問がいくつもありました。 もしPEGに興味があれば、上記資料などを読んで感想をいただけると嬉しいです。

自分のTwitterの使い方を考え直す

自分ははっきり言って重度のTwitter依存症であり、何かあるとすぐTwitterを見る癖がついてしまった。まあ、それはいい。ただ、その使い方があまりにも節操がないので、いらんいざこざを招いているなと自覚するようになった。

たとえば、自分は、興味のある分野でキーワード検索をかけて、自分が妙だと思う意見をみつけたら、それが知人かどうかに関わらず容赦なく間違いの指摘や批判を行うし、見知らぬ人にいきなりメンション飛ばされても何も考えずに反応する。

最近になって、こういうの続けるのはどうも自分の心身に悪い影響を与えてるのではと思うようになったので、今後の自分のためのTwitter利用規約を設けてみることにした。これは自分の心身の健康を保つためであって、他の人のための規範ではないのでその点、勘違いなされぬよう。

  1. 顔見知りでない人にいきなりメンションを飛ばさない

Twitterにツイートした内容は全世界に公開されるのだから、顔見知りだろうが何だろうがおかしなことを言っていたら批判することに躊躇しないで良いと心の底では思っているが、一方で、Twitterを主に仲間内でのコミュニケーションに使っている人々もいる。顔見知りでない人にいきなりメンションを投げたりするのはそういう人と余計なトラブルを生むことになるので避けることとする。

  1. 顔見知りでない人にメンションを飛ばされたとき、スルーするか反応するか熟慮する

自分の今までのデフォルトは「とりあえず反応する」だったが、これは面倒くさい人にぶちあたったときに対応コストが高い。また、その上、政治的立場を異にする人からのメンションだと、政治的信条のぶつかりあいになって疲弊する。ので、基本はスルーすることにして、穏当な内容のメンションに限って反応するか検討する

  1. 表現に気を付ける

これはまあTwitterでなくても注意すべき事柄なのだが、Twitterだとつい思ったことをそのまま書いてしまうことがあるので、基本的に批判するときでも穏当な表現をこころがける。穏当な表現で表現しきれない程批判したいことがある場合は、そもそもスルーする。どうせ、そこまで立場が違った場合、Twitterでの議論程度で相手を説得できる事は期待できないので。

たとえば、

で「馬鹿」という表現を意図して使っているが、「考えが足りていない」にとどめるようにする、など。

とりあえずこの3点に気を付けて今後はTwitterを利用していきたいと考えております。再度述べますが、これは、自分が心穏やかにTwitterを利用するための規範であり、Twitter利用者かくあるべし、という規範ではありません。Twitterは全世界に公開されている言説であり、知人であろうがなんであろうが批判のメンションを飛ばすことを躊躇すべきとは思わないというあり方は原理的に正しいですし、特に否定するつもりはないです。

オーバーロードされたメソッドをeta-expansionする (2)

kmizu.hatenablog.com

を読み返していて、これ、

val add: (Double, Int) => Double = O.add

で行けるんだから、そもそも_が要らなくて

O.add:((Double, Int) => Int)

でいけるのでは?と思って書いてみたら動いたという話です。要はメソッド型の式(O.addはメソッド型の式と解釈される)に型注釈として関数型(function type)を書いてやればいいというだけの話です。オーバーロードされたメソッドをeta-expansionするには、今のところこれが最も手っ取り早い方法だといえそうです。

私のTwitterの使い方

Twitterを始めたのが自分のプロフィールみると2007年春ということなので、Twitterを始めてかれこれ9年になります。というわけで(?)、自分の普段のTwitterの使い方をいくつかに分類して振り返ってみようと思います。

これが自分の中では一番多い使い方かと思いますが、「帰ったー」とか「寝る」とか「起きた」「体調悪い」「二度寝する」「お腹壊した」「魚うまい」とかどうでもいいことも含めてとにかく色々書きます。この辺はもう反射で書くようになったので、誰かが読んでるとかほとんど意識にのぼっておらず、とりあえず自分のログを取る意味で付けています。

Instagramの写真も基本的にライフログの一部として、Twitterでも共有するようにしています。 ちなみに、Instagram自体にTwitter連携機能があるのですが、これだとTwitter上ではインラインで画像が表示されないので、IFTTTという複数のサービスをつなげるWebサービスを使っています。IFTTT経由でTwitterに画像を貼るとインラインで表示してくれます。便利。

なお、 #kmizu_something というタグをときどき使いますが、これはハッシュタグ名前空間汚染を防ぎ、Twilogから目的とする種類のツイートを素早く検索するために使っています。たとえば、最近まで体調が安定しなかったので、 #kmizu_health タグを使っていました(そして、このために、会う人会う人に自分の体調を心配される羽目になったのでした…)。以前ジョギングなどをしていたときは、 #kmizu_walking、ふと思ったことを書くときには #kmizu_thought (これはあまり使っていませんが)、などと活用しています。

ログが数ツイート以上に及ぶときには、先のツイートに対してセルフリプライしてスレッド化するという手段をよく使います。

  • ツッコミ用として

TweetDeckには、検索ワード「Scala」でカラムを作り、他にはKotlin、HaskellJavaJavaScriptなどでも気が向いたときに検索し、気になったツイートがあればツッコミを入れます。このとき相手が誰かはほとんど気にしないので、突然見知らぬ自分からScala関係のツイート爆撃を食らって驚かれた方は結構おられるかもしれません。特に悪意があるわけでなく趣味でツッコミを入れてるだけなのであまり気にしないでください。最近はKotlin関係のツイートもツッコミ対象になっています。

  • 知り合いとの議論用として

面識のある人用のリストを作って、そのリストの人たちのツイートを表示するタブをみて、議論をしたくてウズウズしたくなる話題が出たらリプライしたり、引用ツイートしたりします。

  • 知り合いとの手軽な連絡手段として(DM)

メール書くのはめんどい、しかし、Facebookメッセンジャー使う程でもない、という微妙な感じのときにはTwitterのDMをよく使います。最近は3名以上を入れてDMできますが、この機能、一度くらいしか使った覚えがないです…。

  • 時事関係のニュースの元として

自分は技術関係以外にも割と色々な人を無差別にフォローしているので、大きなニュースがあったらHomeタブにニュースがただーっと流れてくることがあります。まあ、大抵がどうでもいい、というとアレですが、日々消化されるものであり、技術関係のニュースソースはまた別の箇所に頼っています。

  • 面白い/後で参照するかもしれない/意見に賛同するツイートの備忘録として

基本的にLikeはこのために使っています。RTは必ずしも賛同しているとは限りません。

  • その他娯楽として

とりあえずこんな感じでしょうか。また何か思いついたら追記するかもしれません。

オーバーロードされたメソッドをeta-expansionする

eta-expansionとは、メソッドを関数に変換する処理です。 たとえば、

def add(x: Int, y: Int): Int = x + y
val addFunc = add _

で、add _によって、eta-expansionが行われ、メソッドaddが関数に変換されます。このeta-expansion、メソッドがオーバーロードされていると一見正しく行うことができないように見えます。

object O {
  def add(x: Double, y: Int): Double = x + y
  def add(x: Int, y: Double): Double = x + y
}
O.add _
<console>:13: error: ambiguous reference to overloaded definition,
both method add in object O of type (x: Int, y: Double)Double
and  method add in object O of type (x: Double, y: Int)Double
match expected type ?
       O.add _
         ^

これに対して、次のようにしてやればeta-expansionを行うことができます。

object O {
  def add(x: Double, y: Int): Double = x + y
  def add(x: Int, y: Double): Double = x + y
}
O.add _:((Double, Int) => Double)

要はどのような型の関数に変換するか明示してやればいいわけです。eta-expansionは変換先の型を意識せずにできるのがメリットなのでこうしなければいけない時点でメリットが半減シますが、一応回避策はある、ということで。