2012年12月21日金曜日

ゆるふわScalaプログラミング・振り返り(7)

11月30日に開催されたBPStudy#63で「Scalaプログラミング・マニアックス」について、「ゆるふわScalaプログラミング」 といった方向性で振り返りをしています。その7はトレイトです。



トレイトの便利さをどのように紹介するのか考えがまとまらないうちに、当日となってしまったのでスライドは今ひとつ焦点の絞れていないものになってしまいました。

そういうこともあり、本ブログでもトレイトを元に以下の記事を書いてみましたが、プログラミング・モデルにおけるトレイトの本質にせまるという感じには至りませんでした。

トレイトには単に多重継承ができる、というだけでない"何か"があるんですよね。

ただ、Tipsとしては有効な内容と思いますので参考にはしていただけると思います。

トレイトについては、「Scala基礎勉強会」で聞いた:

の2つが強烈ですし、またその他定番としては:

もあります。このあたりを盛り込んだ内容にいずれ昇華させたいと考えています。

モデリング

Scalaプログラミングを通じて、トレイトには大いに触発されて、モデリングにも取り入れ、SimpleModelerにも組込みました。

実際に使ってみたところ、トレイトを上流の設計段階からモデル要素として積極的に使っていくアプローチはかなり有効ではないかと感じています。プログラミング言語側のScalaに受け口があるので、モデリングとプログラミングがシームレスにつながる点も重要です。

モデリングとトレイトというと、DCIのようなアプローチも提案されています。

こういった点も含めて、モデリング、プログラミングの双方からトレイトの活用用法、トレイトによって実現できるアーキテクチャ、プログラミング・モデルを探って行きたいと思います。

2012年12月20日木曜日

Scala Tips / トレイトと抽象クラス

クラスの多重継承の問題点は、クラスの継承関係が循環グラフになってしまった場合に、循環してかち合ってしまったクラスの扱いが非常に難しいということです。循環してかち合ってしまったクラスは一つにまとめたほうが良いのか、別管理にしておいた方が良いのか、用途によってどちらもあり得ます。これに付随して、初期化の順番やメソッド呼び出しの解決の順番など、プログラミングを複雑に解決が難しい難問があります。

こういった問題があるため、Javaでは多重継承はサポートしないという選択を行いました。また、「クラスの継承関係が循環グラフになる」という問題を持たないインタフェースという言語機能を用意して、多重継承的な用途を実装可能にしています。インタフェースは実装を持たないため、「実装部」が循環グラフになる問題が起きないわけです。

インタフェースは素晴らしい解決策でしたが、機能が絞りこまれているので用途がかなり限定されます。具体的には、実装の再利用の目的には使用できません。

トレイトは、実装部を持ちながら、継承関係の循環グラフ問題を以下の手法で回避しています。

  • コンストラクタを持たない。
  • 同じトレイトが複数指定されても、トレイト実装の実体は1つになる。

この制約をつけることで、ミックスインが可能になったのは非常に大きな成果ということができます。

一方、プログラミング時に意識しなければならないのは、トレイトにはコンストラクタがなく、コンストラクタを使っての初期化はできないということです。

この制約がどの程度のものなのか、「実装の再利用」とのトレードオフがペイしたものなのか、という点が気になりますね。

トレイトとケースクラス

以下のトレイトPartyと、このトレイトをミックスインしたケースクラスPerson, Companyの構造は頻出のパターンです。

トレイトPartで抽象変数として定義したnameを、ケースクラスPersonおよびCompanyの引数nameで"実装"しているため、このまま普通に動作します。子供(トレイトPersonやCompany)からコンストラクタ経由でパラメタを受け渡す必要はありません。

trait Party {
  val name: String
}

case class Person(name: String) extends Party
case class Company(name: String) extends Party

抽象クラスとケースクラス

これと同様のことを抽象クラスを用いて実装すると以下になります。

abstract class Party(val name: String) {
}

case class Person(n: String) extends Party(n)
case class Company(n: String) extends Party(n)

トレイト版と同じことができているだけですが、記述が少し冗長になります。特にパラメタをコンストラクタに積み直すところはプログラミング時にかなり面倒くさい部分です。

しかも、抽象クラス版では、抽象クラスであるために複数の親クラスを同時に継承、つまり多重継承を行うことができないという弱点があります。トレイト版が多重継承的なことが自在にできることと対照的ですね。

トレイト vs 抽象クラス

トレイトと抽象クラスの例を比べてみましたが、この例だけをみるとトレイトが圧倒的に便利という結論になります。

もちろん、親クラスに対して、コンストラクタで引数を渡して初期化を行うということが有用なケースも多々あるので、この極端な例だけを引き合いに出してトレイトが"圧倒的に便利"というわけにもいきません。

とはいえ、日々のScalaプログラミングの感想では、案外トレイトだけでも大丈夫なケースが多い、というのが実感です。特にイミュータブルオブジェクトの場合、コンストラクタでの初期化で複雑な処理は必要ないのが普通なので、トレイトで十分なケースが多いでしょう。

さらに、関数型プログラミング的な指向が強くなればなるほど、イミュータブルオブジェクトを多用するようになるので、トレイトの優位性が高まります。

そういうわけで、ボクが最近Scalaプログラミングをしながら着目しているのは、"抽象クラスでないといけない用途はなんだろう?"ということです。

