菊池 Blog

移転しました 続・菊池 和彦の足跡

AILight Banner
AILight Blog

プロフィール

菊池 Blog

目次

Blog 利用状況

記事分類

過去の記事

タグ

仕様は変わっても真理は変わらない

暗黙的型付け(その2) を見て

var の使用是非については一度書いてますがもう一度書いてみましょう。

単純に受け渡すだけでは論点がはっきりしなくなるケースもあるので以下の様なコードを考えて見ましょう。

var amount = this.Amount;
var unitPrice = this.UnitPrice;
var price = amount*unitPrice;

varを多用していますが、数量に単価を積算して金額を計算するコードです。

このコードは単純な「真理」です。

「真理」であるから型が何になろうが、このコードの意味する事は変わらず永遠です。

 

さて、「ソースコードに型を書く」という事は「ソースコード上に仕様のスナップショットを断片的に残していく」事に他なりません。

要するに型名をソースコードに明確に書くという事は、ソースコードにその時点でのAmountはdouble型であったという事のスナップショットを残したわけです。

amount は総量や個数を表す言葉ですので、ユーザによっては絶対に整数かも知れません。また、doubleには15桁の有効精度がありますが、doubleの仮数部は54ビットしかありませんので、longの64bitの値域を完全には整数表現できないのは自明です。絶対に誤差が発生してきます。

要するに型名を明示してしまった事によってソースコードはある種の専用化を受け、値域限界や精度限界というものに縛られる存在となったわけです。

「ソースコード上に仕様のスナップショットを断片的に残していく」という事を繰り返せば「仕様変更」という3回やればプログラマーを殺せるといわれて居るものに当然に弱いという事も理解できるでしょう。

 

型推論や匿名型の是非でよく出る言葉、型が明示されて「わかりやすい」、「読みやすい」に対するトレードオフとしてソースコードの「汎用性」や「可搬性」というものが犠牲となった事に皆さんお気づき頂けたでしょうか?

Genericの必要になった経緯は@ITさんに記事として書かせてもらっていますが本来汎用なアルゴリズムが型を限定することによって汎用ではなくなってしまうこと、そして型が変わる毎に繰り返される移植作業がバグという名の突然変異を生み出す事を防ぐ手法です。

「汎用的で可搬性の高いコードを書くためには型の決定はできうる限り遅らせるべき」なのです。

それを完全に実行時まで遅延させる手法が各種インタープリタ言語に代表される現状の軽量言語であり、コンパイル時まで遅延させる技術がC#3.0での型推論なのです。

「汎用性や可搬性を犠牲にして、(何かの為に)型を明示するというトレードオフを行った」という意識を成しに「型を明示するコード」を「これまでの習慣だから、なんとなく読みやすいから」という理由で書くのは大間違いだという事にガッテンしていただけましたでしょうか?(ぇ)

 

勿論、あなたのコードが特定顧客向けのコードで、その型を利用する事があなたの仕事(ドメイン)についての真理であって永遠であれば型を明示することによる弊害はありません。

varとは「仕様」でなく「真理」に基づいたコードを記述する為の手段であり、型という「仕様」の決定をコンパイル時まで遅延させるという目的に対して使われるべきものです。

DBのフィールド型が int だから int で書いて良いわけではありません。int だという事が「真理」でなく単なる「仕様」であれば、できうる限り int と記述せずに var で書くべきです。それはコードの汎用性や仕様変更に対する耐性という目的に対しての努力として var で書かれるべきなのです。そして読み手としても var は「その時点での仕様による型」として理解し、まず仕様理解をしてコードを読むべきで、その努力を怠るべきではありません。

 

まとめましょう。

ある変数 x の型が T である事が「単なる現状の仕様」であれば可能である限り var で書くべきです。そうでなく「永遠な真理」であれば T で書くべきです。

それは T を明示することによって本来は汎用的であり可搬性のあるコードへ現状の仕様が混入することで汎用性、可搬性が下がったり、無くなる事を防ぐために必要なことです。

具体型 T で書くべきか var で書くべきかを検討するには、それが真理であるかを検討する必要があります。

 

追記:

めんどうだったからやらなかったんだけど、サンプルを綺麗にしてくれたのでリンク

partial class + generic = template ?

partial によるクラス分割を利用して、仕様と真理を切り離すコーディングはきちんとやればできます。

自分としてはどんな技術にもメリットデメリットはあるし、可読性という要素はソースコードに対する数ある尺度のうちの一つに過ぎません。可読性が悪くなるからvarはダメというのは正しいのですが、一つの尺度で物を見るからダメと決まるだけで絶対悪ではありません。

