プログラミング言語の基礎概念を学んでる

プログラミング言語の基礎概念 (ライブラリ情報学コア・テキスト)

プログラミング言語の基礎概念 (ライブラリ情報学コア・テキスト)

この本を読んで学んでる。まだ半分くらいで関数の定義とかについて勉強してる。

プログラミング言語の動作を数学的に厳密に記述する方法を順番に教えてくれるという内容で、記述には導出システムが用いられてる。基本的な算術式からはじまって、変数の定義や関数の定義、パターンマッチや型システムなど、様々な言語の機能を推論規則によって定義する方法を教えてくれる。与えられた規則が意味的に意図したものを表しているかの証明だけでなく、証明のやり方もくわしく説明されていて丁寧でたすかる。

おもしろいのはこの本のためのオンラインの演習システムというのがあって、本の中で与えられた導出システムに基づいて式が正しいことを導出する練習問題をすることができる。サイト内に検証器がうごいていて、自分が与えた導出が正しいかを判定してくれる。どこまで問題を解いたか記録してくれたりもするし、ゲームっぽい感じで勉強できておもしろい。

プログラミング言語の基礎概念 演習システム

雰囲気だけお伝えすると、2 + 0 が 2に評価できることを導出するために、以下の様に書くと正解という感じ。S(S(Z))ていうのはペアノ自然数の表現で、チャーチ数みたいなやつ。by E-Plus みたいなのはどのような推論規則を適用したかという雰囲気。詳しくは本読むか演習システムのドキュメント読むと雰囲気わかる。

S(S(Z)) + Z evalto S(S(Z)) by E-Plus {
    S(S(Z)) evalto S(S(Z)) by E-Const {};
    Z evalto Z by E-Const {};
    S(S(Z)) plus Z is S(S(Z)) by P-Succ {
        S(Z) plus Z is S(Z) by P-Succ {
            Z plus Z is Z by P-Zero {}
        }
    }
}

そんなに複雑な問題はないんだけど、たまにきついのがあるっぽくて油断できない。以下の様な式が正しいことを導出するっていう問題があって、たしかに面倒そうな気はするなーと思ってとりくんだら、めっちゃ大層なことになった

let twice = fun f -> fun x -> f (f x) in twice twice (fun x -> x * x) 2 evalto 65536

問題を解くのは難しいわけではなくて、ルールにしたがって丁寧に間違えずに式を変換していけば、いずれ導出がえられる。ただ、少しでもまちがえると全部おかしくなるので、まちがえずに丁寧にやる必要がある。同じようなことを何度も丁寧にまちがえずにやる必要があって、コンパイラの気持ちが体験できる。

ScalaでWebアプリケーションのエラー処理を綺麗に書く

Play Frameworkにおいて、POSTリクエストから得られたbody中のパラメータをもとに何か処理をするというよくあるコードを、ちょっと整理して見やすくする方法を学んだのでメモがてら御シェアさせていただきます。Playのリクエストハンドラを書くときに頻繁に現れたので、例がPlayのコードになっているけど、内容的にはScala全般的な話だと思う。Scalaプロみたいな人にはまったく新しいことはないと思う。

本題と関係ないけど、YAPCでScalaの話をするかもしれません。言語自体の話よりかは採用理由とか開発フローの話を、これまでのPerlでのWeb開発を踏まえて話す感じになりそう。Scala In Perl Company : Hatena - YAPC::Asia Tokyo 2014

さて、本題ですが、話題の対象になるのは以下の様なPlayFrameworkのコードです。

def update = Action { implicit req =>
  req.body.asFormUrlEncoded.fold(BadRequest("Wrong POST body")) {
    data =>
      Form(tuple( "id" -> number, "title" -> text, "body" -> text )).
        bindFromRequest(data).value.fold(BadRequest("Wrong parameters")) {
          case (id, title, body) =>
            find(id).fold(NotFound("No entry found")) {
              entry =>
                save(id, title, body)
                Ok("Success!")
            }
      }
  }
}