トレイトではなく、抽象クラスでないといけない用途が明確になれば、日々のプログラミングで迷いなく抽象クラスとトレイトの選択を行うことができます。最近は、まずトレイトで実装してみて、抽象クラスでないといけないと分かった時に、抽象クラスに変更する、というアプローチをとっています。その結果、あまり抽象クラスに変換するケースはなく、案外トレイトで大丈夫という感触を得ています。

諸元

  • Scala 2.9.2

2012年12月19日水曜日

Scala Tips / トレイトでクラス分割

Scalaプログラミングの便利さを日々体感しているわけですが、その重要な要素の一つがトレイト。トレイトは関数型言語のものではなく、オブジェクト指向プログラミングの技術ですが、そのあるなしはプログラミング・モデルに大きく影響します。

Scalaにおけるクラス設計は、このトレイトの存在だけを意識するとしてもJavaのそれとは別ものということができます。

トレイトには色々な用途がありますが、最近ボクが地味に多用しているのがクラスの分割です。機能数が増えて大きくなったクラスを幾つかのトレイトに分割するだけなのですが、プログラムの見通しがよくなり、また機能間の依存関係も明確になるので、大変重宝しています。

クラス分割の例

以下のクラスがあるとします。変数aに設定した値をメソッドbとメソッドcが使用しています。

class Whole {
  val a = 1
  def b = a + 1
  def c = a + 2
}

このクラスを以下の3つの部品(クラス+トレイト)に分解し、これらの部品を組み立ててクラスを定義します。

Whole
クラス本体。変数aを定義している。
Parta
部品となるトレイト。変数aを使用するメソッドbを定義している。
Partb
部品となるトレイト。変数aを使用するメソッドcを定義している。
class Whole extends Parta with Partb {
  val a = 1
}

trait Parta {
  self: Whole =>

  def b = a + 1
}

trait Partb {
  self: Whole =>

  def c = a + 2
}

ポイントとなるのがPartaのメソッドbとPartbのメソッドcがクラスWholeの変数aに依存していること。普通はこういう場合、クラスWholeとトレイトb, cはインヘリタンスの関係が必要になってきます。

しかし、この場合はトレイトPartaとPartb内のself-type annotationである「self: Whole =>」によって、トレイトがWholeにmixinされることが保証されるためクラスWholeの変数aを使用することができます。

そしてクラスWholeの定義で「class Whole extends Parta with Partb」とすることで、クラスWholeにトレイトPartaとPartbの機能を追加することができます。

この場合、トレイトPartaとトレイトPartbの間には依存関係がないことが明確に分かります。このような形で、機能間の依存関係をできるだけ疎にしていくのが、大きなプログラムを作るときのコツとなります。

実行

実行結果は以下のとおりです。普通に動作しました。

scala> val w = new Whole
w: Whole = Whole@2bf75442

scala> w.a
res0: Int = 1

scala> w.b
res1: Int = 2

scala> w.c
res2: Int = 3

諸元

  • Scala 2.9.2

2012年12月18日火曜日

SimpleModeler 0.4.0

モデルコンパイラSimpleModeler 0.4.0をリリースしました。

マインドマップ(XMind)、SmartDox DSL、CSVからクラス図とJavaプログラムを生成する機能が実用フェーズとなっています。

最近拡張した文法が動作します。

それそろ安定してきたのでバージョン0.4.0とします。

機能

Simplemodeler 0.4.0では以下のオプションを提供しています。

オプション機能状況
projectプロジェクト生成α
importモデル移入α
convertモデル変換試験的
html仕様書生成α
javaJava生成
androidAndroid生成α
diagramクラス図生成
buildプロジェクトビルド試験的
gaejGoogle App Engine Java生成試験的
gaeGoogle App Engine Python生成試験的
gaeoGoogle App Engine Oil生成削除予定
grailsGrails生成試験的
g3g3生成試験的
asakusaAsakusa生成試験的

基本的にはマインドマップ(XMind)、SmartDox DSLとCSVからクラス図とJavaプログラムを生成する処理が実用フェーズになっています。その他の機能はα版または試験的実装の状態です。

インストール

プログラムの配布は、Scala用のプログラム配布ツールconscriptを使っています。

conscriptをインストールした後、以下のようにしてSimpleModelerをインストールします。

$ cs asami/simplemodeler

以下のコマンドがインストールされます。

sm
SimpleModelerコマンド
$ sm -version
Copyright(c) 2008-2012 ASAMI, Tomoharu. All rights reserved.
SimpleModeler Version 0.4.0 (20121217)
graphviz

-diagramオプションでクラス図を生成する場合は、graphvizのインストールが必要です。graphvizのインストール方法は以下を参照してください。

各プラットフォーム向けパッケージ管理ツールでもインストールできるようです。

Mac
http://www.macports.org/
Windows
http://sourceware.org/cygwinports/

使い方

マニュアルはまだありません。以前のバージョン用のものがありますが、機能が色々変わってしまったので一から見直す予定です。

リファレンスマニュアルとユーザーガイドの元ネタをこのブログで随時書いていきます。

CSV

Mind(マインドマップ)、SmartDox DSL、CSVからクラス図を生成することができます。

以下のCSVファイルをsample.csvとして用意します。

#actor,base
顧客
個人顧客,顧客
法人顧客,顧客
#resource,attrs,powers
商品,商品名;定価(long),商品区分(第1類;第2類;第3類)
#event,parts
購入する,顧客;商品

