kmizuの日記

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

Scalaアンチパターン:変更可能コレクションをvarとして宣言する

Scalaは最初から関数型プログラミングのスタイルで書くことを意識して設計されたという意味で関数型プログラミング言語と言えますが、一方で、「Better Java」な手続き型スタイルで書くことも基本的には否定されるべきではないと思います。たとえば、ビッグデータ関係のソフトウェアとして有名なApache SparkはScalaで書かれていますが、(おそらく)パフォーマンス上の理由のため、手続き型のスタイルで書かれている部分がかなり多いです。

さて、それはいいのですが、そういう「Better Java」なScalaコードによく見られるパターンに、変更可能コレクションを使っているのに、そのコレクションを格納する変数をvarとして宣言しているものが(割と有名なソフトウェアでさえ)あります。ですが、そういうコードは 書いてはいけません(かなり例外的な場合を除いて)。

「書いてはいけません」というのはかなり強い言い方ですが、きちんと考えると割と当然の話です。

各要素が"name:age"で区切られたコマンドライン引数列を元に、Personクラスのオブジェクトの列を作り出す手続き型コードを考えてみます。たとえばこんな感じになるでしょう(書きたい処理の例がかなり無理矢理ですがご容赦ください):

import scala.collection.mutable.ArrayBuffer

case class Person(name: String, age: String)
var persons = ArrayBuffer[Person]() //valにしても意味は変わらない!
for(arg <- args) {
  val Array(name, age) = arg.split(":")
  persons.append(Person(name, age))
}

/*
 * personsを使って何かする(が、personsには再代入されない)
 */

このコードにおいて、ArrayBuffer[Person]型のpersonsを変更可能な変数として宣言していますが、これはvalにしても同じです。なぜなら、personsの「指す先」であるArrayBufferそのものが変更可能だからです。

もちろん、personsに変更可能コレクションを再代入したいなら、変数自体を変更可能にする必要がありますが、かなり考えづらい事態です。コレクションを空にしたいという要求がある場合でもclearメソッドを呼び出せば済む話です。

Javaでは、変数をfinalにするのは「追加で」finalを付加する必要があり、面倒くさいのであえてfinalをつけないということは割とよくありますが、Scalaは、変数の宣言時に、valとvarの「どっちかを選択させる」タイプの言語です。つまり、varで変数を宣言した場合、その変数 そのもののの値を変更する 意図があるとみなして良いのです。これは、コードを読むときの労力を削減する役割を果たします。

しかし、変更するつもりもない変数をvarにするという無意味なことをすることがまかり通ると、せっかくの良い言語設計も台無しになってしまいます。

というわけで、そういうことをしてはいけません。

あるいは、そういうコードを書いている人は、変数自体の変更可能性変数の参照先の変更可能性 の区別がついていないのかもしれません。Javaでも「初学者」がよくはまるポイントです。ですが、その区別がついていない人はまともなJavaプログラマですらないような気がします。

個人的に好き(な/だった)技術書籍10選

プログラマのための技術書籍○選」とかよく見るじゃないですか。あれ、自分はどうにも好きになれないというか、後進を自分がたどってきた道に引きずり込みたいだけじゃねーかと思うのが多いです(全部がそうだとは言いません)。なので、今回のエントリでは、あくまで、「私が好きな」技術書籍を10冊紹介することにします。単に思い出深いというだけの書籍も入っており、間違っても「オススメ書籍」ではないのでご注意を。

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)

オブジェクト指向入門 第2版 原則・コンセプト (IT Architect’Archive クラシックモダン・コンピューティング)