上の例では、以下の3つの処理が、Option型の値を返却します。返却されたOption型の値がNoneだった場合( = 計算に失敗した場合)、処理を中断してエラーレスポンスを返しています。

(Scalaでは失敗するかもしれない計算結果はOption型に包んで扱います。Option型を使うことで、値が存在することを確認しないかぎり、包まれた値を利用できません。詳しくはゆるよろさんの記事が詳しいのでおすすめです。)

  • req.body.asFormUrlEncoded(PlayのAPI) bodyをapplication/x-www-form-urlencodedとして読み込みパラメータを得る
    • 結果がNoneだったら: "Wrong POST body"という内容のBadRequestレスポンスを返す
  • Form(...).bindFromRequest(data).value(PlayのAPI) によりFormの定義にbodyから得られたパラメータを結びつけて値を得る
    • 結果がNoneだったら: "Wrong parameters"という内容のBadRequestレスポンスを返す
  • find(id)(独自に定義した関数) によりパラメータから得られた値を使ってentryを得る
    • 結果がNoneだったら: "No entry found"という内容のNotFoundレスポンスを返す

Option型を処理するのには比較的コンパクトにかけるfoldメソッドを利用しています。Option型のfoldメソッドはOption型の値がNoneだった場合は一つ目の引数リストの値を、Someだった場合は二つ目の引数リストに与えられた関数に、包まれた値を渡して評価した結果を返します。

Option型を使うことで、漏れ無く計算が失敗した場合の処理を書くことができていますが、ご覧のとおり読みにくいです。どの部分が本来行いたい処理で、どの部分がエラー処理なのかが判別しづらくなっています。

そこで、Option型はflatMapメソッドを実装していますから、forを使って整理してみることにします。

def update2 = Action { implicit req =>
  (for {
    data <- req.body.asFormUrlEncoded
    form <- Some(Form(tuple( "id" -> number, "title" -> text, "body" -> text )))
    (id, title, body) <- form.bindFromRequest(data).value
    entry <- find(id)
  } yield (id, title, body)) match {
    case None =>
      BadRequest("Something wrong")
    case Some((id, title, body)) =>
      save(id, title, body)
      Ok("Success!")
  }
}

ご覧のようになりました。パラメータの準備と検証をforの中で行い、いずれかが失敗した場合にはcase分のNoneの節が、すべて成功した場合にはSomeの節が実行される、という読むことができます。正常系と異常系のコードを分けて読むことができ、比較的わかりやすくなりました。

ただ、この例には問題があります。Option型の値を連ねたfor文からは、計算が失敗した場合に、いずれかの計算が失敗したことはわかりますが、どの計算が失敗したのかがわかりません。そのためエラー処理を分岐できず、とりあえず、"Something wrong"という内容でBadRequestを返すという雑なエラー処理をするはめになっています。

さて、このような計算が失敗したときの理由も取り扱いたいときには、Either型を使うのが便利であると、すごいHaskellたのしく学ぼう!にはありました。独習ScalazのEitherの解説をよめば、Scalaでの使い方もわかります。

ここではOption型の値をtoRightメソッドを使って、Either型に変換し、それぞれの計算で別のエラーを取り扱えるようにしてみます。ScalaではEither型にはflatMapが実装されていないので、Either型のrightメソッドを呼び出して、Either型のサブクラスのRightProjection型に変換して使います。RightProjection型のflatMapメソッドは値がRightである場合に計算を継続します。

Option型の値をRightProjection型の値に変換することで、計算結果がRightであれば計算を継続し、Leftの場合は計算を途中で中止してその値を返すという振る舞いを実現できます。

def update3 = Action { implicit req =>
  (for {
    data <- req.body.asFormUrlEncoded.toRight(
      BadRequest("Wrong POST body")).right
    form <- Right(Form(tuple( "id" -> number, "title" -> text, "body" -> text ))).right
    params <- form.bindFromRequest(data).value.toRight(
      BadRequest("Wrong parameters")).right
    entry <- find(params._1).toRight(
      NotFound("No entry found")).right
  } yield params) match {
    case Left(error) =>
      error
    case Right((id, title, body)) =>
      save(id, title, body)
      Ok("Success!")
  }
}

