Hatena::ブログ(Diary)

土屋つかさのテクノロジーは今か無しか

2017-05-27

冗長性の無いプログラミング言語を作ろう(1)

 土屋はプログラミング言語の設計と実装が趣味です(この2年間くらい、本業以外で飽きもせずやっているのがこれだけだから趣味なんだと思う)。これまで作っていた司エンジン( http://d.hatena.ne.jp/t_tutiya/20170212/1486903365 )が、実用性はともかく(!)、そろそろ言語仕様レベルでは完成に近づいてきたので、そろそろ次の言語の設計を始めます。

冗長性の無いプログラミング言語を作りたい

 定期的に書いていますが、土屋は「一意なルールでバグのないコード書き、それによって生産を向上させたい」のです。この一意なルールというのは一般的なコーディングルール的な物ではなく「ある要求仕様をコーディングする際に、そのアルゴリズムが一意に定まる」くらいの物です。

 土屋は、現在のプログラミン言語は冗長性が高すぎて、そのためにレビュー効率が落ち、バグの発生を助長していると考えています。これも定期的に書いてますが、本来プログラマは医者や弁護士と並んで国家試験が必要な職業で、原発/病院/銀行など、生命に関わるシステムの開発は、国家認定プログラマ以外は関われないようにすべきです。

 しかし、現行のプログラミング言語は冗長性が高すぎるため、ある仕様を記述するのにいくらでも書きようがあるため、優れた(≒バグ実用レベルで少ない)コードの書き手機械的に評価する方法がないので、実用的な国家認定試験は成立しないでしょう。

 そこで今回は、冗長性をできるだけ抑え、その上で生産性の向上が狙えるプログラミン言語を考えてみます。

トランスパイラのビルドスクラップ

 司エンジンはRubyの内部DSLとして実装しました。今回はC#トランスパイラとして新言語を設計&実装します(言語を作りたいのであって、コンパイラを作りたいわけではないので)。なので基本的にはC#の言語仕様を拡張する形になります。

 司エンジンは「仮説として言語仕様設計する」→「設計に基づいて実装」→「サンプルコードを書いてみる」→「仮説が不成立であれば作り直し」というサイクルを繰り返しました。今回もこれを踏襲します。ただし、司エンジンでは完成形がある程度見えていのたで実装が割と早かったのですが、今回はまだ仕様自体がぼんやりしているので、暫くは設計が続く予定です。

 途中で諦めるかもしれませんが、アイデアレベルでブログに記事を書いていくので、どうぞよろしく。

2017-05-20

「正しく書かれたソースコードにコメントは必要ない」なんて幻想だという話(補足編)


 前回書いた「「正しく書かれたソースコードにコメントは必要ない」なんて幻想だという話( http://d.hatena.ne.jp/t_tutiya/20170519/1495197904 )」は土屋のブログ記事にしては珍しくはてブやコメント、twitterで反響を幾つか頂きました。その中の幾つかを紹介します。分かっているとは思うけど、サンプルコードはJavaScriptを意識してますがてきとーです。

案1:マジックナンバーを定数化する

MAX_OPAQUE = 1.0f //定数宣言のつもり
if (alpha < MAX_OPAQUE){
 //処理A
}

 "1.0f"に意図を設定すればコメントが不要なのではないか説。うーん、これはちょっと難しいかな……。"MAX_OPAQUE"が正しい変数名だとは思えませんが、どんな名前にすれば意図が説明できるのかちょっと思いつきません。また、数式が複雑化した場合に対応仕切れないのではないかと思います。

案2:条件式をメソッド化する

function isFadeinCompleted(){
  return alpha < 1.0f
}

if (isFadeinCompleted()){
  //処理A
}

 条件式をメソッド化し、そのメソッド名で意図を説明すればコメントが不要なのではないか説。ここではalphaがメンバ変数であると仮定していますが、そうでない場合もメソッド呼び出し時に引数が一個増えるだけです。

 これは一つの回答と言えます。ただ、この条件式がコード上でこの一箇所しか現れない場合に、「(コメントではなく)コードのみで意図を説明する」ためだけにメソッド化すべきなのかには疑問があります(名前空間汚染も起こりますし)。

 実は前回の記事でも当初はこのコードを書いていたのですが「実務で書く時、土屋はこのメソッド化はしないな」と考え、削除したのでした。これが何故なのか理論化できていないのですが、土屋の中では「1行の式を返すだけの処理をメソッド化しない」というルールがあるような気がしています(これはいずれまた検討してみます)。

※5月21日22時追記:ここの「1行の式を返すだけの処理をメソッド化しない」ですが、「一箇所からしか呼ばれない1行の式を返すだけの処理をメソッド化しない」と書こうとしたのを間違っていましたすみません。複数箇所から呼ばれる処理であれば行数に関係なくメソッド化を検討します。

案3:条件式を一時変数化する

var isFadeinCompleted = alpha < 1.0f

if (isFadeinCompleted){
  //処理A
}

 条件式の実行結果を一時変数に代入し、その変数名で意図を説明すればコメントが不要なのではないか説。

 これは土屋が書かないタイプのコードで、なるほどと思いました。条件式がここ一箇所しか使われていないとしても名前空間を汚染しませんし、「1行の式を返すだけの処理をメソッド化しない」という土屋のマイルールにも抵触していません。また、一般的なコンパイラなら最適化されてオーバーヘッドは発生しないでしょう(もしかしたらlint時に警告が出るかもしれない)。

 ただ、自分が書くかというと、どうかなあ……? そこまでしてコードで意図を説明しなければならないのか? と感じます。本来無理筋である(と土屋が思っている)「コード=ドキュメント」を達成するために、逆に回りくどいことをしているように思えます。

コメント工学のススメ

 「コメントで設計意図を記述すると二重管理になる」という批判をよく聞くのですが、結局なにかしらの仕様書を出力しなければならない以上、どこかで自然言語のテキストを書く事になるのであれば、それがコメントとしてコードに埋め込んである方がトータルの作業コストは下がるのではないでしょうか。

 これは土屋が作家だからなのかもしれませんが、「なぜコメントの(セマンティクスの)記法が検証されていないのか」については強い疑問があります。土屋は「コメントを機械的に書きたい」と常々思っていて、自分の中ではなんとなくなルールを定義して運用しています。これについてはまたいずれ記事にしたいと思っています。

 近年ではコメントにタグを埋め込むことで自動ドキュメント化できる仕組みがありますが、もっとプリミティブな部分で「コメント」を検証する必要があると感じています。コメントは必要だ! 何百行もコメント無しで書くんじゃねえ! 後任者がメンテできねえだろうが!(私情)

2017-05-19

「正しく書かれたソースコードにコメントは必要ない」なんて幻想だという話

土屋はプログラマの中でもかなりコメントが多いタイプだと思っています。理想を言えばコード1行ごとにコメントを書きたいくらいです(本当は、識別子を全部日本語で書きたいと思っているのですが、これはまた別の話)。

 これは土屋がPDL(Program Design Language https://www.gamedev.net/resources/_/technical/general-progra... )開発スタイルの薫陶を受けているからなのですが、それとは別に「そもそも日本人には英語ベースのソースコードを高速に読解するのは無理がある」と考えているからです。

 また、より本質的な課題があると思っていて、プログラミング業界的には「コードがドキュメントであるべき(≒正しく実装されたコードであれば、コメントは最低限になる)」という風潮がありますが、土屋は、これはプログラマが夢見る幻想に過ぎず、「本質的に、コードはドキュメント足り得ない」と考えています。

 例えば以下のようなコードがあったとして、

if (alpha < 1.0f){
 //処理A
}

 処理Aが実行される条件が「alphaが1.0fより少ない場合」なのは自明です(なのでそれをコメントで書く必要はない)。
 けれど、「「alphaが1.0fより少ない」とはなんなのか」は、このコードには書いてありません。そのような、コードには現れない「意図」のコメントが必要だと考えています。

 例えば以下のようにコメントを書けば、条件判定の意図が明確化できます。

//フェードインが未完了の場合
if (alpha < 1.0f){
 //処理A
}

 上記は極端な例ですが、実際にコードを書く時にはこのようなプリミティブな処理を記述することが大変多く、3日前に書いた数式の意図を実装者が思い出す所からコーディングが始まる無限地獄を回避する為には、このようなコメントが必要なのです。

 逆に言えば、「正しいコメント」を書く技術というのがあり、その習得は難易度が高いと言えます(上の条件に「alphaが1.0fより少ない場合」とコメントしてあったら「それは見ればわかるだろ!」と叫びたくなる)。

2017-05-03

メッセージ指向ゲーム開発言語「司エンジン」v2.3b4リリース

.■v2.3b4リリース
・Collisionableにアフィン変換系プロパティ追加
・Collisionableに衝突判定有効フラグ/アフィン変換反映フラグを追加

v2.3b4リリース

・b3の落ち葉拾いです。今回の改修で、CollisionableはほぼDXRuby::Spriteのラッパーになりました。こんなにシンプルに実装できると思っていなかったので、ちょっと拍子抜けしてたりします。
・現状、複数オブジェクト同士の衝突判定はできませんが、それは多分専用のコントロールを作ることになるのかなと思っています。

Collisionableにアフィン変換系プロパティ追加

・衝突判定の形状をアフィン変換できるようにしました。拡大縮小回転が可能です。Imageと異なり、center_x/yはデフォルトで[0,0]になっているので注意してください(コリジョン形状がどうなるかわからないので、中心点を決められない)。
・CollisionableLayoutを使う場合、描画要素と連動しないので二度手間ではありますが、Collisionableを継承して描画コントロールを作ればいいかと思います(ただ、それでまともに動くのかは未検証。Mix-inしている状態での親クラスメソッド呼び出しがイマイチわかってない)

Collisionableに衝突判定有効フラグ/アフィン変換反映フラグを追加

・衝突判定を有効にするか判定するフラグとアフィン変換後の形状を衝突判定に利用するかを判定するフラグを追加しました。DXRuby::Spriteの同名プロパティラッパーです。

2017-05-02

メッセージ指向ゲーム開発言語「司エンジン」v2.3b3リリース

・Layoutableにabsolute_x/yプロパティを追加(それに伴い内部ロジックの更新)
・Clickableコントロールでマスク画像を相対パスで指定できるようにした
・Collisionable生成時にshapeオプションを必須にした
・WindowをWindowサイズからshape形状を生成するようにした
・(内部処理)ClickableモジュールのMixin対象をLayoutableモジュールからCollisionableモジュールに変更
・(内部処理)Collisionable#shapeをメンバ変数で持つのをやめ、DXRuby::Sprite#collisionを直接読み書きする形式に変更

v2.3b3リリース

・b2で「今さら直さないけど」と書いたマウスカーソル座標の管理ポリシーをまるっと書き換えました(!)。

Layoutableにabsolute_x/yプロパティを追加(それに伴い内部ロジックの更新)

・各コントロールの絶対座標を保持するabsolute_x/yプロパティを追加しました。インターフェイスは変わっていませんが、この更新の為に内部構造がゴリッと変わってまして、多分v2.3最大の修正箇所になります。
・b2でも書いた通り、これまではClickableを実現するためにコントロールツリーをたどるたびに上位からマウスカーソル座標を相対化して下位に伝搬していました。自分で書いていてもよくわからないコードだったのですが、描画の座標伝搬はやたら複雑なロジックになっていて、できれば触りたくない領域でした。
・しかし、Collisionableが追加された結果、Clickableは明らかにCollisionableの派生コントロールになるべきなのに、この相対化処理のせいで統合できず、最終的にClicableの方を修正することにしました。
Excelで描画機能を持つコントロールの親子関係を網羅し、適切なパラメーターがなんなのかを洗い出しました。その結果、これまで1個のパラメータ伝搬で済ませようとしていた箇所が、実際には2個パラメータが必要で、それによってコードが複雑化していることがわかりました。
・改修が済んでロジックもスッキリしたし、全てのコントロールが絶対座標を持てるようになったので、任意の階層にいるコントロール同士での衝突判定が可能になりました。
・これで、司エンジンにDXRuby::Spriteの機能が追加されたわけですが、現状ではアフィン変換に対応していないためラッパーとしては不完全です。これは今後対応予定。
・課題としては、1:nでの衝突判定処理がありますが、これはさすがに汎用的な手段を用意するのは無理かな……。専用のコントロールを作るしかなさげ。