SimpleModelerを以下のように実行します。

$ sm -diagram sample.csv

以下のクラス図の画像が生成されます。

SmartDox

以下のSmartDoxファイルをsample.orgとして用意します。

#+title: Table

SmartDox DSLを使って記述したモデルの
サンプル文書です。

文書でサンプルモデルの定義をします。
本来は文書中の仕様記述の文書は
定義するモデルに対するものになります。

しかし、この文書ではSmartDox DSLの記述例として
SmartDox DSL文法の説明を記述することにします。

* サンプル文書の目的

このサンプル文書は表を中心にしてクラス定義するサンプルです。

登場人物、道具、出来事の各エンティティの種別の下に
顧客、商品、購入といった具象エンティティを節として
定義します。

そして、それらの節の下に属性一覧または特性一覧として
エンティティの属性や関連を記述していきます。

* 登場人物

** 顧客

IDの指定はありませんが、以下のルールで推測しています。

- 陽にID指定がない場合、先頭の属性の属性名が「id」(大文字可)で終わる場合はIDとみなす。

#+caption: 属性一覧
| 名前   | 型     | カラム  | SQL型        |
|--------+--------+---------+--------------|
| 顧客ID | token  | ID      | CHAR(16)     |
| 名前   | token  | NAME    | VARCHAR(64)  |
| 住所   | string | ADDRESS | VARCHAR(256) |

* 道具

** 商品

IDは、ID欄で指定しています。

#+caption: 属性一覧
| 名前   | 型    | ID | カラム | SQL型       |
|--------+-------+----+--------+-------------|
| 商品ID | token | ○ | ID     | CHAR(16)    |
| 名前   | token |    | NAME   | VARCHAR(32) |
| 定価   | money |    | PRICE  | LONG        |

* 出来事

** 購入

IDは、特性欄で指定しています。

#+caption: 特性一覧
| 特性 | 名前   | 型    | 多重度 | 派生        | カラム      | SQL型    |
|------+--------+-------+--------+-------------+-------------+----------|
| ID   | 購入ID | token |        |             | ID          | CHAR(16) |
| 属性 | 日付   | date  |        |             | DATE        | DATE     |
| 関連 | 顧客   | 顧客  |      1 |             | CUSTOMER_ID | CHAR(16) |
| 属性 | 顧客名 | token |        | 顧客.名前   |             |          |
| 関連 | 商品   | 商品  |      1 |             | GOOD_ID     | CHAR(16) |
| 属性 | 数量   | int   |        |             | AMOUNT      | INT      |
| 属性 | 商品名 | token |        | 商品.名前   |             |          |
| 属性 | 単価   | money |        | 商品.定価   |             |          |
| 属性 | 総額   | money |        | 数量 * 単価 |             |          |

SimpleModelerを以下のように実行します。

$ sm -diagram sample.org

以下のクラス図の画像が生成されます。

2012年12月17日月曜日

MindmapModeling「チャネルの多様化で難題と化す予算の最適配分」

12月15日(土)に横浜モデリング勉強会(facebook group)を行いました。また、会場には(株)アットウェア様の会議室をお借りしました。参加された皆さん、アットウェア様、どうもありがとうございました。

この勉強会で、浅海が作成したモデルを紹介します。モデルはMindmapModelingの手法で作成しました。(勉強会で使用したチュートリアル)

ワークショップの流れ

モデリング勉強会はワークショップ形式で以下の作業を行います。

  • 雑誌記事から情報システムの企画書、提案書、RFPの元ネタとなるモデルを作成する。

その上で、「要求仕様確認、実装可能性確認、開発のベースとなるプログラムを自動生成するモデルを目指」します。詳細は「ワークショップの進め方 第2版」になります。

テーマ

モデリングの対象は、日経ビジネス誌の記事「チャネルの多様化で難題と化す予算の最適配分」です。

用語の収集と整理

まず用語の収集と整理します。

MindmapModelingに慣れてくると、用語がだいたいどこの枝に収まるのかわかるようになるので、用語を拾いながらラフなモデルを作っていきます。




今回の記事は、広告の効果を計測するメカニズム的なことが多く書かれていたので、このあたりを規則と登場人物を中心に分類しました。

クラス図

この段階でのマインドマップをSimpleModelerでクラス図化したものが以下になります。




いくつかモデルの島ができていますが、全体としてはまだばらばらです。

物語

次の作業は「物語」です。

モデルは中心軸がないと単なる「用語」の集りなのでまとまりがでてきません。何らかの目的を実現するための構造を抽出したいわけですが、この「目的」と「目的を実現するための構造」を掬いとるためのツールとして有効なのが「物語」です。オブジェクト・モデリングの概念ではビジネス・ユースケースということになります。

「物語」を中心軸と定め、「物語」のスコープで用語を取捨選択、組織化し、足りない用語を補っていきます。

その手順は:

  1. 物語の名前をつける。目的(goal)が明確になる名前がよい。
  2. 物語の主人公、相手役、脇役などの登場人物を定める。
  3. 物語で使用する道具を定める。
  4. 出来事または脚本の列として脚本を記述する。

となります。2の登場人物と3の道具は最初から完全なものはできないので暫定的なものを定め、4の脚本の作業を通して洗練させていきます。




「物語」として、「広告予算編成を最適化する」を設定し、この「物語」の作成を軸に、「出来事」の整理、「道具」の整理を進めました。