ご覧のようになりました。計算の見通しが良いまま、それぞれの計算ごとに失敗した場合の計算結果を返すことができるようになりました。RightProjection形のfilterメソッドの制限か何かで、forの中でパターンマッチが使えないのが惜しい。

追記: id:xuwei さんに教えていただいたところによると、Eitherについて自然なfilterを定義できない(Optionを返すようなMonadPlusに適合しないシグニチャになってる)ために、forの中で利用できないとのことでした。ありがとうございます。参考: HaskellのdoとScalaのfor式とEitherとMonadPlus - scalaとか・・・

追記2: ScalazのEither型相当の\/型はfoldMapはRight優先で実装されているので、それを使うとちょっとスッキリする。

import scalaz._
import Scalaz._

def update5 = Action { implicit req =>
  (for {
    data <- req.body.asFormUrlEncoded \/>
      BadRequest("Wrong POST body")
    form <- Form(tuple( "id" -> number, "title" -> text, "body" -> text )).right
    params <- form.bindFromRequest(data).value \/>
      BadRequest("Wrong parameters")
    entry <- find(params._1) \/>
      NotFound("No entry found")
  } yield params) match {
    case -\/(error) =>
      error
    case \/-((id,title,body)) =>
      save(id, title, body)
      Ok("Success!")
  }
}

インデントが深くなるのが嫌であれば、エラーが発生した時点でreturnすれば分かりやすいしええやん?という風にも思えます。例えば以下の様になります。

def update4 = Action { implicit req =>
  def handle: Result = {
    val dataOption = req.body.asFormUrlEncoded
    if (dataOption.isEmpty) {
      return BadRequest("Wrong POST body")
    }
    val form = Form(tuple( "id" -> number, "title" -> text, "body" -> text ))
    val valueOption = form.bindFromRequest(dataOption.get).value
    if (valueOption.isEmpty) {
      return BadRequest("Wrong parameters")
    }
    val (id, title, body) = valueOption.get
    val entryOption = find(id)
    if (entryOption.isEmpty) {
      return NotFound("No entry found")
    }

    save(id, title, body)
    Ok("Success!")
  }
  handle
}

たしかに、シンプルな手続きの並びでわかりやすくはあります。ただし、Option型の中の値が入っているかをチェックする部分と使う部分が別になってしまい、コンパイル時に値の取り出しが安全かどうかを判定できなくなってしまいました(getを使っているためランタイムエラーの可能性が残る)。わざわざコンパイラによるチェックがされない方法をとるメリットはなさそうです。Actionが引数にクロージャをとる都合上、returnを使うために内部に関数を定義しないと行けないのもつらいところですね。

まとめ

  • Either便利
  • for文便利
  • 追記: Scala本体に付いているEither型はちょっと微妙。Scalazについているやつは便利

とりあえずfor文をつかっておしゃれに書きたい期に突入してると思う。

"Scala RightProjection"とかでググってたら、エラー処理をいい感じに書く議論がScalaハッカーの方々の間で執り行われているのを見つけて、めちゃくちゃ参考になった。RightProjection以外のいろんな方法について言及されている。

Scalaハッカーの方々による議論

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

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

  • 作者: Martin Odersky,Lex Spoon,Bill Venners,羽生田栄一,水島宏太,長尾高弘
  • 出版社/メーカー: インプレスジャパン
  • 発売日: 2011/09/27
  • メディア: 単行本(ソフトカバー)
  • 購入: 12人 クリック: 235回
  • この商品を含むブログ (46件) を見る
すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

関西2014年夏アニメ 放送時間まとめ

今期も関西における今期のアニメの放送状況を表にまとめました。いつもどおりしょぼいカレンダーのデータを利用させていただいています。ありがとうございます。予約設定時の確認などにお役立てください。

今期の関西最速は三作品となりました。

今期は最速が3作品のみとなってしまいました。残念です。来季におおいに期待しましょう!

