@ledsun blog

無味の味は佳境に入らざればすなわち知れず

メンバーの教育を(有期の)プロジェクトリーダーの責務に入れてはいけない

t.co

を読んで考えた話です。

30歳近くになっても無能、ということは、そいつはほとんどの場合、一生無能だ

と刺激的な言葉が使われています。 状況を限定すれば、一理あると感じる点があります。 表題の「メンバーの教育を(有期の)プロジェクトリーダーの責務に入れてはいけない」です。

結論

プロジェクトリーダーの最大の責務はプロジェクトを成功です。 メンバーの教育は、プロジェクト成功の阻害要因になっているときだけやります。

無能なやつには、意見を聞いても、ろくな話は出てこない。時間がもったいない

の例では、「意見を聞かない」という簡単な回避策があります。 回避策があるときは、教育の手間を掛けてはいけません。

そうは言っても、一緒に仕事する仲間はできるだけ賢くあって欲しい、自分の足を引っ張らないで欲しいと思うのが人間です。

教育は誰がやればいいのか?

ティーチングとコーチン

メンバーの教育は上司(ライン上の上司)がやります。 ここで、教育を大きく2つにわけます。 ティーチングとコーチングです。

プロジェクトに直結したティーチングはプロジェクトリーダーの責務です。 例えば「gitの操作に不慣れなメンバーに、gitの使い方を教える」(または教える人を割り当てる)のはプロジェクトリーダーの仕事です。

コーチングでは、本人に思考を促し、失敗に対して工夫し、何度か失敗を繰り返すのを見守り、成功まで導きます。 要はPDCAサイクルを回すサポートをします。 これは上司の責務です。

コーチの責務

コーチングの目的は、未知の問題にぶつかった時に、自ら工夫してチャレンジしていく能力と自信を身に着けさせることです。 本人が試行錯誤して成功にたどり着く必要があります。 時間が必要です。 またコーチ役は「教えちゃえば簡単なのに」を我慢する必要があります。

一人の人間が同時に、プロジェクトの成功を急ぐのと、メンバーの成長を待つのをやるのは無理です。 人を分けましょう。 プロジェクトリーダーが安心して、教育を手放せるように、他に教育を責務とする人を用意しましょう*1

*1:コーチングを上司の責務にして、プロジェクトリーダーの責務から引き剥がすのが、組織を作る人(CTO、VP of Engineering、人事部門)の責務です。

革ベルトを作る計画を立案せよ

動機

レザーパンチを買ってベルトに穴を開けていたら、革ベルトが作りたくなりました。

予算と手順を立ててみます。

方針

なるべく工数を下げ、可能な限り早く完成にたどり着いて、お手軽に達成感を得ます。

そのため、

  • 手作業での革のカット
  • 手作業での革の縫い合わせ
  • スタッズ等の装飾
  • バックルなどパーツへのこだわり

は放棄します。加工済みの部品で値段が安いものを組み合わせて作成します。

予算

種類 名前 URL サイズ 単価 数量 金額 送料
ベルト 昭南多脂ベンズ ベルト ナチュラ http://l-phoenix.shop-pro.jp/?pid=62568295 4cm 2600 1 2600
バックル固定部分漉き加工 http://l-phoenix.shop-pro.jp/?pid=46123850 200 1 200
バックル バックル 415-35 真鍮 http://l-phoenix.shop-pro.jp/?pid=88940283 680 1 680
ループ ループ35-1B http://l-phoenix.shop-pro.jp/?pid=97645764 340 1 340
カタビス http://l-phoenix.shop-pro.jp/?pid=62568295 9x4x5 70 4 280
トコノール https://item.rakuten.co.jp/lc-palette/s72002/ 324 1 324 220

総額 4,644円

作業手順

  1. バックルを通す用の穴位置を決める
  2. バックルを通す用の穴を開ける
  3. バックルを取り付ける穴の位置を決める
  4. バックルを取り付ける穴を開ける
  5. バックルを取り付ける
  6. 穴の位置を決める
  7. 穴を開ける
  8. 理想の長さを測る
  9. ベルトを切る
  10. トコノールで断面を処理する
  11. ミンクオイルで表面を磨く

11手順、1手順15分として3時間程度で完成できそうです。

参考URL

ミントチョコレートの起源

アイスに限らずにミントとチョコレートの組み合わせの起源を探ります。