物語「広告予算編成を最適化する」は以下の出来事による脚本から構成されます。

  • マーケッティング施策を実行する
  • マーケッティング施策の結果を計測する
  • マーケッティング施策の結果を分析する
  • 予算配分する

さらに、各出来事を詳細化して、規則や道具を結びつけていきます。

今回の記事は内容がかなり絞りこまれていたので、モデリングの方向を問題領域の世界を記述するというよりシステム化を指向した詳細なモデルを記述するようにしてみました。

クラス図

この段階でのマインドマップをSimpleModelerでクラス図化したものが以下になります。




かなりクラス図らしくなってきました。ビジネス・ユースケース→イベント→アクター+リソースの流れが分かりやすく可視化されています。

ここまでの作業で時間切れとなりました。

ちょっと洗練

時間内に作ったマインドマップモデル+クラス図はSimpleModelerの機能不足もあって、出来事から規則に続く流れがクラス図上に表示できませんでした。

そこで、SimpleModelerに機能追加を行いながら、この点の記述の精度を上げたモデルが以下のものです。




クラス図

クラス図は以下になります。




SimpleModelingではアルゴリズムを記述するためのモデルとして、規則に加えてサービスを持っていますが、今回このサービスをマインドマップモデルでも使ってみました。

SimpleModelerで出来事から規則およびサービスへの関係は依存性(dependency)として記述しています。

ノート

オブジェクト・モデリングの鍵の一つは、静的構造を記述するドメイン・モデル(クラス図)と動的モデルをどのように関連付けていくのかという点です。

SimpleModelingでは、イベント(出来事)を起点にルールやサービスを呼び出すという点を軸の一つにしています。今回はよい機会なのでSimpleModelerにこのあたりの機能を追加してMindmapModelingからクラス図の生成の処理に盛りこんでみました。

具体的な記述方法、使い方は追々ブログで紹介していきたいと思います。

次回

次回はまだ確定していませんが1月19日(土)を予定しています。

詳細情報はfacebookグループ「横浜モデリング勉強会」を参照してください。

今回と同じく「ワークショップの進め方 第2版」の手順で、「雑誌記事から情報システムの企画書、提案書、RFPの元ネタとなるモデルを作成する」を行う予定です。

2012年12月14日金曜日

Scala Tips / Streamで採番(2)

Streamで採番では、Streamのfromメソッドを使って数値の連番による番号の採番を行いました。

場合によっては、採番する番号として「A00001」というような英数字と数字の組合せを取りたい場合があります。

このような場合は、カスタムでStreamを作ることになります。

といっても、これは簡単で以下のような再帰関数のイディオムを使います。

def nums(i: Int): Stream[String] = {
  "A%05d".format(i) #:: nums(i + 1)
}

使ってみる

それでは新しく作ったnums関数による採番用Streamを使ってみましょう。

採番した値の型はStringになるのNumberedNameを修正します。

case class NumberedName(name: String, number: String)

シーケンスに対して採番を行い値と採番した番号のTupleを返す関数は以下になります。Stream#fromの代わりにnums関数を使っています。

def f(names: Seq[String]): Seq[NumberedName] = {
  for ((n, c) <- names zip nums(1)) yield {
    NumberedName(n, c)
  }
}

実行

それでは実行してみましょう。

まずテスト用のListを定義します。

scala> val xs = List("a", "b", "c")
xs: List[java.lang.String] = List(a, b, c)

このListに対する関数fの実行結果は以下になります。

scala> f(xs)
res6: Seq[NumberedName] = List(NumberedName(a,A00001), NumberedName(b,A00002), NumberedName(c,A00003))

ごくごく簡単に使うことができますね。

ノート

再帰関数とStreamを使うことで、採番ロジックを関数として部品化することができました。この部品はSeqのzip関数でシーケンスと結びつけることができます。

こういった疎結合の関数部品を用意して、関数合成のテクニックで組合せて使うのが、関数型らしいプログラミングです。Streamの活用はそのような関数型プログラミングを行う上で重要なテクニックになっています。

諸元

  • Scala 2.9.2

2012年12月13日木曜日

Scala Tips / Streamで採番

名前に連番で番号を採番して関連付ける処理を考えます。採番する番号は、10から始めて10の間隔の昇順の数値とします。

名前と番号の対応を記述するケース・クラスNumberedNameを用意します。

case class NumberedName(name: String, number: Int)

引数で名前のシーケンスを受け取り、10から始めて10の間隔の昇順の数値を採番し、NumberedNameに名前と対にして格納する処理は、普通に考えると以下のようになるでしょう。

def f(names: Seq[String]): Seq[NumberedName] = {
  var c = 10
  for (n <- names) yield {
    val r = NumberedName(n, c)
    c += 10
    r
  }
}

手続き型プログラミング(Javaのメソッド実装を含む)では、よく出てくるロジックの形です。

実行

名前のリストを用意します。

scala> val xs = List("a", "b", "c")
xs: List[java.lang.String] = List(a, b, c)

実行結果は以下になります。

scala> f(xs)
res0: Seq[NumberedName] = List(NumberedName(a,10), NumberedName(b,20), NumberedName(c,30))

関数型のアプローチ

