読者です 読者をやめる 読者になる 読者になる

ux00ff

ビールとプログラミングと

Scala x Matsuri

2月25日、2月26日にお台場で開かれたScalaMatsuri2017に参加してきました。初参加です。

とはいえスポンサーブース側でブースに張り付いていたので講演は聞けず、惜しいことをしました。後から資料を見て楽しむことにします。

ユーザー母数としてはけっして規模が大きいわけではないScalaという単一言語のイベントでありながら、運営母体がすごくしっかりしていて参加者も愛に溢れており、心から楽しむことができました。コミュニティの関心のノリがいろいろわかって、濃い人とお話もできて、実に濃密な二日間でした。スタッフの練度も高く、不愉快に感じたことは全くなかったと言っていいくらいです。それにしても同時通訳すごかったなぁ。

f:id:ux00ff:20170310115457j:plain

写真は公式Twitterのエントリからパクってるわけですが、ブースはこんな感じで出していました。今回スポンサーブースで建てた MacBook Proのプレゼント企画、課題として設定したクロスワードはおおむね好評だったけど、日本語読めない勢がちょっと残念そうだったので、次回は日本語に依存しないコンテンツ作らないとなと痛感しました。こんなに国際色豊かだとは思っていなかった。

これまではあまり技術系のコミュニティイベントに参加することはなかったのだけど、けっこう熱気に当てられました。いいもんですね。こういうの。有名人に全く知らずに話を振って苦笑されるシーンもたまに。講演者の @niw さんに「今日は何を目当てに来られたんですか」と聞いてしまったり、・・・いや、最大の失敗はきの子さんに「本名ですか」と聞いてしまったことです。思い出すと悶えるしかない。あるある。

Right value is right

Either型で成功と失敗を表すとき、どうして慣習的にLeftがエラー情報、Rightが正常系なんだろうと不思議だったんですが、Right(右)とRight(正常な)がかかってるっていうことを知って地味に感動しました。ダジャレか。

個人的には最初、先に(左側)正常値を書くのが自然かなと思っていたんですが、コードで戻り値をチェックする際 Right で正常というのは言われてみたら納得感あります。

scala> def div(c:Int ,d:Int) :Either[Exception, Int] = {
     |   d match {
     |     case 0 => Left(new Exception("d is 0"))
     |     case _ => Right(c/d)
     |   }
     | }

scala> div(5, 2)
res34: Either[Exception,Int] = Right(2)

scala> div(10, 0)
res36: Either[Exception,Int] = Left(java.lang.Exception: d is 0)

トレイトと自分型を利用してPAppletを分割する

引き続きProcessingです。自分型を設定したトレイトを利用すると、トレイトを実装する側のクラスを縛ることができます。Scalaスケーラブルプログラミングに「大きなクラスをトレイトを利用して分割するときにめっちゃ便利だよ」と書いていたが確かに便利ですね。

rubyの「おっと Enumerable をミックスインしたんだけど each メソッドがないよ?」みたいなことを、こちらはコンパイラが弾いてくれるのだな的理解で先へ進みます。

以下のようなデバッグ情報を表示するトレイトを作ったとして、

trait DebugView {
  self: PApplet =>

  def showDebugView(): Unit = {
    fill(255)
    textSize(13)
    textAlign(LEFT, TOP)
    text(s"(x, y) : ($mouseX, $mouseY)", 0, 0)
    text(s"${millis} ms", 0, 15)
    text(s"${frameRate} fps", 0, 30)
    text(s"${frameCount} frame count", 0, 45)
    text(s"mouse => ($mouseButton) key => ($keyCode)", 0, 60)
  }
}

利用側はこんな感じにします。

class Applet extends PApplet with DebugView {
  override def settings(): Unit = size(320, 240, JAVA2D)

  override def draw(): Unit ={
    background(0, 0, 100)
    showDebugView()
  }
}

f:id:ux00ff:20170221235542p:plain

トレイト側で self: PApplet => しているため、この DebugViewPApplet かまたはそのサブ型からしか利用できず、その制約の中で showDebugView のメソッド内では PApplet のメソッドを呼び出すことができます。

ProcessingをScalaで書く用のgiter8テンプレート

学習も兼ねて Processing 用のプロジェクトテンプレートを作って見ました。「scala-processing g8」 で検索しても最近のものが出てこなかったのでこれはきっと需要があるのだろうと信じて(苦しい)

コードはこちらに置きました。

sbt がインストールされている状態で、以下コマンドで Processing プロジェクトを作成することができます。なお sbt は公式通り brew でインストールしました

$ sbt new ma2saka/scala-processing.g8
続きを読む

読書の春