1962年 アフターエイト

アフターエイトは初めて知った大人のミントチョコレート : イギリスの食、イギリスの料理&菓子

アフターエイトは、1962年にイギリスで生まれたチョコレート菓子。

単純にチョコレートとミントを組み合わせるだけなので、一番古い組み合わせなのかと予想していました。 1962年で予想外に新しいです。

1945年 チョコミントアイス

チョコミントの発祥はどこ?ミントがトルコならチョコミントは?

1945年にアメリカのカルフォルニアでバスキンさんとロビンスさんによって バスキン・ロビンスという世界最大のアイスクリームのチェーン店が誕生しました。

これがサーティーワンの発祥です。

現地では、この頃からチョコミントフレーバーは人気のアイスでした。

チョコミントと言えばアイスぐらいの王道だけあって、 1945年には既にチョコミントアイスがあるようです。

1900年 グラスホッパー

カクテルガイド|カクテルを知る

現代式のカクテル=「器具を使って作る氷で冷やしたミクスト・ドリンク」となったのは1879年の製氷機の発明以来です。

アイスクリームが大衆化するよりは、カクテルが普及した方が古いので、チョコミントアイスより古そうです。

グラスホッパー・カクテル(Grasshopper Cocktail)のレシピ・作り方| しっぽり...

サンフランシスコにある有名な老舗ホテルのパレス・ホテル(The Palace Hotel)のバーテンダーだったハリー・オブライエン(Harry O'Brien)が1900年頃に考案した

1900年。予想通り、今の所発見できた最古の情報です。

だとすると、ミントチョコレートはもともとカクテルの味として誕生した、大人の味だったのでしょうか? お菓子のチョコレートとミントを組み合わせる方が作るのが簡単そうです。

チョコレートの歴史 - Wikipedia

1875年にミルクチョコレートの販売を始めた。またミルクチョコレート製造には、牛乳から水分を抜く必要があったが、ダニエルは隣りに住んでいたベビーフード生産業者のアンリ・ネスレネスレ創業者)と協力して研究を行った

甘いチョコレートが普及したのが19世紀末なようです。 ミントとチョコレートの組み合わせは、グラスホッパーが起源で、アメリカ生まれの、可能性もありそうです。

Ruby on Railsの開発環境でマルチスレッドでクラス定義を探索すると刺さるが再現できなかった話

現象

Ruby on Railsの、ActiveJob内で起動したスレッドで非同期にDBに書き込もうとすると、ActiveRercordのクラス探索で無限に待って固まります。

わかっている条件

  • ActiveJobで起きる
  • ActiveJobのQueueAdapterにはAsync adapterを使っている
  • DBへの接続以前のActiveRercordのクラス探索で固まる

例えば

Thread.start do
  executor = Lodqa::OneByOneExecutor.new dataset, query, debug: false

  # Bind events to colletc answers
  collected_answers = []
  executor.on(:answer) do |_, val|
    Answer
  end
end

このような処理*1をApplicationJobクラスで実行すると、Answerの探索で固まり、無限に待ちます。

Thread.startでスレッドを開始するほか、executor内でEventMachine#deferを使って更に複数スレッドで処理を行っています。

再現の努力

そこで、再現する最小のソースコードを書いてみます。

class CircularDependencyDetectedJob < ApplicationJob
  queue_as :default

  rescue_from(StandardError) do |exception|
    logger.fatal exception
  end

  def perform(*_args)
    Thread.new { EventMachine.run }

    1.times do
      Thread.new do
        2.times do
          EM.defer do
            Answer
            p 'Goal!!!'
          end
        end
      end
    end
  end
end

エラーが発生

これを実行すると、固まりはしませんが、次のようなエラーが発生します。