関数型プログラミングでは副作用をできるだけ避けるのが正しいプログラミング・スタイルになります。この目印となるのが「var」による変数宣言で、Scalaプログラミングではこの「var」が出てくると注意信号です。「「var」は明確な意図がない限り使わない」というのが有効なプログラミング戦略になります。

この方針を取るとすると、前述の関数fは明らかに方針に反しています。ただ、副作用なしでこのロジックを記述するのは案外大変です。このような簡単なロジックが捌けないようでは、副作用なし、「不変」戦略によるプログラミングを行うことできません。

このような連番採取の反復処理を関数型プログラミングで記述するのに使えるのが無限シーケンスであるStreamです。

Streamを使って記述すると以下のようになります。

def f(names: Seq[String]): Seq[NumberedName] = {
  for ((n, c) <- names zip Stream.from(10, 10)) yield {
    NumberedName(n, c)
  }
}

Streamのfromメソッドでは、第一引数に開始の番号、第二引数に連番の間隔を指定すると、数値の無限シーケンスであるStreamが返ってきます。これをSeqのzipメソッドを使って引数のシーケンスと接続すると引数のシーケンスとStreamに格納された数値を使った、名前と番号のTupleのシーケンスができあがります。Streamは無限シーケンスですが、引数のSeqが有限であれば、Tupleのシーケンスはそちらの長さになるので問題はありません。

このTupleをfor式で「(n, c)」という形の変数に受け取ってケースクラスNumberedNameに値を設定すればOKです。

実行

実行結果は以下になります。

scala> f(xs)
res1: Seq[NumberedName] = List(NumberedName(a,10), NumberedName(b,20), NumberedName(c,30))

この手の処理は「Stream+zipメソッド+for式の「(n, c)」」による記述が一つの形です。イディオムとして覚えておくとよいでしょう。

ノート

関数型プログラミングではStreamがかなり重要な部品になります。

以前「Streamで脱出」という話題でもStreamを取り上げました。

オブジェクト指向プログラミングでもjava.io.InputStreamといった形で無限を扱うためのテクニックとしてストリームの概念は出てきていますが、気軽に普段使いするようなものでもなく、IO入出力向けの用途限定テクニックという扱いでした。

一方、関数型プログラミングではごく気軽な普段使いのテクニックとして登場します。さらに、副作用を用いずプログラミングするには非常に有効なテクニックでもあり、そういう意味では関数型プログラミングの必須テクニックということができます。

諸元

  • Scala 2.9.2

2012年12月12日水曜日

ゆるふわScalaプログラミング・振り返り(6)

振り返り(5)では:

  • 可変分離⇔並行プログラミング
  • 不変⇔並列プログラミング

の組合せの中で「可変分離⇔並行プログラミング」について考えてみました。

不変⇔並列プログラミング

並列プログラミングの場合、リソースの更新は必須ではありませんから、可能な限り「不変」戦略を取りたいところです。

「不変」戦略による並列プログラミングの手法として以下のものが考えられます。

  • 並列コレクション
  • フューチャー・モナド

使い方が簡単なのは並列コレクションです。この場合、並列コレクションに対してmapコンビネータなどを用いて関数を合成してく形になります。しかし、この方法では、mapコンビネータを多段に結合した場合に各段毎に同期が発生してしまうために、並列度に制約が出てしまうという問題があります。

このあたりの問題はScalazの以下のスライドが詳しいです。

そこで、より本格的な並列プログラミングの基盤として期待したいのがフューチャー・モナドです。上のスライドにあるようにScalaz 6系ではPromiseモナドとして提供されています。Scala 2.10では、同等機能がFutureモナドとして基本ライブラリで提供されるので、本記事ではフューチャー・モナドと呼ぶことにします。

そこで、スライドの以下のページになります。



フューチャー・モナド(ScalazのPromiseモナド)を使って、並列プログラミングした場合の動作時間と並列動作の概念図を4ページを使って説明しています。ここで重要なのは、フューチャー・モナドの使い方はOptionモナドやListモナドといった他のモナドと全く同じでよいということです。普通にモナドを使ったMonadicプログラミングをしておけば、モナドとしてをフューチャー・モナドを使うことで、自動的に並列プログラミングになるわけです。

2012年12月11日火曜日

ゆるふわScalaプログラミング・振り返り(5)

振り返り(4)では、並行プログラミング/並列プログラミングの軸と、可変共用、可変分離、不変の軸を導入し、この2つの軸の相性の良い組合せと考えられる:

  • 可変分離⇔並行プログラミング
  • 不変⇔並列プログラミング

について簡単に考えてみました。

可変分離⇔並行プログラミング

アクター・プログラミングでは、「可変分離」戦略によって、アクター内に閉じたリソースに関しては競合を気にすることなく自由に更新することができます。

とはいえ、プログラムの信頼性、保守性を考えると可能な限り「不変」戦略を取るのがよいでしょう。また、個人的には一定のテクニックをマスターすれば、「不変」戦略の方が開発効率が高いと感じています。バグが出難いのが大きいですし、オブジェクトのライフサイクルに対する考慮をしなくてもよいのも大きなメリットです。

スライドにある以下の図は、オブジェクト指向と関数型を組み合わせたプログラミング・モデルです。オブジェクト指向の部分と関数型の部分を明確に分離し、オブジェクト指向の部分でリソースの更新を行います。そして、関数型の部分では「不変」戦略によって信頼性、保守性、開発効率のメリットを取りにいきます。