ペルソナ4 ザ・ゴールデンは、同名のゲームのアニメ化で、2011年の秋アニメとして放送されたペルソナ4の関連作品です。僕は原作をプレイしていないので、ザ・ゴールデンではどういった展開になるのかが楽しみです。新キャラがきになる感じですね。

黒執事 Book of Circusは、黒執事シリーズ三度目のアニメ化です。すごい人気ですね..。僕はあまりくわしくないですが、宮野真守の活躍に期待したいです。

モモキュンソードはもともとはWebサイトで声優の方が、オリジナルの物語を朗読してくれる企画だったらしいのですが、その物語のストーリーがアニメ化したということのようです(もともとはパチンコのシリーズだった?) 。出自がちょっと変わっているだけにどういう作品になるのか楽しみですね。

関西勢には少々厳しい期とはなりましたが、くじけずに今期もがんばりましょう!

関西での放送開始が遅いばらかもんの放送情報は以下になります。


TypeScriptリファレンスを読んだ

チームでTypeScriptを使う機運の高まりを感じたので、最近出てイケてると言ううわさの
TypeScriptリファレンスを読んでみた。

TypeScriptリファレンス Ver.1.0対応

TypeScriptリファレンス Ver.1.0対応

本の内容は、TypeScriptの基本的な文法や開発環境などについて、網羅的に教えてくれるというものだ。JavaScriptについてあまり知らない人も読めるようになっているので、JavaScriptを使っている人であれば、分厚さの割には素早く読める(本文にもこの章は知ってたらとばしてよいと書いてある)。特に、TypeScriptをしっかり使った上でないとわからないような、実用上のアドバイスも書かれていて参考になる。例えば、型システムの微妙な動作をうまく回避する方法や、なぜそういった動作になっているかが、TypeScriptの開発コミュニティの議論を踏まえて解説されていたりする。

本書を読むと、JavaScriptととの互換性を壊さずに型システムを導入するために、涙ぐましい努力をしまくった結果がTypeScriptなのだということがわかる。

TypeScriptの言語機能は、JavaScriptからみると、型システムに関係ない部分は最低限しか追加されていない。classやmoduleの機能は追加されているが、ECMAScript 6などの標準を見据えているし、いきなりパターンマッチが使えるようになったり、部分適用がサポートされたりはしていない。実際、TypeScriptにはランタイムがほぼ必要なく(継承を使った時だけ6行だけランタイムが出力される)、比較的シンプルなJavaScriptのコードに変換されるだけだ。

型システムに関しては、変数やメソッドに型アノテーションを記述できたり、interfaceを定義することができるようになったりと、多くの言語機能が追加されるが、これらはコンパイラが型チェックに利用するだけで、最終的に出力されるJavaScriptのコードには現れない。型を削れば、素のJavaScriptとして使えますってのを目指してるんじゃないかと思う。

JavaScriptのライブラリを使うための型定義の記法が充実しているのもおもしろい。JavaScriptのオブジェクトは、たいへん柔軟なので、例えば、関数オブジェクトにプロパティを代入することで、関数としても呼び出せるし、モジュールのようにも使うことのできる。本書で例にあげられていたjQueryの$オブジェクトは$("#content")のように関数としても呼び出せるし、$.ajax(...)のようにモジュールのようにも使うことができる。$オブジェクトのTypeScriptの型定義を本書から引用したのが以下のコードだ。

interface JQueryStatic {
  (selector: string, context?: any): any; // $("#content") のような関数としての型定義
  ajax(url: string, setting?: any): any;  // $.ajaxの定義
}

declare var $: JQueryStatic;

こういった、複雑で型をとらえずらいオブジェクトにも型を与える方法があって、徹底してると思う。引数の型が変わるオーバーロードされた関数の定義の方法なんかもあって、JavaScriptの構造に型を与えることに対する情熱を感じる。DefinitelyTyped/DefinitelyTyped: The reposi... - GitHubが便利。