/usr/src/myapp # rails c
Loading development environment (Rails 5.2.0)
irb(main):001:0> CircularDependencyDetectedJob.perform_later
Enqueued CircularDependencyDetectedJob (Job ID: 9676f7ff-5c23-411c-84e3-33aca66c5eae) to Async(default)
=> #<CircularDependencyDetectedJob:0x0000562444702420 @arguments=[], @job_id="9676f7ff-5c23-411c-84e3-33aca66c5eae", @queue_name="default", @priority=nil, @executions=0, @provider_job_id="39bf3d7d-051f-4915-a752-17686211c958">
irb(main):002:0> Performing CircularDependencyDetectedJob (Job ID: 9676f7ff-5c23-411c-84e3-33aca66c5eae) from Async(default)
Performed CircularDependencyDetectedJob (Job ID: 9676f7ff-5c23-411c-84e3-33aca66c5eae) from Async(default) in 62.71ms
#<Thread:0x000056244503a970@/usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1067 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
    7: from /usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1077:in `block in spawn_threadpool'
    6: from /usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:16:in `block (4 levels) in perform'
    5: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    4: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
    3: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:534:in `load_missing_constant'
    2: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    1: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
/usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:500:in `load_missing_constant': Circular dependency detected while autoloading constant Answer (RuntimeError)
#<Thread:0x00005624446907f8@/usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:9 aborting> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
    7: from /usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1077:in `block in spawn_threadpool'
    6: from /usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:16:in `block (4 levels) in perform'
    5: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    4: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
    3: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:534:in `load_missing_constant'
    2: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    1: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
/usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:500:in `load_missing_constant': Circular dependency detected while autoloading constant Answer (RuntimeError)
Traceback (most recent call last):
    7: from /usr/local/bundle/gems/eventmachine-1.2.7/lib/eventmachine.rb:1077:in `block in spawn_threadpool'
    6: from /usr/src/myapp/app/jobs/circular_dependency_detected_job.rb:16:in `block (4 levels) in perform'
    5: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    4: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
    3: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:534:in `load_missing_constant'
    2: from /usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:193:in `const_missing'
    1: from /usr/local/bundle/gems/bootsnap-1.3.0/lib/bootsnap/load_path_cache/core_ext/active_support.rb:43:in `load_missing_constant'
/usr/local/bundle/gems/activesupport-5.2.0/lib/active_support/dependencies.rb:500:in `load_missing_constant': Circular dependency detected while autoloading constant Answer (RuntimeError)

エラーの原因

該当のソースコードを読むと

if loading.include?(expanded)
  raise "Circular dependency detected while autoloading constant #{qualified_name}"
else

loadingに自身が含まれていたら、循環依存で読み込みをやめる処理です。 マルチスレッドで読み込むと、循環依存していなくても、このチェックをするタイミングで他スレッドで自クラスを読み込んでいることがあるので、発生しているようです*2

対応

この処理は定数の自動読み込みと再読み込み | Rails ガイドのための処理です。 自動読み込みを停止してみましょう。

Rails アプリケーションを設定する - Railsガイド

config.eager_loadをtrueにすると、config.eager_load_namespacesに登録された事前一括読み込み(eager loading)用の名前空間をすべて読み込みます。ここにはアプリケーション、エンジン、Railsフレームワークを含むあらゆる登録済み名前空間が含まれます。

config/environments/development.rb

config.eager_load = true

を指定すると、このエラーは起きなくなります。

やはり、「定数の自動読み込み」と関係がありそうです。

残る謎

  • マルチスレッドであれば起きるはずなのに、Rails consoleで次のソースコードを実行しても発生しない
1000.times { Thread.new { Answer } }
  • 元の現象では固まるのに、再現コードではエラーが発生する

暫定の結論

元のソースコードの、クラス探索で固まる現象もeager_loadを有効にすると発生しなくなります。 これ以上調査時間が取れないので、この記事で供養して完了とします。

*1:発生したソースコードと完全には一致していません。

*2:GILがあるから並列に動かないし衝突しないはず・・・と思ったら、ファイルからクラスを読み込む処理なので並列に動くようです。

<Repository (class)> yielded |nil| to block with

RSpecMockで見慣れないエラーが起こせました。 再現コードをメモります。

class Repository; end

RSpec.describe do
  it do
    expect(Repository).to receive(:doc).and_yield(nil)
    Repository.doc {}
  end
end

実行すると

~ rspec spec.rb
F

Failures:

  1) should receive doc(*(any args)) 1 time
     Failure/Error: Repository.doc {}
       #<Repository (class)> yielded |nil| to block with
     # ./spec.rb:6:in `block (2 levels) in <top (required)>'