簡単な流れは以下のようになります。

  1. アクターがリクエストを受け取る。
  2. リクエストからリソースを更新するための更新指示書を計算。
  3. 更新指示書に従ってリソースを更新。
  4. 必要に応じてレスポンスを返す。

ここでは、ざっくり「更新指示書」と呼んでいますが、単なる値の場合もありますし、SQLのようなバッチ的更新プログラム、インタラクティブなインタープリタ・プログラムといったパターンがあります。

いずれにしても、アクター処理の大半を関数型的な「不変」戦略によって記述することで、信頼性、保守性、開発効率のメリットを享受することができます。

この方式の短所として、メモリ使用量が多くなる、実行速度が若干遅くなる、ということはありますが、今時のハードウェアではほとんど問題にならないと思われます。問題になるケースでも、問題箇所のみオブジェクト指向を使う、Javaを使う、という形で旧来の技術で最適化を行えばよいでしょう。

2012年12月10日月曜日

ゆるふわScalaプログラミング・振り返り(4)

振り返り(3)では並行(concurrent)プログラミングと並列(parallel)プログラミングの用語を導入しました。

続けて、並列/分散処理を記述するためのプログラミング技法を議論するためにもう一つの軸を導入します。

もう一つの軸

もう一つの軸とは以下の3つです。

  • 可変共用(shared mutability)
  • 可変分離(isolated mutability)
  • 不変(immutability)

このあたりはスライドの以下のページにまとめています。

プログラミングの方針では、不変が最上で、不変が難しい場合は可変分離、避けたいのが可変共用となります。

いわゆるマルチスレッド・プログラミングというと可変共用のカテゴリになるわけですが、これが「避けたい」プログラミング・モデルというのが重要です。

可変分離や不変のプログラミング・モデルは(現時点の)Javaでは可能ではありますが、あまり相性がよくありません。逆に、関数型言語と相性のよいプログラミング・モデルです。この点から、関数型言語が並行/並列プログラミングに向いているのではないかと予測できるわけです。

可変分離は、Scalaもサポートしているアクターによって実現できます。

不変は、言うまでもなく純粋関数型のプログラミング・モデルの前提条件ですし、関数型プログラミングの核となる概念です。

合わせてどうなるか

さて、並行プログラミングと並列プログラミングの軸、可変共用、可変分離、不変の軸の2つの軸を導入しました。

この2つの軸は直交するので、それぞれの組合せが可能ですが、相性の良い組み合わせの候補と考えられるのが以下の2つです。

  • 可変分離⇔並行プログラミング
  • 不変⇔並列プログラミング

まず、並行プログラミングは同時に発生する事象から駆動される処理が同一リソースを更新したいということが問題の発端なので、「不変」(のみ)では対処することができません。アクターを用いた「可変分離」でこの問題に対処するのが一つの解になります。

一方、並列プログラミングは1つの処理を分割して複数のCPU /コアで同時実行することで処理性能を向上させることが基本になるので、「不変」による実装が可能です。より好ましいのは「不変」>「可変分離」ですから、不変と可変分離の両方の選択肢が取れるのであれば不変を選ぶのが得策ということになります。

2012年12月7日金曜日

SmartDox 0.3.0

SmartDox 0.3.0をリリースしました。

本バージョンでは細かい機能拡張を行なっています。内容についてはブログで紹介していく予定です。

機能

SmartDox 0.3.0では以下のオプションを提供しています。

オプション機能
-html5HTML5生成(試験的)
-html4HTML4生成
-html3HTML3生成
-plainプレインテキスト生成
-pdfPDF生成
-latexLaTeX生成
-bloggerBlogger用のHTML生成

インストール

プログラムの配布は、Scala用のプログラム配布ツールconscriptを使っています。

conscriptをインストールした後、以下のようにしてSmartDoxをインストールします。

$ cs asami/dox

以下の2つのコマンドがインストールされます。

dox
SmartDoxコマンド
sdoc
SmartDocコマンド(互換用)
依存プロダクト

SmartDoxでは、以下のプロダクトに依存しています。

プロダクト使用する機能
LaTeXPDF生成
Graphviz画像生成
Ditaa画像生成

プロダクトに依存する機能を使わない場合は必要ありません。

LaTeX

platexコマンドとdvipdfmxコマンドが実行可能になっていれば基本的にはOKです。

Mac OS上でmacportsを使ってインストールしたLaTeXで動作確認しています。他の環境の場合、スタイルファイルなどがない可能性があります。

Graphviz

dotコマンドが実行可能になっていればOKです。

Mac OS上でmacportsを使ってインストールしたGraphvizで動作確認しています。

Ditaa

ditaaコマンドが実行可能になっているか、optlocalsharejavaditaa09.jarのJarファイルが存在していればOKです。

Mac OS上でmacportsを使ってDitaaをインストールすると、optlocalsharejavaditaa09.jarに配置されます。このditaa09.jarを決め打ちで使用しています。(いずれパラメタで指定可能にする予定です。)

それ以外の環境では、シェルスクリプトなどでditaaコマンド(インストールされているJarファイルを呼び出す)を作成してください。

使い方

まだマニュアルがないので、文書フォーマットは org-modeを参考にしてください。あまり難しい文法を使わなければ大体大丈夫だと思います。

org-mode形式で作成した文書から以下のようにしてHTMLやPDF、プレインテキストに変換してください。