根本的には読み手、書き手が多用な尺度でソースを見れる目を持つ事が一番重要で、ソースコードに対して可読性以外の視点で見ることができれば、varは害悪でもなんでもない存在であると思います。

投稿日時 : 2007年12月26日 20:09


コメントを追加

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 7:18 ひろえむ

んー、言わんとすることは理解できるのですが、それでもvarは型を指定していないワケではなく、結果的には型指定しているので、可搬性や汎用性という目的とはちょっとずれているように思います。

たとえば実際の演算ではintなのかlongなのかはたまたdecimalなのかで取り扱いが変わります。 DBなどで永続化されるデータであればなおのことですね。

これらの型の決定はコンパイル時まで遅らせるという発想でコーディングするケースもある意味ありといえばありですが、実際にはそういうケースだけではなく型が決定された前提でないとコーディングはすすめられない場合も多く存在するように思います。

そういう意味ではGenericのような汎用性を持たせる機能(結果的に可搬性を持たせるような機能)と型推論や匿名型は別の技術(というか単なるシンタックスシュガー)のように思うのですがどうでしょう?

という前提に経てば、やはり使いどころをきちんと決めて利用しないと結構varは危険な存在のように思います。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 9:58 シャノン

数値型が全部、多倍長実数なら解決する問題ですね。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 9:59 菊池

「思います」が多く根拠がわからない事が多いので聞き返しが多くなりますが…

>んー、言わんとすることは理解できるのですが、それでもvarは型を指定していないワケではなく、結果的には型指定しているので、可搬性や汎用性という目的とはちょっとずれているように思います。

var x = Hoge.Hage

「xの型はHoge型のHageと同じになる」と読むか「xの型はHoge型のHageの仕様に従う」と読むかですね。
 これがHogeHoge型にあったとき、HogeHoge型を「修正するべき理由が一つ減る」、修正するべき理由が減らせるのに可搬性や汎用性と切り離すべきだという根拠はなんでしょう。

>たとえば実際の演算ではintなのかlongなのかはたまたdecimalなのかで取り扱いが変わります。 

 値段を計算するのに量に単価を積算しないなんてありえますか?
 平均を求めるのに合計を件数で割らないなんてありえます?
 数学の3平方の定理が成立しない数値型はありますか?

 真理であり定理、公理というのは型が何であれ成立するので真理であり定理、公理なんです。

 ビットシフトとビット演算で全部計算してやる主義の人にはvarはお勧めできませんけど、数値型で普通に計算する人については var で問題起こる例を探すほうが難しいはずですが。
 まぁ、値域限界とかで無効な計算になる事を気にしているのであれば var 以前にchecked { } を使う事をお勧めしておきます。

>DBなどで永続化されるデータであればなおのことですね。

 ではDBでの永続化層にテーブル直接アクセス以外にストアドやビュー、トリガといった中間層を挟み込む方法がたくさん用意されているのはナゼでしょ?


>そういう意味ではGenericのような汎用性を持たせる機能(結果的に可搬性を持たせるような機能)と型推論や匿名型は別の技術(というか単なるシンタックスシュガー)のように思うのですがどうでしょう?

 そもそも、これがシンタックスシュガーだったり別の技術であるかは自分にとってはどうでもいいことです。
 using はシンタックスシュガーだから使っても意味が無い? イテレータもIEnumerableを実装する為のシンタックスシュガーだから使うべきではない?
 LINQも殆どはシンタックスシュガーなんだけど同様?

 そうではないですよね?

 どんな技術でも目的や用法を誤れば危険なことになったり被害は起こります。
 それがシンタックスシュガーであろうかなかろうと同じことです。

>という前提に経てば、やはり使いどころをきちんと決めて利用しないと結構varは危険な存在のように思います。

 えっと、Hoge( Hage() ) もHageの戻り値の型が明示されないから危険という事で良いんでしょうか?


 良く読んで下さい。使いどころを考えずに闇雲に使えとは自分は一言も言ってないですよ。

 目的、根拠、用法の3つが揃って別の目的で別の根拠に基づいて、用法が別の結果である「型を明示するべきだ」にたどり着く結論はあって良いと思っています。