Finished in 0.01831 seconds (files took 0.20957 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec.rb:4 # should receive doc(*(any args)) 1 time

ちょっとおもしろいのは

Repository.doc {|a|}

のように、ブロック引数を指定してRepository.docを呼び出すと、このエラーは出なくなります。 RSpecMockは、ブロック引数の個数を見て動きを変えているようです。

Node学園 31時限目でLTしました #tng31

Node.jsの並列プログラミングについて話してきました。 大量のhtmlファイルをパースする処理を、 child_process.forkを使って複数プロセスへ分散した話をしました。 また、worker_threadsを試した話をしました。 めっちゃフィードバックがもらえて楽しかったです。

speakerdeck.com

フィードバック

「並列数を増やした時のボトルネックがわかりません」と発表したところ、 楽しんでいただけだようで、たくさんのアイデアをいただきました。

週末の間、ボトルネックとして成り立つ確率と、検証方法を考えてみました。 一通り検証する予定です*1

ボトルネックファイル仮説

いずれか(複数の可能性もある)のhtmlファイルが巨大であるなどの理由で時間が掛かっていて、ボトルネックになっているのでは?

1ファイル辺りの処理時間は数十ms程度なので、多少ばらついても秒単位のボトルネックにならないようにも思えます。 しかし、現在はラウンドロビンでタスクをサブプロセスに割り振っています。 何回起動しても、同じプロセスに同じタスクが割り振られます。 1ファイルで秒単位のばらつきが出ないとしても、タスク群としては出る可能性があります。

プロセス単位で、終了までの時間を出力すれば検証できそうです。

メモリ仮説

プロセス数が増えて消費メモリが大きくなるがボトルネックになっているのでは?

現代では36プロセスが多すぎるとは思えません。 また、EC2はデフォルトでは、swapファイルの割当がなく、メモリが足りなくなると即エラーになりOOM killerが飛んできます。 今の所、OOM killerを観測していません。ただ、すべてのサブプロセスの状態を監視しているわけではないので、知らずに死んでいる可能性はあります。 この場合も、出力データが壊れても、処理時間には影響がなさそうです。

実行時の消費メモリを観測すれば検証できそうです。

物理コア仮説

AWS EC2で試したので、Virtual CPUが36コアだとしても物理CPUが10コアしか無いため、実際は36並列まで伸びないのでは?

クラウド環境にありそうな話です。 ただし、グラフがなめらかな点が気になります。 コア数が足りなくなればそこから水平になると予想できますが、公開したグラフで25コアまでは微減です。 また、機械学習勢の人たちは、僕より早くマルチコア環境を試していそうなので、そういう事実があれば炎上していそうなものです。

物理 CPU、CPU コア、および論理 CPU の数を確認する - Red Hat Customer Portal のコマンドで物理CPU数・コア数と論理コア数を確認すれば何かわかるかもしれません。

共有ログ仮説

書いているログ・ファイルが一つであれば、書き込みがボトルネックになっているのでは?

実際ログは一つのファイルなのでありそうです。 発表中に説明していませんが、重複ログを書きこまない機能があります。 ログを書くかどうかはログの状態によってばらつきます。 この機能が影響しているにしては、グラフがきれいすぎる気もします。

ログ書き込みを止めて計測すれば検証できそうです。

その他

GNU Parallel使うといいのでは?

元々Go lang*2Ruby*3辺りに移植して、多言語の実行環境と性能比較したいとは思っていました。 1から違う言語で書き直すのは大変なので、Node.jsのまま入出力だけ修正して、GNU Parallelを使うというのは、 より短い時間で試せるいいアイデアです。

その他の感想

参考に次の記事を挙げました。 何人か「この記事を参考にしてスクレイピングに挑戦しています」と言ってくれる人がいました。 書いてよかったです。

qiita.com

*1:確率が低くても否定材料を集めておくことは、デバッグ最速理論的に大事なのです

*2:この手の並列はgo langのGoroutinesが強いイメージなので、実際に試してみたいです。

*3:rubexを使うとGILを超えて並列化できるという触れ込みなので実際に試してみたいです。

#rubykaigi 2018でLTしました

会社がお金を出してくれるのでRubyKaigiへの参加が決まっていました。 どうせ参加するなら何かしら発表したいです。 LTに応募したら通りました。

資料です。

speakerdeck.com

結論に被さるぐらいの勢いで銅鑼が鳴りました。

英語で発表

英語での発表にチャレンジしました。 「英語の資料を日本語で発表」はやったことがあります。 「資料も発表も英語」は初めてです。

何人かに、なぜ英語で発表したのか聞かれました。 これにはいくつか理由があります。

某所で「3年以内に海外カンファレンスで発表する」を目標にしました。 海外を国際に置き換えるだけで、ぐっとハードルがさがって実現可能なことに気づきました。

募集要項が全部英語でした。 「参加者は日本人が多いハズだが、国際カンファレンスだったはず・・・」日本語と英語の発表のどっちが良いのか空気が読めませんでした。 英語なら失敗しても「ナイスチャレンジ」と言われるだけなので、英語にしました。

github.com

を見ると、内容は大体伝わっていたようなので一安心です。

二夏続けて、総額90万円、英会話学校に課金しているので、成果が出て嬉しいです。

緊張

5分とはいえ、1000人いる会場で発表するのは緊張します。 そこで敬愛する右角ヒサシを見習いThee Michelle Gun Elephantの曲で己を鼓舞しました。 f:id:ledsun:20180613181640p:plain

さらに気持ちを盛り上げるためにTシャツはLAST HEAVEN TOUR 2003のライブTシャツです。

f:id:ledsun:20180603101351j:plain

15年前の遺物ですが、役に立つのです。

参考資料

R.I.P Abe...

Roy Fieldingの論文

qiita.com

RESTについてRoy Fieldingの論文原著を必ず読んでください

本文は読んでいません。周辺情報を調べてみました。

Architectural Styles and the Design of Network-based Software Architectures が論文全体です。 ネットワークを使ったアプリケーションのアーキテクチャを扱った長い論文の5章 「Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)」にRESTの話があります。