$ dox -html4 mydoc.dox
$ dox -pdf mydoc.dox
$ dox -plain mydoc.dox

2012年12月6日木曜日

SimpleModeler 0.4.0-RC5

モデルコンパイラSimpleModeler 0.4.0-RC5をリリースしました。

マインドマップ(XMind)、SmartDox DSL、CSVからクラス図とJavaプログラムを生成する機能が実用フェーズとなっています。

次ぐらいに0.4.0正式版になる予定です。

機能

Simplemodeler 0.4.0-RC5では以下のオプションを提供しています。

オプション機能状況
projectプロジェクト生成α
importモデル移入α
convertモデル変換試験的
html仕様書生成α
javaJava生成
androidAndroid生成α
diagramクラス図生成
buildプロジェクトビルド試験的
gaejGoogle App Engine Java生成試験的
gaeGoogle App Engine Python生成試験的
gaeoGoogle App Engine Oil生成削除予定
grailsGrails生成試験的
g3g3生成試験的
asakusaAsakusa生成試験的

基本的にはマインドマップ(XMind)、SmartDox DSLとCSVからクラス図とJavaプログラムを生成する処理が実用フェーズになっています。その他の機能はα版または試験的実装の状態です。

インストール

プログラムの配布は、Scala用のプログラム配布ツールconscriptを使っています。

conscriptをインストールした後、以下のようにしてSimpleModelerをインストールします。

$ cs asami/simplemodeler

以下のコマンドがインストールされます。

sm
SimpleModelerコマンド
$ sm -version
Copyright(c) 2008-2012 ASAMI, Tomoharu. All rights reserved.
SimpleModeler Version 0.4.0-RC5 (20121206)
graphviz

-diagramオプションでクラス図を生成する場合は、graphvizのインストールが必要です。graphvizのインストール方法は以下を参照してください。

各プラットフォーム向けパッケージ管理ツールでもインストールできるようです。

Mac
http://www.macports.org/
Windows
http://sourceware.org/cygwinports/

使い方

マニュアルはまだありません。以前のバージョン用のものがありますが、機能が色々変わってしまったので一から見直す予定です。

リファレンスマニュアルとユーザーガイドの元ネタをこのブログで随時書いていきます。

CSV

Mind(マインドマップ)、SmartDox DSL、CSVからクラス図を生成することができます。

以下のCSVファイルをsample.csvとして用意します。

#actor,base
顧客
個人顧客,顧客
法人顧客,顧客
#resource,attrs,powers
商品,商品名;定価(long),商品区分(第1類;第2類;第3類)
#event,parts
購入する,顧客;商品

SimpleModelerを以下のように実行します。

$ sm -diagram sample.csv

以下のクラス図の画像が生成されます。

SmartDox

以下のSmartDoxファイルをsample.orgとして用意します。

#+title: Table

SmartDox DSLを使って記述したモデルの
サンプル文書です。

文書でサンプルモデルの定義をします。
本来は文書中の仕様記述の文書は
定義するモデルに対するものになります。

しかし、この文書ではSmartDox DSLの記述例として
SmartDox DSL文法の説明を記述することにします。

* サンプル文書の目的

このサンプル文書は表を中心にしてクラス定義するサンプルです。

登場人物、道具、出来事の各エンティティの種別の下に
顧客、商品、購入といった具象エンティティを節として
定義します。

そして、それらの節の下に属性一覧または特性一覧として
エンティティの属性や関連を記述していきます。

* 登場人物

** 顧客

IDの指定はありませんが、以下のルールで推測しています。

- 陽にID指定がない場合、先頭の属性の属性名が「id」(大文字可)で終わる場合はIDとみなす。

#+caption: 属性一覧
| 名前   | 型     | カラム  | SQL型        |
|--------+--------+---------+--------------|
| 顧客ID | token  | ID      | CHAR(16)     |
| 名前   | token  | NAME    | VARCHAR(64)  |
| 住所   | string | ADDRESS | VARCHAR(256) |

* 道具

** 商品

IDは、ID欄で指定しています。

#+caption: 属性一覧
| 名前   | 型    | ID | カラム | SQL型       |
|--------+-------+----+--------+-------------|
| 商品ID | token | ○ | ID     | CHAR(16)    |
| 名前   | token |    | NAME   | VARCHAR(32) |
| 定価   | money |    | PRICE  | LONG        |

* 出来事

** 購入

IDは、特性欄で指定しています。

#+caption: 特性一覧
| 特性 | 名前   | 型    | 多重度 | 派生        | カラム      | SQL型    |
|------+--------+-------+--------+-------------+-------------+----------|
| ID   | 購入ID | token |        |             | ID          | CHAR(16) |
| 属性 | 日付   | date  |        |             | DATE        | DATE     |
| 関連 | 顧客   | 顧客  |      1 |             | CUSTOMER_ID | CHAR(16) |
| 属性 | 顧客名 | token |        | 顧客.名前   |             |          |
| 関連 | 商品   | 商品  |      1 |             | GOOD_ID     | CHAR(16) |
| 属性 | 数量   | int   |        |             | AMOUNT      | INT      |
| 属性 | 商品名 | token |        | 商品.名前   |             |          |
| 属性 | 単価   | money |        | 商品.定価   |             |          |
| 属性 | 総額   | money |        | 数量 * 単価 |             |          |

SimpleModelerを以下のように実行します。

$ sm -diagram sample.org