本書を読んだおかげで、TypeScriptがどういった言語を目指しているのかが理解できたように思う。チームに導入しても良さそうかという判断をするのには調度よかった。TypeScript自体については、言語機能に派手さはなくて無難という感じだけど、JavaScriptに型システムを載せるということについて、徹底して取り組んでいると感じた。伊達にTypeってついてなかった。ECMAScriptに型システムが追加されるということは、しばらくはなさそうだし、Dartのような代替言語がすべてのブラウザで実行できるようになるのも先に思う。純粋にJavaScriptに型システムがつきましたよということであれば、受け入れられやすく、ECMAScript 6世代くらいまではメンテされていて使っていけそうだなと感じた。

Kinedle版もあった

TypeScriptリファレンス Ver.1.0対応

TypeScriptリファレンス Ver.1.0対応

UNIXという考え方読んだ

誕生日に id:aereal さんに頂いた本。ありがとうございます!

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

たいへん有名な本。原著は1994年に書かれていて、古典という感じ。

大きな一枚岩のソフトウェアよりも、cat、sort、uniq、sedやawkのような小さくて便利なツールを組み合わせることが好まれる。UNIXの世界には、こういった、UNIX開発者やユーザがが大事にしている哲学があり、そのおかげで、この本が書かれた1994年、そして現代においてもUNIXが広く使われることになった。この本では、そういったUNIXの考え方のエッセンスの部分を9つの定理にまとめてとして詳しく教えてくれる。

  • スモール・イズ・ビューティフル
  • 一つのプログラムには一つのことをうまくやらせる
  • できるだけ早く試作を作成する
  • 効率より移植性
  • 数値データはASCIIフラットファイルに保存する
  • ソフトウェアの挺子(てこ)を有効に活用する
  • シェルスクリプトを使うことで挺子(てこ)の効果と移植性を高める
  • 過度の対話的インタフェースを避ける
  • すべてのプログラムをフィルタにする

いくつか、おもしろかった部分をピックアップしてみよう。

第一のシステム、第二のシステム、第三のシステム

"できるだけ早く試作を作成する"について説明してくれる3章で説明されている第一、第二、第三のシステムの話が特におもしろい。曰く、人間にはこの三種類のシステムしか作れない。

第一のシステムは、一人もしくは少人数が、時間に追われる中で創造性を発揮し、勢い良く作ったシステムだ。時間追われたために、足りない機能もあるが無駄はなく効率的に動く。第一のシステムまったく新しい考え方を導入するので、はじめは世間には相手にされない。

しかし、第一のシステムが成功しはじめると、多くの人が集まってくる。そして、第一のシステムにより有用性が証明されたアイディアをもとにした新しいシステムが作られる。これが第二のシステムだ。第二のシステムは第一のシステムに目をつけた多くの参加者からなる委員会が機能を決定する。その結果として、多くの人の意見を取り込みすぎため、機能は多いが、無駄のある遅いシステムができあがる。多くの人に期待された第二のシステムは時には商業的にも成功するが、実はあまり役に立たない。

第二のシステムの失敗に反省した人々が、第三のシステムを構築する。第三のシステムだけが、第一のシステムが発見したアイディアと第二のシステムの中で見つかった必要な機能をバランスよく取り入れて、やっと役に立つシステムを構築することができる。

UNIXの考え方では、なるべくはやく第三のシステムを構築するために、すばやく試作することをおすすめしている。直接、第三のシステムをつくることはできないのだ。

必ずしもこうだと言い切れるものではないだろうが、実感としては同意できる。よくできたと思えるソフトウェアが一度にできることはない。イテレイティブな開発が現代のアジャイル開発手法では推奨されているけども、1994年の時点でもこのような話があるのはおもしろい。

ソフトウェアに完成はない

ソフトウェアには常に改善の余地はあるのはもちろんだし、時間的な制約などでそのソースコードは必ずしも最高の状態が保たれているわけではない。曰く、ほとんどのソフトウェアは妥協の産物だ。完成することはなく、ただリリースがあるだけだ。