タイトル「Architectural Styles and the Design of Network-based Software Architectures」でググると6780引用と出てきて驚きます。

Referencesを見ると

  1. Alexander, S. Ishikawa, M. Silverstein, M. Jacobson, I. Fiksdahl-King, and S. Angel. A Pattern Language. Oxford University > Press, New York, 1977.

Alexanderを参照していて、パターンランゲージにも関連があるようです。

英語の論文は読書会でもやらないと読める気がしません・・・。

面白かったけど役に立てられる気がしないセッション #rubykaigi

Hijacking Ruby Syntax in Ruby

クレイジーでした。Binding#local_variable_set や TracePoint を使ってRubyの言語仕様を拡張しようという趣旨です。 CRuby本体を拡張せずに、新文法のProof of conceptが書ける意味はわかります。 とはいえ、いくらなんでもやりすぎなのではという思いで一杯です。

tagomoris.hatenablog.com

TTY - Ruby alchemist’s secret potion

github.com

の紹介でした。

クレイジーでした。CLIを作るのに便利なgem群まではわかります。 とはいえ、なぜそこまでCLIなのでしょうか? プログレスバーやテーブル表示までは理解できるのですが、Markdownの表示までいくと、もうブラウザをUIに使ったほうが簡単なのではという思いです。

印象に残ったセッション #rubykaigi

Ferrari Driven Development: superfast Ruby with Rubex

RubyのC拡張を作るためのプログラミング言語の話でした。 グルー言語好きにはたまらないジャンルです。

機械学習方面の計算を早くするために並列計算をしたいのですが、CRuby上ではGILがあるのでCPUヘビーな計算を並列化できません。C拡張を書けばGILを避けて並列化できます。C拡張を書くにはC言語の知識が必要で、それがハードルになっています。Rubyライクな言語を作ることでC拡張を書くコストが下がります。

何がすごいって、こんなにミニマルで実践的なプログラミング言語を作る余地があることに気づいたことです。Rubyライクな言語からC言語に変換するだけなので実装量はそれほど大きくありません。コンパイル言語やインタプリタ言語の実装と違い、pure Rubyで書くことが出来ます。言語デザインを除けば似た実装を書くのはそこまでは難しくないと思います。それでいて、現実の問題をクリアするための言語として機能しているところ、すごいです。

もう一つは、作っている人は東工大の院生だし、ソースコードをみれば普通にちゃんとした言語処理系だしで、恐るべきです。

Parallel and Thread-Safe Ruby at High-Speed with TruffleRuby (Keynote)

CRubyの5倍速いデモのインパクトがすごすぎです。