Scalaスケーラブルプログラミング第3版、なんだかんだで読み終わるまでに四日くらいかかった。良い本でした。 やっぱり紙の方が読みやすい・・・んだけど、お風呂で読んでいたのでちょいちょいふやけていたりします。

普通にJavaからの置き換えで学習してるだけだとたどり着かないあたり、for式の書き換えの理屈や implicit の使われ方、抽象メンバとトレイトの組み合わせ(withの前に初期化ブロック置く設計とか…)、それにLISTの実装解説はテンション上がった。少しだけ言語設計者やコンパイラの気持ちになれる気がします。ただあくまでも標準のコンパイラと実行系、そしてなにより言語の本であって、周辺のエコシステム周りについての情報は全くない。こちらは別にやっていかないとだ。どっから手をつけたものかしら。

Scalaスケーラブルプログラミング第3版

通称コップ本。版を重ねるごとにコップが増えている。どうも Better Java っぽい書き方が抜けないなーと思って、買った。

第三版は2016年出版で2.11、2.12対応。

自分はだいたいプログラミング言語の基礎はオライリーの「プログラミング○○」から入るんだけど、Scalaの場合は2.7, 2.8対応版で止まっていて、ちょっと古いんですよね・・・ということでこちらをチョイス。

最初から順に読んでるけど、ところどころ気の利いた Tips が差し込まれていたり、ためになる訳注が多かったりしていい本感ある。なにより愛が溢れてる。各所から「ほらね、Scalaすごいでしょう?」っていう嫌味のないドヤ感が伝わってきて読んでいて楽しい。ざっと読んでから、ゆっくり読み直そう。

Scalaでコンウェイのライフゲーム with Processing

定番ということで、書いてみました。ライフゲームについては Wikipedia の記事がわかりやすいので参照してください。本当に Better Java (というか普通に手続き的) な書き方ができてしまうところに面白みというか味を感じます。昔はライフゲーム書くときは二次元配列を多用していたけど、最近は一次元配列の方が自然に思える。ふしぎ。

f:id:ux00ff:20170212125812p:plain

フレームレートを明示的に指定することで世代交代をちゃんと眺めることができます。普通に実行すると一瞬で安定するので面白くありません。

シンプル版

import processing.core.PApplet
import processing.core.PConstants._

class App extends PApplet {
  private[this] val CELL_SIZE = 10
  private[this] val W = 50
  private[this] val H = 50
  private[this] val arr = new Array[Boolean](W * H)
  private[this] val tmp_arr = new Array[Boolean](W * H)

  override def settings(): Unit = {
    size(W * CELL_SIZE, H * CELL_SIZE, JAVA2D)
  }

  override def setup(): Unit = {
    frameRate(5)
    fill(60)
  }

  override def mousePressed(): Unit = {
    for (x <- 0 to W * H - 1) {
      arr(x) = if (random(1.0f) < 0.5f) {
        true
      } else {
        false
      }
    }
  }

  override def draw(): Unit = {
    background(200)
    write_lines()
    update_cells()
    write_cells()
  }

  private[this] def write_lines() = {
    for (x <- 0 to W * CELL_SIZE by CELL_SIZE) line(x, 0, x, height)
    for (y <- 0 to H * CELL_SIZE by CELL_SIZE) line(0, y, width, y)
  }

  private[this] def update_cells(): Unit = {
    for (i <- 0 to W * H - 1) {
      var c = 0
      // 左側
      if (i % W > 0 && arr(i - 1)) {
        c += 1
      }

      // 右側
      if (i % W != W - 1 && arr(i + 1)) {
        c += 1
      }

      // 上、右上、左上
      if (i / W > 0) {
        if (arr(i - W)) c += 1
        if (i % W != W - 1 && arr(i + 1 - W)) c += 1
        if (i % W > 0 && arr(i - 1 - W)) c += 1
      }

      // 下、左下、右下
      if (i / W < H - 1) {
        if (arr(i + W)) c += 1
        if (i % W != W - 1 && arr(i + 1 + W)) c += 1
        if (i % W > 0 && arr(i - 1 + W)) c += 1
      }

      // 生存判定
      if (arr(i) && (c == 2 || c == 3)) {
        tmp_arr(i) = true
      } else if (arr(i)) {
        tmp_arr(i) = false
      } else if (c == 3) {
        tmp_arr(i) = true
      } else {
        tmp_arr(i) = false
      }
    }

    tmp_arr.copyToArray(arr)
  }

  private[this] def write_cells(): Unit = {
    for (i <- 0 to W * H - 1) {
      val y = i / W
      val x = i % W
      if (arr(i)) {
        rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE, CELL_SIZE)
      }
    }
  }
}

こいつをScalaっぽく書き直すのはまたこんど。

続きを読む