(それらが多く集まることによって、こういう場合にはこうするべきだというケーススタディが生まれるわけです)

 目的が「可読性」で、根拠が「書いてないじゃん」で用法として「型を明示するべきだ」とは、最初の目的が違うので違う結果になりました。
 それだけです。

 全てのコードを「汎用性、可搬性」だけを目的として書くべきだ、全てのコードで型なんて明示するなと言っているわけではありません。

 

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 10:03 R・田中一郎

>「これまでの習慣だから、なんとなく読みやすいから」という理由で書くのは大間違いだという事にガッテンしていただけましたでしょうか?(ぇ)

ガッテン!

読んでいて激しく同意しました。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 10:39 菊池

>数値型が全部、多倍長実数なら解決する問題ですね。

なんだかね、そんな処理系遅くて使い物にならんことぐらい予想付かんかい?

何かの目的を追いかければ別の何かのトレードオフが発生する、可読性だけを追うと汎用性が損なわれるって話でこんな返しするとは正直信じがたいわ

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 11:28 ひろえむ

> 目的が「可読性」で、根拠が「書いてないじゃん」で用法として「型を明示するべきだ」とは、最初の目的が違うので違う結果になりました。
> それだけです

なるほど。 おっしゃっていたことがやっと理解できました。
納得です。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 11:49 シャノン

> そんな処理系遅くて使い物にならんことぐらい予想付かんかい?

もちろん、現実的でないことは承知していますよ。
まぁ、数値型が多すぎるとは思っていますけど。

また余談ですが、俺はパフォーマンスの話が好きではありません。
varを使う限り、可読性がある程度落ちるという代償は払い続けなければなりません。
一方で、もし整数型が万能な一つしかなければという場合、パフォーマンスを代償にして可読性とか可搬性を得るわけですが、パフォーマンスは可読性や可搬性を犠牲にしなくても、処理系次第で向上し得るからです。
だからといって実装時にパフォーマンスを無視していいというわけではもちろんありませんが、本質的な話をするときには無視すべきだと思っています。

まぁそれはいいや。

言いたいのは、「それは本当に型推論で解決すべき・型推論でないと解決できない問題なのか?」ということです。
将来の話も含めて、俺はこれを型推論で解決すべき問題ではないと思っているわけです。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 12:14 凪瀬

トラックバックがうまく飛ばないようなので直接リンク。
http://blogs.wankuma.com/nagise/archive/2007/12/26/114706.aspx

「汎用的で可搬性の高いコードを書くためには型の決定はできうる限り遅らせるべき」
には賛成なんだけども、抽象型で十分ではないかというのが私の意見。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 12:44 菊池

>varを使う限り、可読性がある程度落ちるという代償は払い続けなければなりません。

 可読性も含めて各種メトリック取り揃えて何かの目的の為にトレードオフ対象としていつでも差し出す覚悟をしている自分にとっては造作も無く差し出すトレードオフです。

>一方で、もし整数型が万能な一つしかなければという場合、パフォーマンスを代償にして可読性とか可搬性を得るわけですが、パフォーマンスは可読性や可搬性を犠牲にしなくても、処理系次第で向上し得るからです。

 えとですね、プロファイラをかけながらmsec/nsec単位をチューニングしてると可読性なんて…なんて事はよくあります。
 汎用性にしてもしかりです。

 ある目的があってその目的のトレードオフとして何かを切り捨てるって事を決断するのを日々やってる身から言えば、可読性も汎用性も可搬性もパフォーマンスもひっくるめて何かを求めればトレードオフ対象として切り捨てる対象になるものでしかありません。

#全部を求めるなんてそんな贅沢なことを言ってられるのはコードの書き始めの序盤ぐらいかな

 そんな処理系が出るまで待つというトレードオフを貴方がするのはご自由にですけど、自分にはそれができません。

 ある特定のメトリックだけは絶対に損なうべきじゃないという主張は宗教と一緒で、自分としてはおかしな話だと思うわけです。


>言いたいのは、「それは本当に型推論で解決すべき・型推論でないと解決できない問題なのか?」ということです。
>将来の話も含めて、俺はこれを型推論で解決すべき問題ではないと思っているわけです。

 自分は.NET Framework 2.0のジェネリックが数値演算に対して全く汎用プログラミングの手段として使えなかった事に比べれば十分進歩してるしその現状に満足して「汎用プログラミングの手段として」使えるのであれば使うというスタンスに居ます。

 型なんか意識せずに適当にvarって書けばいいじゃんとも思ってないし、型よりももっとメタな層での概念「それは真理なのか仕様なのか」の判断に悩みながら var を使うか具体形を使うかを決めます。

 そこで出てくるものが真理だったとしてもIEnumerable< KeyValuePair< string, ICollection< int,Nullable<datetime> > > > hoge = new Hashtable<string, ICollection< int,Nullable<datetime> >()
 とか長すぎて涙が出ちゃうときにはあきらめてvarってしちゃうトレードオフもきっとやらかします。

 そして将来にもっといいシンタックスが出てくればきっとさっくり乗り換えます。