チューニングの詳しい仕組みは、「isreading Chat Episode 07 – One VM to Rule Them All」を聞くとわかりやすいです。 misreading.chat 聞いてから振り返ると、発表前半のチューニングの仕組み部分は、GraalVMの仕組みのようです。

JITコンパイラJVMバイトコードだけであれば、インライン展開(関数呼び出しを展開するチューニング手法)できます。展開したい関数がRubyの埋め込み関数やライブラリ呼び出しの場合は、インライン展開できません。そこで、GraalVMのPartial Evaluationという機能を使い、実行しながらプロファイルを取得して、関数の呼び出し結果が定数になる場合は、定数に置き換えます。これでインライン展開を適用できる箇所を増やして、鬼のようなスピードアップを図っているようでうす。

それにしても、GraalVM用のRubyインタプリタ実装書くのめちゃくちゃ大変なのでは・・・?という気持ちです。

参考

アルゴリズムとデータ構造をたどるWebサーフィン

#rustfestの発表

yoshが #rustfest の発表が、すごい面白かったとツイートしているのを見ました。

B木の論文

リンクを貼ってある先が RRB-Trees: Efficient Immutable Vectors いきなりこれを読むのは辛いので、一番古い引用文献*1

[2] R. Bayer and E. McCreight. Organization and maintenance of large ordered indexes. Acta informatica, 1(3):173–189, 1972.

をググります。2300引用されている神文献のようです。

Organization and Maintenance of Large Ordered Indexes

どうやらB木の論文のようです。

AVL木の論文

ついでに更に最古引用文献

  1. Adelson-Velskii, G.M., Landis, E. M. : An information organization algorithm. DANSSSR, 146, 263-266 (1962).

をあさります。

G. M. Adel'son-Vel'skii, E. M. Landis, “An algorithm for organization of information”, Dokl. Akad. Nauk SSSR, 1962, Volume 146, Number 2,Pages 263–266

タイトルは違うのですが、著者と発表年を見るとこれのようです。 中身はロシア語なので読めませんでした。 ていうかSSSRって、何かと思ったら旧ソ連のことでした。驚きました。

AVL tree - Wikipediaを見るとAVL木の論文のようです。 AVLって何かと思ったら、Adel'son-Vel'skiiとLandisの頭文字でした。RSA的なネーミングですね。

この辺のことが話題になるということは、Rustは、新しいシステムプログラミング言語なので、まだデータ構造のライブラリが少なく、自分で実装するチャンスがあるのかもしれません。

素振るには?

アルゴリズムの解説はAVL木 - Wikipediaにありますが、理解するには実装するのが手っ取り早いです。 C言語やRustのような低レイヤーの言語で実装しないと理解できないのでしょうか? 使い慣れたJavaScriptで実装しても理解できるものなのでしょうか? と、悩んでいたら

AVL Treeの実装も含まれていました。 これを参考にすると良さそうです。

*1:古典なので既知の情報である確率が高い上に、わざわざ引用しているので読む価値が高いと仮定

fluxのstoreはMVCのモデルではない

結論

fluxのstoreは、(意味があって)「プレゼンテーションとドメインの分離」(PDS)に則っていないので、MVCのモデルではありません。

要約

MVCが考えられた時代では、プレゼンテーションロジックとドメインロジックが同等、もしくはドメインロジックの方が多かったです。その場合、PDSが有効でした。

現代のWebフロントエンドでは、プレゼンテーションロジックの方が圧倒的に多いです。プレゼンテーションロジックが9割ということも珍しくありません。この場合は、PDSは役に立ちません。

プレゼンテーションロジックの中で状態を持つ部分と、画面を描画する部分を分離する方が合理的に分割できます。この分離された「プレゼンテーションロジック中の状態を管理する部分」が「fluxのstore」です。

背景

以下の一連の議論を読んで、コンテキストに誤解がありそうなので、補完します。

blog.nkzn.info

nekogata.hatenablog.com

mizchi.hatenablog.com

本文

MVCが考えられた時代

Wikipediaの以下の記述を見ますと

1979年: パロアルト研究所にてTrygve Reenskaugが考案。[1][2]長い間、Smalltalk-80の実装のみが公開され、MVCに関する公開情報はなかった