ソフトウェアや計算機が常に進化していることを意識しなければならない。UNIXの考え方はソフトウェアの進化にも強い。ソフトウェアを小さくてシンプルな部品に分割すれば、将来の変更に容易に対応できるし、ソフトウェアを容易に移植可能にしておけば、来年には利用できるようになる、よりはやい計算機でも簡単に動かすことができる。

この話もまったく納得するはなしで、ソフトウェアを常に進化する流動的なものとして扱うというのは、現代でもホットな話題だが、普段利用しているUNIXにもその考え方が息づいているというのも面白い。

シンプルなソフトウェアのコンポーネント化手法

小さなソフトウェアを組み合わせる方法がシンプルであることもUNIXでは重要だ。UNIXではパイプを通じたプロセス同士の通信の仕組みがあって、多くのソフトウェアがこのシンプルなインタフェースに準拠してる。第8章ではMHを例に複雑なメールアプリケーションが、40近くのコマンドの連携によって構築できることを示している。

ソフトウェアコンポーネントのように、ソフトウェア特定の機能群を部品として使う方法を標準化して、それらを組み合わせるだけでアプリケーションを構築できるようにするというアプローチはいろいろある(CORBAとか)が、大成功している例はあまりみない。たぶん気楽に試すには難しすぎるのではないかと思う。

UNIXのプロセス同士をパイプでつなげて通信する方法は、ソフトウェアコンポーネントほど複雑なことはできないが、そのシンプルさ故にアプローチとしては成功している。これくらいシンプルだと、ちょっと自分でも試してみようと思うし、シンプルなわりに強力なことができてしまうので、人気がでてエコシステムも潤う。バランスの妙という感じで真似できるならしたい。

まとめ

この本は、UNIXをお手本にしたソフトウェア工学について書かれた本だとも言えると思う。古典だが現代に通じる考え方が多くて参考になる。UNIXに慣れ親しんでいるひとは、あらためて読む必要はないかもしれないが、よくまとまっているのでポイントを整理するのにも良い。うすいしすぐ読める。

ときどき時代背景がわかる記述があるのもおもしろい。CD-ROMが今後のソフトウェア業界を大きく変化させるであろう、だとか当時の時事ネタを楽しめるのもおすすめ。

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

はじめてのClojure読んだ

はじめてのClojure (I・O BOOKS)

はじめてのClojure (I・O BOOKS)


nyampassの登尾さんの著書ということで読ませていただいた。URL短縮Webアプリケーションの実装を通じて、Clojureの基本や開発の方法を一気に学べる本。

全体としては、内容がすごく丁寧にかかれているので読みやすい。この本で特に良かったのは、Clojure界でのWebアプリケーション作成がすぐに開始できるようになっているところだ。例えば、Clojure界におけるWSGI/Rack的なものであるRingについてや、Leingenと連携した開発フローなどについて、簡潔に説明されていて、Clojure界のいまどきのWebアプリケーション開発手法に効率良く入門できる。

新しい言語を学ぶときは、文法や機能について知りたいのと同時に、その分野で流行っているライブラリや開発環境なども知りたいのでとても助かる。

一方、Clojureの言語自体のくわしい説明はあまり多くないので、言語自体に興味がある場合は別の本も読んでみたほうが良さそうだった。Clojureの本はあまりしらないけど、7つの言語 7つの世界にはloopの文法やSTMの解説がのっていた。

まず手を動かしてClojureでWeb開発をしてみたいという場合に読むととても良さそうな本だった。にゃんぱす〜

カラフルFizzBuzz

f:id:hakobe932:20140611222819p:plainなんかかわいいような気がする。Clojureの入門に書きました。

f:id:hakobe932:20140609223757p:plain

括弧の色の感じとか、縦に揃ってる感じとかそういうのです。

u (id:ujihisa)

1, 2など数字部分の出力もできるようになる修正を行うと聞いて

まじだ!! これははずかしい!

f:id:hakobe932:20140610003857p:plain

まとまりが悪くなった気がするのが残念。

f:id:hakobe932:20140610091728p:plain

うむ

空文字列ではなくnilでよいとのこと!

f:id:hakobe932:20140611222819p:plain