このスタンスが悪魔に身をうった邪教の教徒に見えても、これが私のスタンスです。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 13:39 ghost_shell

ぼくの記事もリンクさせてください
http://blogs.wankuma.com/gshell/archive/2007/12/26/about_var.aspx

Rさんの意見より同意しているけど、少々行き過ぎているようにも思います。(導入されたvarキーワードを使わない手はないと思うのがきっかけなら、その点は同じです。)

varによって型は省略できても、オブジェクトに対して処理を書いていく中で、型は縛られませんか?

少なくともこれ(仕様に束縛されてしまう)が現在のC#(C#に限った話をしているのではないと思いますが)の限界だと思います。

理想としては菊池さんの話に共感するところがありますが、メリットが少ない気がします。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 13:53 菊池

ほいほい、みんないらっしゃい。

まぁ、自分の書いたものは一例に過ぎないよ。

「ホゲを目的として、パゲって使い方をすると、フガフガのメリットがある」
「アババを目的とすると、こんな使い方になるけど、ぜんぜんメリットないねー」

って辺りを、何を目的にしてるのか、どんなコードになるのかをメリットデメリット出してくださし。

class Hoge
{
override string ToString()
{
//実はここに重い処理
}
}

var x = new Hoge()
for( int i=0;i<1000000;i++ )
{
string str = string.Format( "{0}",x );
}

var じゃ無くてstringでもっとけば幸せだったのにねぇ…

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 14:21 ghost_shell

>var x = new Hoge()
>for( int i=0;i<1000000;i++ )
>{
> string str = string.Format( "{0}",x );
>}

>var じゃ無くてstringでもっとけば幸せだったのにねぇ…

リファクタリングした方がよい。
#じゃなくて?

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 15:26 nsharp

メソッドが

 1. 何をするものなのか(HowじゃなくてWhat)
 2. 入口の内容は何か(型など)
 3. 出口の内容は何か(型など)

これらがはっきりしてる限りは、メソッドの中身なんて本当に枝葉の問題でしかありません。
(いわゆる企業システムの世界では。)

で、varの使う/使わないがこれら3つに影響するものは「皆無」です。
なので、varの是非がなんでこんな熱い論争になるのか、さっぱり理解できません・・・。(´・ω・`)

漏れ的には、自分の頭がコンパイラの推論能力より劣ってることは認めてますから、w
完全に推論に身をまかせるスタンスです。

この変数の型なんだっけ?ってときは、VS上でマウスを当てればいいだけの話ですし・・・。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 15:36 シャノン

> この変数の型なんだっけ?ってときは、VS上でマウスを当てればいいだけの話ですし・・・。

ほ、ほ。
「プログラミングってのはメモ帳でソースコード書いてコマンドラインでコンパイルするものなんだよ!」派には怒られそうです。

#  re: 仕様は変わっても真理は変わらない 2007年12月27日 21:04 菊池

>で、varの使う/使わないがこれら3つに影響するものは「皆無」です。
>なので、varの是非がなんでこんな熱い論争になるのか、さっぱり理解できません・・・。(´・ω・`)

 アンチテーゼ投げ逃げのつもりで最初は記事書いたけど、逃げるの忘れたって言っておくー


> 2. 入口の内容は何か(型など)

これは入り口としてパラメータ以外もあるって事を考えるとvarを遠くに依存させることもできるのですよって事で。
 パラメータで貰ったオブジェクトから数段手繰った何かにvarを依存させてあげること(1段手繰ればそれはフィールドorプロパティの型ね)ができますから。
#パラメータを経由しなくてもstaticであれば依存対象にできます…なんてのは釈迦に説法よね。

>漏れ的には、自分の頭がコンパイラの推論能力より劣ってることは認めてますから、w
>完全に推論に身をまかせるスタンスです。

 うん!、9割7分同意!
 のこり3分は意識して確信をもってvarを書く自分の懐に収めさせていただきます。

#  re: 仕様は変わっても真理は変わらない 2007年12月28日 12:05 菊池

:TIMESTAMP: ここで追記をあげました :TIMESTAMP:
タイトル
名前
URL
コメント