MVCが考えられた時代は1979年頃のようです*1。 70〜80年代のアプリケーションの設計の話です。 Webアプリケーションはまだありません。 対象としているのはGUIアプリケーションです。 MVCは、Ruby on Rails等のWebサーバーアプリケーションでの設計にも活用されていますが、Webフロントエンドの話をする時に気にする必要はありません。

GUIアプリケーションといっても、現在のような複雑なプレゼンテーションは持っていません。 CUIに毛が生えたものと考えてください。 例えば、次の操作を想像してください。

  1. ユーザーは、GUIでパラメータを10個設定
  2. ユーザーは、実行ボタンを押す
  3. アプリケーションは、パラメータに基づいて計算をする(この間10分)
  4. アプリケーションは、結果を表示

複雑かつ重要なロジックは、3の部分にありこれをドメインロジックと呼んでいました。 それ以外の1, 2, 4は、記述は面倒さという意味の複雑さはありますが、重要度は低いです。 これをプレゼンテーションロジックと呼んでいました。

PDSMVC

このような構成のアプリケーションには、 プレゼンテーションとドメインの分離PDS)が大変有効機能します。 ドメインロジックをプレゼンテーションロジックから分離することで、入念にテストしたり、GUIの代わりにCUIをつけて他の環境で実行したりできます。

さらにMVCでは「ユーザーの入力」に関わる部分をController、「画面の表示」に関わる部分をViewと分割します。 上記の操作の

  • 1, 2がController
  • 3がModel
  • 4がView

と、綺麗に分割できました。

現代Webフロントエンドの複雑さ

現代のフロントエンドのもつプレゼンテーションロジックは遥かに複雑です。 ドメインロジックは変わらず単純です。 複雑なドメインロジックが必要な場合はサーバーで実行することが多いでしょう。

プレゼンテーションロジックは複雑です。 現代のアプリケーションでは、ユーザビリティを高めるためにユーザーの操作には即応することが可能であり、必要とされています。 例えば、ユーザーの操作に即座に反応してバリデーションの表示状態を変更します。 複数のパラメータから中間データのプレビューを作って表示するかもしれません。

次の操作をイメージしてください。

  1. ユーザーは、パラメータを変更する
  2. アプリケーションは、バリデーションを実行する
  3. アプリケーションは、バリデーション状態に変更があれば、バリデーション表示を更新する
  4. アプリケーションは、複数のパラメータから、プレビューデータを作る
  5. アプリケーションは、プレビューデータに変更があれば、プレビュー表示を更新する

これを何度も繰り返した上で、

  1. ユーザーは、保存ボタンを押す
  2. アプリケーションは、中間データをサーバに送る

このように、現代のWebフロントエンドのプレゼンテーションロジックは、MVC時代のプレゼンテーションロジックに比べて遥かに複雑です。 ユーザーの1操作ごとに、MVC時代のプレゼンテーションロジックと同等かそれ以上のロジックが実行されようなものです。

PDS適用の困難さ

前述の操作のうち、どこがドメインロジックで、どこがプレゼンテーションロジックでしょうか?

  • バリデーションはプレゼンテーションの状態を更新するので、プレゼンテーションロジックではないでしょうか?
  • 中間データの生成ロジックも、プレビューデータを更新するので、プレゼンテーションロジックではないでしょうか?

「バリデーションはサーバーと同じロジックを実行するので、ドメインロジックだ」と考えることもできます。一方で、「データをサーバに送るのは、永続化層であってドメインロジックではない」という考え方もできます。 PDSが簡単な判断基準ではなくなっています。

また、アプリケーションごとにPDSを拡張したルールを作成し、プレゼンテーションロジックとドメインロジックに分けたとしましょう。 さらにMVCに分割してみましょう。 一部は流用できるかもしれませんが、入力パラメータのバリデーションは、入力パラメータごとに実行する必要があります。 結局、ユーザーの操作1つに対して1つのMVCのセットを作ります。 これが設計の助けになるかというと、なりません。

私の経験では、ユーザー操作ごとプレゼンテーションロジックとドメインロジックの境界が揺らぎ、綺麗なレイヤードアーキテクチャにはなりません。 修正する時は、1つのMVCセットごとに、「これはここではプレゼンテーションロジックだっけ?ドメインロジックだっけ?」と悩みながら修正する羽目になります。

設計は、本来、アプリケーションの抽象度を階層化して、全体のアーキテクチャと個々のロジックを別々に悩めるために行うはずです。 PDSを適用してこれでは、教条主義であり、現場の助けになりません。