以下のクラス図の画像が生成されます。

2012年12月5日水曜日

ゆるふわScalaプログラミング・振り返り(3)

メニーコアのプログラミングを考える上で重要なのは:

  • 並行(concurrent)プログラミング
  • 並列(parallel)プログラミング

の違いです。

この点をセッションでも触れようと思っていたのですが、結局スライドを作りそこねてしまいました。

この2つの用語はいろいろな意味で使われていて、場合によっては同じ意味で用いられるケースもあると思いますが、セッション内では以下の用法を想定しています。

並行プログラミング
同時並行に動作する複数の処理を複数のタスクによって同時に実行する
並列プログラミング
ひとつの処理を並列に動作する複数タスクの協調動作で実行する

たとえば、外部イベントで駆動される処理の場合、並行プログラミングは同時期に発生する複数の外部イベントに対応する処理が同時に動作するのに対して、並列プログラミングは1つの外部イベントに対応する処理が複数のCPU上で並列動作して高速に処理を完了する、というような違いになるかと思います。

ここでいう並行プログラミングはメニーコアでなくても普通に発生する問題で、今までもスレッド技術である程度うまく扱えていた問題です。Javaでいうとサーブレットの技術が相当します。

一方、メニーコア時代に入って問題となるのが一つの処理をメニーコア上で高速動作できないのか、という点です。たとえば、100の処理量のある処理を100コアを使って1の時間で実行できないのか、ということです。この問題は「同時並行に動作する複数の処理」を捌くためのプログラミング・モデルである「並行プログラミング」ではうまくさばけない、あるいは効果が限定的なのではないのか、という懸念につながります。

この観点から並列プログラミングに期待が集まるわけです。ボクも並列プログラミングに詳しいわけではないですが、関数型言語のモナドを使うとかなりうまく扱えそう、ということがScalaプログラミングを通じて分かってきました。

そういうわけで、今回のセッションのテーマの一つはモナドを使った並列プログラミングとなります。OFPによる新三種の神器の「モナド」の具体的な適用分野ですね。



2012年12月4日火曜日

ゆるふわScalaプログラミング・振り返り(2)

11月30日に開催されたBPStudy#63で「Scalaプログラミング・マニアックス」の振り返り第2弾です。

後から気づいた書き忘れの項目として 「Scalaを採用する理由」があります。具体的には以下の5つを考えていました。

  • 静的型付け
  • JavaVM
  • 関数型によるアルゴリズム記述力
  • メニーコア(並列プログラミング)
  • DSL

静的型付けとJavaVMは、個人的にプログラミング言語を選ぶ時の必須項目と思っているものです。

静的型付けはよいとして、JavaVM上で動作するプログラミング言語であるというのは地味に大事です。Javaで開発された膨大なクラスライブラリが再利用できること、異なったプラットフォーム間での可搬性、マルチスレッド動作の安定性、といった要素を考えると、余程のことがない限りJavaVM以外は選びにくいというのが個人的な考えです。

関数型言語はリスト処理の機能が充実しているのと、高階関数といった機能を使っていろいろな技が使えるので、手続き型言語よりもアルゴリズムの記述力は高くなります。

Javaの場合、処理の記述は「手続き型」ですから「関数型」が可能なScalaの方が記述力が高くなるわけです。

とはいえ、以上の点だけであれば、今までどおりJavaを使いつつ、Project Lambdaのような関数型系の記述方式の拡張を待つという選択もあります。拡張を待つ間、部分的にGroovyやxtendといった言語を併用して凌ぐという手もあるでしょう。

ただ、Javaに「関数型」的な機能を拡張する方式で克服が難しいのではないかというのが、後の2つ「メニーコア」と「DSL」です。

それはなぜか…ということでセッションのディテイルに入るような流れを考えていたわけです。

2012年12月3日月曜日

ゆるふわScalaプログラミング・振り返り

11月30日に開催されたBPStudy#63で「Scalaプログラミング・マニアックス」と題してお話させていただきました。



ゆるふわScalaプログラミング」 といった方向性を考えていたのですが、11月中にあまり時間を取ることができず、今まで作ったScalaに関するスライドの再構成という形にしました。ただ、Scalaプログラムの細かいところに踏み込まず、Scalaプログラミングの要素技術、特にJava系のオブジェクト指向プログラミングでは意識することのない技術に焦点を当てることを目指しています。

Javaなどオブジェクト指向プログラミングをひと通り知っているプログラマが「全く聞いたことのない技術ばかり!」と驚いてもらえたら成功です。

とはいえ、実際の所Scalaはオブジェクト側に倒してある言語なので、オブジェクト指向を主、関数型を従の塩梅で使うのがよく、あまり関数型方面ばかりを強調するのもミスリードの可能性もあります。

そうではあるのですが、今のタイミングでJavaからScalaへ乗り換えるべきである積極的な理由としては、Javaにはない新しい概念、新しいプログラミング・モデルを使うことで、新しい現実であるクラウド・アプリケーションに対応できる可能性が高まることです。このための新技術、特にオブジェクト指向プログラマが見たこともない技術に気づいてもらうのがセッションの狙いというわけです。

この観点から、クラウド・アプリケーションに必要な要素とScalaの言語機能の関係をまとめてみようと思っていたのですが、セッションの段階ではまだまとめきれませんでした。そこで、ブログの方でセッションを振り返りながらつらつら考えていきたいと思います。