オブジェクト指向入門 第2版 方法論・実践 (IT Architects' Archiveクラシックモダン・コンピューティング)

オブジェクト指向入門 第2版 方法論・実践 (IT Architects' Archiveクラシックモダン・コンピューティング)

最初は、メイヤー先生の『オブジェクト指向入門」第二版。ちなみに第一版は今からすると内容が古すぎるので読むのはやめた方がいいです。この本の何が面白いって、メイヤー先生の強烈な思想がそこかしこから伝わってくるところです。特に、序盤の方の、「方法論」批判はとても痛烈で、「どうしても必要な場合にのみそれを使うこと」といった謎のアドバイスに疑問を持ったことのある方は読む価値があります。

ただまあ、DbCとかのコンセプトを既に知っている読者にとっては退屈な部分も多いであろうことも一方で否定しません。メイヤー先生の独特の語りが好きな人は読みましょう。ちなみに、この本、タイトルに偽りありの代表格であり、全然オブジェクト指向入門ではありませんし、メイヤー先生のOOPの定義はやや独特なので(ただし、本文中でちゃんと「定義」しているのがエライところ)、その点は注意です。

次は、Date先生の「データベース実践講義」です。この本もタイトルに偽りありというか、まったくもって現在のいわゆるRDBMSを扱うために有用な知識は身につきません。この本は読み物として読むのが正解というか、Date先生のSQL & 今のRDBMSディスり芸のために読むのが正解な気がします。といっても、Date先生、リレーショナルモデルの祖であるCoddと一緒に働いていた経験があるだけあって、いい加減なことを書いているわけではなく、現在のいわゆるRDBMSSQLがいかにリレーショナルモデルからかけ離れているかを口を酸っぱくして語ってくれます。この本でいわゆる現在のRDBMSSQLを扱うのに必要な知識は「ほとんど」身につきませんが、本来のリレーショナルモデルと現在の実装との差異について知りたい方にとっては色々発見があるのではないでしょうか。

Parsing Techniques (Monographs in Computer Science)

Parsing Techniques (Monographs in Computer Science)

超マイナーな上に日本語訳もされていませんが、次はParsing Techniques 2nd Editionです。この本、Parsing Techniquesとタイトルにあるのは伊達ではなく、LLやLR、CYKといった古典的なパーザはもちろん、PrologのDCGや、recognition based grammar、parallel parsing、boolean grammarといったマイナーな(?)話題に至るまで、構文解析に関する話題のかなりをカバーしています。一家に一冊あると安心できる、そんな書籍です。

Programming in Scala: A Comprehensive Step-by-step Guide

Programming in Scala: A Comprehensive Step-by-step Guide

おなじみコップ本の原著です。あえて原著を選んだのは、最初にこの本を読んだのが原著であり、また、初めてのScala DaysでOdersky先生にサインをもらったという思い出深いものだからです。内容についてはコップ本の書評をみれば色々あるでしょうから割愛します。

C言語ポインタ完全制覇 (標準プログラマーズライブラリ)

C言語ポインタ完全制覇 (標準プログラマーズライブラリ)

次は前橋和弥さんの「C言語ポインタ完全制覇」です。C言語のポインタの文法がどうにもよくわからんなーと頭をひねっていた大学1年生の頃、この本を読んだことがその後のC言語の理解にとてもとても役立ちました。内容については記憶している限りではANSI C 89ベースなので、今だとやや古い記述もありますが、大筋で今でも役に立つ内容だと思います。

Java 謎+落とし穴 徹底解明 (標準プログラマーズライブラリ)

Java 謎+落とし穴 徹底解明 (標準プログラマーズライブラリ)

次も同じく前橋和弥さんの「Java 謎+落とし穴 徹底解明」です。実は、この本を読む時点で、既に書いてある内容についてはあらかた知っていたので、前橋さんのJavaに対する語りを読みたくて買ったようなものです。まあ、正直言うと、この本に書いてある落とし穴の大部分は現在のJavaだと克服されていますし、克服されていないものも、まあ慣れれば大したことがないものが多い気がしますが、前橋さんのJavaディスりが面白く、ついつい読みふけってしまったのを覚えています。

Bruce EckelのJavaプログラミングマスターコース―徹底探究!Javaのしくみとオブジェクト作法〈上〉

Bruce EckelのJavaプログラミングマスターコース―徹底探究!Javaのしくみとオブジェクト作法〈上〉

  • 作者: ブルースエッケル,Bruce Eckel,安藤慶一
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 1999/10
  • メディア: 単行本
  • 購入: 1人 クリック: 8回
  • この商品を含むブログ (5件) を見る

Bruce EckelのJavaプログラミングマスターコース〈下〉徹底探究!Javaのしくみとオブジェクト作法

Bruce EckelのJavaプログラミングマスターコース〈下〉徹底探究!Javaのしくみとオブジェクト作法

  • 作者: ブルースエッケル,Bruce Eckel,安藤慶一
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 1999/10
  • メディア: 単行本
  • 購入: 1人 クリック: 2回
  • この商品を含むブログ (5件) を見る

これは良書かというと必ずしもそうではないし、内容も今となっては古いですが、自分を本格的なプログラミングの道に誘ってくれた忘れがたい書籍です(ちなみに著者のBruce Eckel氏は今はもうJavaを見限った(?)ようで、Atomic Scalaという書籍を出したりしています。ご本人にScala Daysで会ったりもしました)。この本でデザインパターン病にかかったのもとても思い出深いです。ちなみに、デザインパターン病はOOPを学ぶ過程でかかる人が多い病で、学生のうちに経験しておくと予後不良にならずに済みます。

Introduction to Algorithms (MIT Press)

Introduction to Algorithms (MIT Press)

  • 作者: Thomas H. Cormen,Charles E. Leiserson,Ronald L. Rivest,Clifford Stein
  • 出版社/メーカー: The MIT Press
  • 発売日: 2009/07/31
  • メディア: ペーパーバック
  • 購入: 5人 クリック: 90回
  • この商品を含むブログ (19件) を見る

これは、邦訳が既に出ていますが、自分が研究室の輪講でやったのは原著だったので、原著を掲載しています。"Introduction"とついていますが、日本語の入門本にあるようなテキトーな感じのものではなく、様々なアルゴリズムについて、割と詳しくみていく本です。入門系の本としては、計算量を"証明"する課題などがあるのは割と珍しいかもしれません(英語の本だとよくありますが)。この本、研究室の輪講の期間では終わらなかったので正直言って紹介するのは気がひけたのですが、アルゴリズムに対する自分のアプローチのあやふやさを認識することになったという意味で良い本でした。

Purely Functional Data Structures

Purely Functional Data Structures

知る人ぞ知る名著、Purely Functional Data Structures(通称PFDS)です。これについては、k.inabaさんの(半分)ネタ解説を読む方がよっぽど面白いのでここでの紹介は割愛します。

詳説 正規表現 第2版

詳説 正規表現 第2版

これも思い出系ですが、日本語の正規表現解説書籍としては一時期、かなり一般的だったのではないでしょうか。大学2年の頃、簡易正規表現エンジンを作ってみようとして(そして作りました。NFAベースのごく簡単なマッチャですが)買ったのがこの本だった記憶があります。様々な処理系における正規表現の違いや、NFA、POSIX NFA、DFAの違いなど、一通り解説してあります。

今だと、「正規表現技術入門」

正規表現技術入門 ――最新エンジン実装と理論的背景 (WEB+DB PRESS plus)

正規表現技術入門 ――最新エンジン実装と理論的背景 (WEB+DB PRESS plus)

の方がいいかもしれません。

というわけで、あやふやな記憶を頼りに、思い出深い技術書籍を10冊無理やり紹介してみました。

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するには、今のところこれが最も手っ取り早い方法だといえそうです。