PDSの放棄とプレゼンテーションモデル

PDSを放棄します。 プレゼンテーションロジックとドメインロジックを分けることをやめ、両者を含んだものをプレゼテーションモデルとして扱います。

以上が、MVP / MVVMなどのアーキテクチャが提唱される背景です*2

結論

現代Webフロントエンドの複雑なプレゼンテーションにおいては、PDSに則ってアプリケーションを分割する意味が薄いです。 ですので、PDSに基づいているMVCのモデルは、現代Webフロントエンドの世界には居ません*3

宣伝

以上の話を、ソースコードを交えて「現代Webフロントエンドデザインパターン」の「第11章 プレゼンテーションモデルパターン」に書きました。

ledsun.booth.pm

また、「Android アプリ設計パターン入門」では、プラットフォームはAndroidですが、以上のようなUIパターンの議論がとても綺麗に整理されています。一読の価値があります。

Android アプリ設計パターン入門

Android アプリ設計パターン入門

  • 著者:日高 正博,小西裕介,藤原聖,吉岡 毅,今井 智章,
  • 製本版,電子版
  • PEAKSで購入する

*1:Wikipeidaに書いたのは私です。信用せずにWikipeida記載の参考文献を確認してください。

*2:MVP / MVVMについては詳しくありません。「Android アプリ設計パターン入門」などを読んでみてください。

*3:極々小さなアプリケーションでは、MVCが有効なこともあります。が、ほんの少しユーザー操作に反応する機能を増やすと破綻します。実例を「現代Webフロントエンドデザインパターン」に書きました。

RubyKaigi 2018 参加に関するお金の話

弊社では、今年からRubyKaigi 2018への参加支援を始めました。 今年は3人の参加者がいます。

主にお金周りの準備の仕方のメモを残します。

参加費

参加者個人で申し込んで支払いました。

以下の文書で経費精算

  • doorkeeperからの支払い確認メール
  • Paypalからの支払い確認メール

クレジットカードを持っていない社員もいたので、その人は会社のクレジットカードで支払いしました。

交通費

今回は仙台で移動時間が短いので、初日の朝と最終日の夜に移動します。

新幹線のチケットは、急な予定変更に対応できるように、 個人で取得し、後日精算します。

えきねっと(JR東日本)|新幹線・JR特急列車のきっぷ予約で予約できます。 いまどき、住所に半角文字を受け入れられない素敵なUIです。 指定席の前々日の取り消しでも310円*1なので、早めに申し込むのが良さそうです。

宿泊費

会場近くは栄えていないそうなので、仙台駅近くに、1人部屋を3部屋予約します。 会社で直接決済します。

#技術書典 #技術書典4 で「現代Webフロントエンドデザインパターン」を頒布しました

サマリ

完売。

赤字にならなかった点で成功、真の需要がわからなかった点で失敗でした。

電子版

在庫切れてしまったので、電子版を用意しました。

ledsun.booth.pm

bookwalker.jp こちらはEPUB形式です。

デザインパターンについて

GoFの23のデザインパターン

ほとんど同じでないです。被っているのはオブザーバーパターンの1つだけです。 クラス図は出てきません。サンプルコードでクラスは出てきますが、継承は出てきません。 JavaScriptでは継承、特にインタフェースの利用にほとんど旨味がありません。

GoFデザインパターンよりは広い対象に対して「デザインパターン」という言葉を使っています。 GoFデザインパターンではMVCも「デザインパターンではない」扱いでした。 現代のソフトウェア業界では、もう少し広い範囲を対象に「デザインパターン」を使っていると思っています。

デザインパターンの数

数は足りないと思っています。 パターンを考える会がやりたいです。

経費

  • サークル参加費 7,000円
  • 日光企画さん オンデマンド印刷 平とじ 68ページ 150部 58,300円
  • テーブルクロス 500円
  • コインケース 100円

よかったこと

  • 本文用紙を「淡クリームキンマリ」にしたら、「上質紙」よりかっこよくなった気がする
  • 「簡単後払い」のバーコードを印刷しておいた

次回やりたいこと

  • そろそろカラー表紙をつけたい
  • 300部に挑戦したい*1
  • オフセット印刷に挑戦したい

*1:客足と天候の因果の存在に恐怖しかない