Railsを例に枯れた技術の水平思考について考える
フレームワークは枯れた技術の水平思考なのでは?
枯れた技術の水平思考をどう説明すればいいのかを考えてたとき、フレームワークを題材にすればいい、と思いました。世に出ているフレームワークを思い出したとき、どれも跳躍したようなアイディアではなく今まであった技術を再解釈された物が多いと考えました。コンポーネント指向も古くから合った。wikipediaだと1960年台には存在していて、かなり古い。オブジェクト指向を初めて実装されたと言われているSmalltalkも1980年台に発表されていてそれよりも古い。
これらの歴史を見ると、フレームワークとは、新進気鋭のかつてないアルゴリズムの発明ではなく、長い研究を終えた技術を再解釈をされて誕生したと考えられます。もちろん、開発者本人は新しい発明であることには変わりはありません。なぜなら、このように視点を切り替えた発明もれっきとした発明だからです。コンポーネント指向を発明、発見をしたことはすごいことですし、それらを研究した人たちも偉大です。そして、彼らの研究に新しい視点を加えた人たちも等しいくらい偉大です。
フレームワークの特徴は「誰でも使いやすいように設計されている」点です。RailsもWebの技術を1〜100まで知らなくともウェブアプリを作ることができます。OSI参照モデルを知っていなくてもウェブアプリを作れるのは「誰でも使いやすいように設計されてる」からです。抽象化をしたり、OSI参照モデルの部分は知らんくてもいいように設計をされているのでRailsは使いやすいのです。
そもそも枯れた技術の水平思考とは?
枯れた技術の水平思考とは横井軍平さんという任天堂に在籍をしていたエンジニアが設計や発想をするときの考え方です。
枯れた技術とは、研究しつくされた技術を指します。オブジェクト指向もそこに入って差し支えないでしょう。オブジェクト指向を例に説明をします。オブジェクト指向の良いところは「変更がしやすい」ところです。ポリモーフィズムを利用すれば、使っているオブジェクトのメソッド名が同じあればオブジェクトを置換しても動きます。しかし、悪いところは「オブジェクトの依存が複雑だと変更がしにくい」というところです。先程のポリモーフィズムだと一箇所の型の変更でそれほど変更点が狭い場合です。オブジェクトの依存関係が複雑の場合一箇所を直したら別の箇所も直さないといけなくなります。
ざっくりとした説明ですが、ネット上でもメリット、デメリットがたくさん出てきます。つまり、オブジェクト指向のようにある程度まで出し尽くされた発明が枯れた技術です。
横井軍平さんの発明でも「光線銃SP」という商品では、太陽光パネルが光にあたったときに反応することを利用して当たり判定を作っています。太陽光パネルは発電用として使われていますが、視点を変えてゲームの的にしています。ここがすごいんです!
太陽光パネルを別の用途に転用したり、技術そのものの物の見方をありえない角度で覗いてみることが「枯れた技術の水平思考」と言えます。
枯れた技術の水平思考としてのRails
Railsは名のしれたWebアプリのフレームワークです。今やソシャゲから業務用までのバックエンドの開発でも使われている優秀なフレームワークです。Railsの良いところは一からすべてを用意せず雛形を渡されてそれを自分用にアレンジするところです。
Railsでは枯れた技術がふんだんに使われています。もっともわかりやすいのがMVCモデルです。MVCモデルは古い技術でSmalltalkと同じくらいの時期に誕生した技術です。MVCはGUIの考え方で、VIEWという画面に表示して、それに対してコントローラーを使って司令を出してModelがプログラムされた手順を実行するモデルです。
Railsはこれに「DRY」,「 設定より規約が優先される」という考え方を加えて作りやすくしています。この2つの考え方の起源はわかりませんでした。「DRY」は同じような処理を書かないようにする。例えばお金のプログラムを作るとします。Moneyオブジェクトのメソッドで米ドルへ変換してくれるメソッドがあったとしましょう。別の製作者が米ドル変換のメソッドの存在を知らなくて、新しく米ドル変換オブジェクトを作ってしましました。このような2度の実装をやらないようにしようというのが「DRY」の考え方です。
「設定より規約が優先される」は、設定値をいじるよりもフレームワークの決まりを守る方を優先させよう、という考え方です。設定値の変更はひとによってばらつきがあります。NGINXの設定値もかなりばらつきがあります。例えばAサーバーでRailsのアプリを動かしてNGINXの設定値をBサーバーで新しいRailsアプリで動かすと動かない、ということがあります。設定値はある程度まで制限ができますが、動きをはっきりとさせることがまず無理です。ですので、規約を優先させるように設計をすること万が一ユーザー側でまずい設定をしたとしても規約の方でだめなら通さずにデフォルトのままにしておきます。それかエラーを出します。
このように見ていくと、Rails自身がオープンワールドのような新進気鋭の新しい技術を使われていないことがわかります。少なくとも、基本機能はそのような真新しい機能ないはずです。
枯れた技術を使うことで、技術面で制御がしやすくなります。オープンワールドのゲームの場合はまずどうしたらメモリを気にせずにオブジェクトを展開させるか、そして〜GBのメモリのデータの置換をどうするのか、そしてユーザにそれを見破られないための方法を考えます。ここでは、技術1つ1つが非常に予測がしづらいです。アルゴリズムはあるけど、それを実装するとなる環境の問題やそれ以外のIOでの処理問題がかかってきます。仮にオープンワールドの技術が枯れていたらそのようなことを考えなくてもいいです。考えるかもしれませんが、枯れていてフレームが揃っていればそれほど意識せずに書けるはずです。
Railsが作りやすいのは枯れた技術を別の視点でわかりやすく書かれているのだとしたら、フレームワークは本当に観察をする力が求められてくると思います。
Rubyの作者のmatzさんも言語に対して非常に観察して設計されています。Rubyは楽しくプログラミングができるように設計されていて、真新しさよりも楽しさを優先していると聞いています。ということは、楽しさ、遊びというのは観察に観察を重ねてそこから新しい視点を見つけることかもしれません。
参考資料
Rustの初見殺し感がある
目次
- Rustの初見殺し感がやばい
- 初見殺しポイント
- なれる方法(対処療法)
1. Rustの初見殺し感がやばい
Rustは初見殺しが多い。Rustについて「難しい」、「難解」という意見がありますがこれらはおそらくRustの初見殺しだと思います。
逆に初見殺しが少ない言語の一例として、Rubyが挙げられます。私も仕事で使っていて、あまり初見殺しのような仕様はないと思います。
ゲームでいうとマリオみたいで、見ればある程度までは書けます。少なくとも別の言語で関数やクラスの使い方を知っていれば使えるようになっています。マリオもチュートリアルから詰むことはまずないです。そのように設計されているし、Rubyも読みやすいように書きやすいように設計されているからできる技と言えます。
Rustはどうでしょう。ゲームでいうと、フロムゲーだと思います。Rustの初見殺しとして「借用」、「所有権」、「ライフタイム」、「<>の表記」、「異なる配列の設定方法」、「Boxの使い方」がある。私もそこで躓きました。
Rustはこれらの使い方を知ればめちゃくちゃ便利なんです。
上記のうち「所有権」はこれがあるとコンパイル時に変なポインタの使い方を防いでくれてメモリの開放忘れを防ぐことができます。
メモリの開放忘れとは、使わないデータをメモリの中に溜め込んでしましす。こうなると、メモリにいらないデータが溜まってPCがフリーズします。
現に描画プログラムを作ったときにメモリが逼迫させてしまってフリーズを何回もしました。それを防いでくれるんです。
フロムゲーのBloodborneでもこの知らないがための初見殺しがあります。ゲーム内で「内蔵攻撃」という攻撃方法があるのですが、これを知らないと苦戦するんです。ひるませて相手の内蔵に直接攻撃をして大ダメージを与えます。知っていれば楽しいのですが、知らないとなんで死んだかわからないんです。
Rustの場合、「借用」、「所有権」、「ライフタイム」、「<>の表記」、「異なる配列の設定方法」、「Boxの使い方」を知っていれば、通常の書き方ができます。知らないとなぜコンパイルからエラーが出てくるのかも、その意味もわからないままゲームオーバーになってしまうんです。
Rubyの場合このようなところで躓くことはまずなです。そもそもRubyの書き方は今までのプログラミング言語をうまく配合したような作りをしているので分かりやすいです。
エラーが出たとしても基本的な機能なら読めばどこでエラーが出ているがわかったり、静的型付け言語ならエラーになるところをRubyがうまく回避してくれます。つまり、詰みにくいように設計されているのです。
マリオでも、それほど詰む人がないはずです。それと同じで、Rubyは詰む部分が比較的少ないと思います。
2. 初見殺しポイント
配列のコード
trait Animal { fn talk(&self) { println!("souzou "); } } #[derive(Clone, PartialEq, Debug)] struct Status <'a> { pub hash: &'a str, pub state: f64, } struct TestStruct { } impl Animal for TestStruct {} fn main() { let mut v: Vec<Box<Animal>> = Vec::new(); v.push(Box::new(Status{hash:"test" ,state: 1.0})); v.push(Box::new(TestStruct{})); }
すぐに出せるコードとしては、この異なる型を配列で持たせる方法です。
Rubyの場合はなんの苦労もなく入れることができるんですよ。Rubyでうまく解釈をしてくれて配列に異なる型を入れることができるんです。Cでもちょっと強引ですけど、ポインタ変数の配列を作っておいて配列を作ることができます。
Rustの場合は型がしっかりしているから、どのアプローチも使えないんです!Rustは配列をIntならInt, FloatならFloatで決められたら変換をしない限り、Float配列にIntを入れることができないんです。逆も同様です。
これはコンパイル時にすべて型をRustが定めてくれて、型が間違っていたらコンパイル時にエラーを起こしてくれるんです。面倒かもしれないんですけど、事前に「ここバグりそうなので直して!」といってくれると致命的なバグを回避させてくれるんです。
それで異なる配列を作ろうとすると、Rustのコンパイルを通さないといけないんです!Rustには継承に相当するシステムはありますが、従来のオブジェクト指向のように扱うように作られていないように見えました。traitはinterfaceのような動きをしているように見えたので「これ継承のように使うん?」と思って却下しました。
やっていくうちにわかったんですけど、このtraitとBoxを使えば行けることがわかったです。
let mut v: Vec<Box<Animal>> = Vec::new();
Box
すると、Animalのtraitを継承させれば配列に組み込むことができます。あとは関数型でもオブジェクト指向でもいいので配列に命令を送ることができます。
3. 慣れる方法(対処療法)
慣れる方法ですが、おそらくRustの型システムを理解するようにしたほうがいいと思います。やってみてわかったのが、Rustの難しいところはいわゆる型システムが今までと毛色が違うところです。
CはRustと比べて型システムが緩いんです。Cだと選択肢として「ポインタ変数を使う」とかがすっと出るんですけど、Rustではそれが一切なかった。Boxを使えば値をヒープに移しておいて必要なときに使うようにする。Boxに値を入れておいてあとは中にAnimalがいればよし!とすればいい。
Rustは型システムのおかげで低レイヤーで起こる問題を解決しようとしています。逆をいえば型システムの動き方さえわかれば書きやすくなります。
この型システムの説明はまだ私はできませんが、ここさえ説明できれば従来の方法の通りの教えることができます。
オブジェクト指向について書いてみた結果
休日は、オブジェクト指向についての記事を書いていました。
というのも、去年の12月の終わりあたりからオブジェクト指向についての記事書きたいな〜と思って書いていました。
書いたきっかけは、オブジェクト指向の説明って結構ピンと来ないものばかりだったので自分の中で噛み砕いて説明できるようになりたかったからです。
インターネット上の説明でも分かりにくいものも多く、おそらく言い方だったり、説明の仕方が原因だと思っています。
たい焼き、犬猫の説明でオブジェクト指向について説明しようとしています。
この説明は確かにオブジェクト指向について説明はされています。たい焼きの話もインスタンスの説明としてはあっているし、犬猫の話も継承について説明としてはあっています。
しかし、この説明だけで何も知らない人に対して「分かった!」と言わせることは無理だと思っています。
中学生にこれを説明するのであれば、たい焼きや犬猫の話をされても「なにいってんだこいつ。」となると思うんです。
だって、機械のことを説明しているのにいきなりたい焼きの製造方法したり、生物学の分類について説明するのって、2つとも知っていたらなるほど!となるかもしれませんが、なんの脈絡もなく話されても理解したくても理解できないです。
現に資料を作るまで僕もあまりいいイメージのつかみ方が分かっていませんでした。本で読んで、コードを書いていてふわっとした理解をしていましたが、改めて資料を書いてみると100%理解をしていなかったことが分かりました。
校閲をかけたり、ブラッシュアップするためまだ公開をすることはできませんが、この説明の根本にした物はアラン・ケイが考えたオブジェクト指向を参考にしました。
アラン・ケイ自身の研究はコンピューターをもっとフレンドリーにしようということをしていました。コンピューターを子どもにも使えるようにするためにDynaBookを考案するくらいでした。
アラン・ケイの最初に出したSmalltalkのオブジェクト指向の考えの元はメッセージングがありました。機械が命令を出しあうという形でソフトを組み上げていく考え方です。
僕の中ではこれが一番しっくり来る方法でした。
なぜなら、今までのオブジェクト指向の説明の仕方よりも明確な基準があったからです。
おかげで、オブジェクト指向についての理解が深まりました。
まだ100%の理解とは行きませんが、やれてよかったです。
rustでゲームを作ってみて
かねてよりやっていたrustでのゲームの開発をしていました。
ひさびさのRustだったので、借用権とか所有権とかコアになる仕様を忘れていたのにびっくり
2日間で、すこしだけゲーム作成に対してイメージがつかめたような気がします。
というのも、最初は仮面ライダーのゲームを作りたいという願望から始まった企画だったので、頭の中でイメージがあってもそれをどうやって作ればいいのかが全くイメージがつかめませんでした。
ですが、2日間やってみて気づいたのがゲームを手軽に作るという考えではなく、
「子どもがキャンバスにキャラクターを動かしたらコントローラーを持ってそれを動かせるようにする。」というイメージが一番しっくりくるのではないのか?と気づきました。
最近オブジェクト指向についての資料を趣味で作ることがあり、SOLIDの原則とSmalltalkについて調べまくりました。Smalltalkの設計思想の一つにこんなのがありました。
「再帰設計の基本原理は、部品が全体と同じ力を持つようにすることだ」
この考えはオブジェクト一つ一つは全体に影響を与えるほどの大きな力を持つということだと考えています。現にSOLIDの原則でも明らかに一つ一つのオブジェクトをなんでもできる物を作ることよりも特化したオブジェクトのネットワークにするようになるようになっています。
子どもがゲームを作るという行為をお絵かきの延長線上でできるようにする。僕自身ができない人間だから、特別な知識(微積分、行列など)の知識がなくても筆を取ってキャラクターができてそのキャラクターがすぐにそのまま動かせるようになるというイメージならば、アラン・ケイの考えたオブジェクト指向に近いと思いました。
今回SDL2をrustで使える物を使っているのですが、&mutを使うことが多くてメモリの使い方をかなり考えないといけなさそうです。
というのも、&mutは生ポインタを使うのでメモリ安全でないポインタを使うことになります。すぐにどうこうなるというわけではありませんが、rustのメモリ安全ができるという利点を損なうことをしなければなりません。
しかし、ゲームのプログラムではもしかしたら、多少のメモリ安全を損なうことをしなければらないかもしれません。
ですので、直近ではメモリ安全を損なうやり方は使います。ただし、それを回避することができるようにはしたいです。
あと、できればこのゲーム開発でテスト駆動がrustでできるようにもなりたいです。これができれば、ある程度試しながらできるので僕のやり方にぴったりかもしれません。
(僕はメモリを頭で管理できるほどできがよくないので)
参考資料
Smalltalk:Wikipedia ja.wikipedia.org
実践rust入門
mrubyでの30日開発日記(22日目)(GWのまとめ編)
GWなにをしていたか
- ゲームのために必要な数学を学んでいた。
自分のスペック
数学:高校1年前半で止まっている。学校のコースが文系で、数学を一年で終わらせるコースだった。なおかつ、数学は数学Iをやった程度だったと思う。(Aをやった記憶がない)大学時代に克服しようとするも挫折しまくる。
なんでしたのか?
ゲームを作るにあたってさまざまなことが必要だと実感したからだ。もともと3Dゲームを作るつもりでいた。仮面ライダーを描画するのとそれをストーリーにするのにドット絵だけだと表現が制限されるために3Dにした。もちろん、僕に作家性というのがあれば2Dでもできただろうが、僕には作家性というのは乏しくやるなら徹底的にやろうと思い3Dにした。
だが、3Dをするにあたって避けては通れない数学があった。おおまかに2つ。微分積分と線形代数だ。線形代数は3Dを描画するときにどこに点を置いて画面に映すのかに使い、微分積分はゲーム上で重力だったり風の動きだったりと使う。
仮面ライダーWを映す時は、スカーフをなびかせたいし低スペックのものでもきれいに映した。そんな欲望からやろうと決意した。
障害はなんだったのか。
もちろん、7日間であって(途中旅行が入った)3つと障害となるものがあった。
- 時間
- 目標
- モチベーションの低下
この2つが主な障害となった。
まず時間だが、これが一番頭を悩ませた。最初に取り組んだのた、大学への数学を解いて高校数学をどうにかするとということだった。もともと、高校数学で止まっているということもあり、これをやらなければやばいと思った。最初は「7日もあるし大丈夫だろう!」と思ったが中盤あたりに「やっぱり時間が足りない!」ということがわかった。
そりゃあそうで、僕が思うに数学は理解に重きを置く学問でありそれを7日でやるのは無理があった。これによって目標が達成できないのではないのか?という懸念が出てくる。
目標はそれに達成できないのではないのか?という焦燥感である。もちろん、やっていくうちにできないのではないのか?と思いスケジュールとかをちゃんと計算し直すようになる。目標が結構重しになることがあるということを忘れていた。
すると、モチベーションの低下が起こる。どういうことかというと、目標がたどり着けないし、これで本当にゲーム製作に近づいているのか?ということを問題視する。すると、じょじょにモチベーションが下がっていく。現実逃避する。アニメを見る。時間がすぎる。これの繰り返しになりそうになる。
どうやってモチベーションを持ち直した?
ここからはモチベーションをどう持ち直したか?を書いていきます。
精神論で片付けたのではなく、あくまでどうやって目標を達成するかの手段を変えたりしてどうやってゲームを作る事につなげるのか、を重点的に考えた。
- ゲーム作りに必要な分野を抽出する
- 集中するために何をする必要がないのかを考える
- やり方を見直す。
主にこの3つをやった。
まず最初の項目だが、ゲームで必要な分野を抽出するというのは数学の分野は多岐にあるので、とにかく集中をするためにどの分野が必要かを調べた。調べた結果、使う数学の分野は
の6つだった。これを優先順位で並べ替えると
ということになりました。優先順位はとりあえず最低限必要な物の順番で並べた。
これが分かったら、自動的にやらなくていいことが分かり、この順番で学んでいく。
そして、やり方を変えた。一番変えたのは、「時間を分割する」ことと、「復習する」ことを付け加えた。
時間を分割するのは、ぶっ通しで集中しないようにする。やろうと思えば一時的ならぶっ通しでできるがこれが続くとけっこう体力的にも気力的にも持たない。メタルギアでいうと気力ゲージと体力ゲージをごっそり持って行かれて、次の日リトライしているときにはマックスの半分しか回復していない。これが続けば、4日くらいで体力がなくなる。だから、ぶっ通しではなく25分やって休憩5分を数セットしてから大きく休むという「ポモドーロ・テクニック」を採用した。これによって、体力の回復がしやすくなった。
復習をするのは、覚えやすくするのと理解を深めるためにもやる。理解を深めないといざという時に役に立たないし、もう一度やると新しい発見がある。ユダヤの格言でも「学んだことを復習するのは、覚えるためではない。何回も復習するうちに、新しい発見があるからだ。」とあったがあながち間違いではなかった。
mrubyでの30日開発日記(19日目)(番外編)
今日やったこと
- 大学への数学を解く、読む
今日の発見
大学への数学をここ4日間やってわかった。
あきらかに、時間が足りない!
高校数学やってないからって、とりあえず量が少なくて、質のいい問題がある大学への数学をやったが、そもそもゲームを作るためにやっているから、カリキュラム通りやる必要もなかったかもしれない。
こう思ったのにも理由があって、今日「新・数学の学び方」の小平邦彦の「数学に王道なし」で書かれていたことがきっかけだ。この話の中では、もともと著者が中学生のころから数学が好きで、そのために「代数学」という本をかじるように読んでいたおかげで高校数学も大学数学も学ぶのに苦労しなかったということが書かれていた。
もちろん、天才というエピソードというようにも捉えることができるが、これがヲタクのような考え方によく似ていると思った。僕が、アニメにハマり始めた中学生のころはさまざまな作品が乱立し競い合っていた時代でした。(今もそうだけ、もっと凄まじい勢いで深夜アニメが盛り上がっていた時代)その中でおおよそ中学生が分からないであろう物語もありました。僕の中で一番当てはまったのは、「狼と香辛料」だろう。
「狼と香辛料」は、商人ローレンツが狼の神様「ホロ」が商いをしながら旅をする物語だ。これの中では商人の生活が描かれており、取引に現金だけではなく、毛皮や小麦を使ったり、ほかにも投資をしたり、バブルが起こったりというように中学生が見るには早そうな気もするような物語であった。
最初はわからないでいたが年を重ねるうちにその物語で何が起こっているかが分かってきて、理解したいから調べるというようなきっかけにもなった。高校生の頃にドラッカーを読み始めたのもそういう影響があったのかもしれない。
これほどまでにできたのは、おそらくその作品への愛情というか好意というか。そういう計り知れない思いなような気がする。
ゲームに関してもそうだろう。僕が作りたいのは仮面ライダーを映した。それだけの動機だ。ぼくが思うに、そういう明確で単純な動機を持っていてやることはやっていくうちにいろいろなことが身につくものではないのかと思う。数学も「今は」ゲームのためにやっているが、数学者になりたいからやっているわけではない。だから、試しに必要な知識「アルゴリズム、微積分、線形代数」だけに絞ってやってみようと思う。
これはテストじゃないから、少しくらい邪道になってもいいような気がする。
こういう理由があるから、やる分野を絞ってみます!
では
ciao!
参考文献
mrubyでの30日開発日記(17日目)
きょうやったこt
- 大学への数学を解いた。
今日の発見
数学って、変数で考えるほうが楽なことに気づいた。小学生までは定数を使った計算が主流で、それが高校になってから変数中心の計算に変わっていった。おそらく、この移行が高校数学を難しく感じる原因だと思う。
中学までの数学は算数のように主に計算を中心にし、xのような変数をつかった計算を使わなかった。算数というのは日常生活で必要とされる計算を学ぶためのものであり、四則演算をめちゃくちゃ使っていけば解ける問題が多かった。要するに、計算を中心にしている。
変わって中学生から行う数学は、なるべく計算量を少なくしようとする動きがある。少なくとも、数学を解いている時は「いかに計算量をすくなくするのか」を重点的に考える。というよりも、そうしないとまず解けない。
要するに、算数のように正確に計算をするのか、そもそも計算する量を少なくするのかの問題なのです。
【算数/数学】数学を勉強する意味って?数学が苦手になる中学生の特徴は | 新潟県新潟市の家庭教師・ホームティーチャーズでも、同様の算数と数学の説明がありました。
これらを踏まえて考えると、正確性をいかに極めるかを訓練した人が数学のようにいかに効率よく考えるかを訓練する分野に足を踏み入れれば、苦労するのは当然だ。
これが発見できたのは大きな成果だと思います。この「いかに計算量を少なくするのか」はプログラミングで必ず必要となる分野になります。そうなると、アルゴリズムを学ぶ意義というのも分かってきます。
(*-ω-)。o○(まあ、その主な動機が仮面ライダーを映したいっていうのなんだけど)
では!