ブログトップ 記事一覧 ログイン 無料ブログ開設

サンプルコードによるPerl入門

2013-02-27

変数に型がないということの利点について考える

 PHPPerlRubyPythonなどのスクリプト言語に対して、変数に型がないということを否定的にとらえる人もいるかと思います。特にC言語Javaなどの静的言語を使ってきた人にとっては、型がないということが不安材料として目に映ることが多いのではないかと思います。

 けれども、型がないということは、本当に素晴らしいことです。型がないことによって、たくさんの面倒から解放されるからです。

どのような型の値でも代入できる

 まず基本的なこととして変数に型がなければどのような型の値でも代入できるということです。つまり、受け取るときに、どのような型の値を受け取るのかを意識する必要がありません。

my $str = 'Hello';
my $num = 1;
my $nums = [1, 2, 3];
my $person = {age => 2, name => 'taro'};
my $ua = LWP::UserAgent->new;

 文字列であろうと、数値であろうと、配列であろうと、連想配列であろうと、オブジェクトであろうと、それを意識する必要がありません。

記述量がとても短くなる

 変数の型がないことによって記述量がとても短くなります。

my $ua = LWP::UserAgent->new;

 もし変数に型があれば、次のようになるでしょう(Javaの例)。

LWP::UserAgent ua = new LWP::UserAgent();

 型推論で解決できるという意見があるかもしれませんが、型推論は不完全だと思います。まず右辺で型が明示されていない場合に使えないので、右辺に型がない場合は、結局型を書かないといけないからです。また型推論はソースコードコンパイルの時間を遅くしてしまいます。ソースコードが大きくなってきた場合に、すばやく書いて、すばやく実行結果をもらうことができなくなります。また統合開発環境での、メソッドの自動補完の機能の実装が少し難しくなります。

変数に型がないと変更に強い

 変数に型がないとソースコードの変更に強くなります。たとえば右辺の返す型に変更があったとしても、受け取る側のソースコードを変更する必要はありません。

# clinetはClientA型でもClientB型でもよい
my $ua = $c->client;

関数オーバーロードが不要になる

 変数の型を持つ言語は、型が異なるのだが、処理としては同一の処理を行いたい場合には、オーバーロードという機能を使う必要があります。変数の型がなければ、オーバーロードの機能は必要ではなく、ただ単にif文で分岐すればよいだけなのでとても楽です。

sub sum {
  my $value = shift;
  
  if ($valueがA型なら) {
  
  }
  elsif ($valueがB型なら) {
  
  }
}

 変数に型がないことによって、関数の重複を減らすことができるという大きなメリットがあります。

複数の型を受け取りたいときに、インターフェースを実装する必要がない

 Javaで大きなの労力といえば、インターフェースの仕組みを覚えて、実装することでしょう。複数の型を受け取りたい変数を作成したい場合は、まずインターフェースを実装することになります。

 けれども、変数に型がなければ、インターフェースという仕組みは不要です。変数に型がないことによって、クラスの実装が重複がなくとてもシンプルになります。

C++テンプレートのような機能も必要がない

 関数の引数が配列を受け取る場合を考えてみてください。そして、配列に含まれている変数の型が、定まっていない場合を考えます。また配列自体が、普通の配列なのか、動的配列なのか、特殊なリストなのかということがわからない場合についても考えてみてください。変数に型があると、このようなたくさんのことを個別に考えて、うまくインターフェースを実装したり、C++のテンプレートのような複雑でデバッグしにくい機能を使ったりしなければなりません。

 けれども、変数に型がなければ、そもそもこのような問題に直面することがありません。関数で受け取った後に、必要に応じて、if文で型を判定すればよいだけだからです。ですから、変数に型がないことによって、関数やメソッドの実装が重複なく簡単に書けます。

変数に型がないとどのような型の値が代入されているかわからないという批判に答える

 変数に型がないとどのような型の値が代入されているかわからないという批判があるかと思います。可読性の問題です。でも、僕は型のない言語の可読性が低いと感じたことはないです。それは、そもそも静的言語を読むときとは、違う読み方をしているからだと思います。

 スクリプト言語を読むときには、文脈を追って読むとどんな値が入っているかが、たいていはすぐにわかります。たとえば変数が次のような使われ方をしているとします。

$nums->[3];

 $numsは配列(へのリファレンス)です。

$ages->{age};

 $agesはハッシュ(へのリファレンス)です。

my $ua = LWP::UserAgent->new;

 $uaにはLWP::UserAgent型の値が入っています。

my $c = Client->new;
my $ua = $c->ua;

 $uaってなんだろう? Clientクラスのuaメソッドが何を返しているかを調べればわかるな。

 こんな風に読んでいってスクリプト言語を読むことに慣れてしまえば、何の問題もないと思います。

変数に型がないことのメリットは重複を少なくソースコードがかけること

 変数に型がないことのメリットは重複を少なくソースコードがかけることです。変数の型を意識しなくてもよいということは、それを利用するクラスや関数、メソッドも、利用するときに、型を意識する必要がないということです。これは重複の少ない保守性の高いプログラムを書くときにとても役に立ちます。

 スクリプト言語は保守性が低いといわれますが、そもそも根拠はないと思います。静的言語はインターフェースやクラスをそのたびに実装しなければならないので、修正や変更が行いづらいです。その点では、保守性は低いといえます。

 もう型のない言語は終わった、これからはハイブリッド型の言語の時代だという論評もときどき聞かれますが、その人は、型がないことのメリットを理解していないように思います。たぶんその人は、ソースコード上の見栄えという面しか理解していないんじゃないかと思います。

変数に型がないことのデメリットはないのか

 あげるとすれば、パフォーマンスです。値の型が事前にわかっていれば、数値演算は圧倒的に速くなります。それが整数であることがわかっていれば、それを前提に計算ができるので、数値計算のパフォーマンスは、静的言語が圧倒的によいです。

 でもパフォーマンスが問題にならない局面では、スクリプト言語を利用するのがよいと思います。

 もうひとつは、メソッドや関数名の補完の実装がうまくできないので、統合開発環境には頼れないということですね。スクリプト言語を書く場合は、まるっとたくさん暗記して、統合開発環境に頼らないのが、開発効率がよいと思います。

はてなブックマークコメントへの返信

型がないことのデメリット:型があれば静的にコンピューターが発見できるバグを発見できないので人間様がやる必要がある というのはあると思う

(nkgt_chkonkさん)

 これはコンパイル時に、コンパイルエラーがわかるという意味だと思います。でもスクリプト言語というのは、コンパイルと一緒に実行してしまいます。すると、バグがその時に見つかるので、バグの発見しやすさは変わらないと思います。

いくらなんでもポジティブすぎるのでは…!

(gfxさん)

 ポジティブすぎる箇所を教えてください。

工エエェェ(´д`)ェェエエ工

(choplinさん)

 工エエェェ(´д`)ェェエエ工となる箇所を教えてください。

一人で把握出来る規模のものを一人で作って保守していた頃は同じことを考えていた気がする

(Dolpenさん)

大規模になったとき動的言語の型がないデメリットを実感すると思う

(t2y-1979さん)

 大規模になってくると、保守できなくなるという発想は、そもそも間違いだと思います。CookpadGithubmixiはてな、faccbook、amazon、みんな動的な型を持つスクリプト言語を使って、大規模サイトを作っています。そう思うのは、思い込みや、プロジェクト管理の問題ではないかな。

型がない言語、書いた人間のマインドモデルに依存しすぎるので、よっぽど優れたマインドモデル持ってないと辛い

(mizchiさん)

 それは静的な型がソースコードの中に見えれば、マインドモデルに依存しないといっているように聞こえます。静的な型を持つ言語で書いても、ひどいソースコードはたくさんあります。

冗長に書くことでコンパイル時にそれらの間に矛盾がないか部分的ながらチェックできるのが静的型システムの一つの利点なんです

(nakag0711さん)

 なんでみんな、コンパイル時、コンパイル時って呪文のようにとなえるの? 一回プログラム実行したらわかるんじゃないのかなと思う。

実行時までエラーが判明しないというのは、アプリケーションの質を高める阻害要因になりかねないのですよ。

(mohnoさん)

 プログラムを一回実行したらいいだけじゃないの。というよりも、もし統合開発環境使っているのだったら、コンパイルと実行は、同時じゃないのかなと思う。

変態整数型仕様(変数に型があるくせに代入時エラーもなく暗黙に型変換しやがる)のせいで阿鼻叫喚が絶えない,あの酷い言語に聞かせてやりたい.

 どの言語のことかわからないです。

変更に強い/弱いの考え方がズレてないか?

 ずれてないです。たとえば、インターフェースを作成して、実クラスにメソッドをひとつ追加しようと思うと、インターフェースにも重複して変更を加えないといけないです。動的な型を持っていれば、そこを修正する必要がないので、変更に強いんです。

PythonとRubyは同列に並べるがPHPは入れないところに謎の序列を感じた / 型推論のある言語をマジメに使ったことがあるとは思えない / 大規模になると、LLでも型を明示してIDEの補完を活用した方が断然生産性上がるよ

 PHPを抜いたことには深い意図はないです。PHPはデプロイのしやすさ、Webに特化した関数がコアに初めから入っているので、利用しやすいという点ではWebプログラミングをしやすい言語だと思っています。型推論に関しては、僕の認識が甘かったです。LLでも型を明示するということの意味は、ちょっとわからないです。

爆釣ですなぁ。自分もJavaやってからRuby始めたころは、こんな感想だった。いいんじゃない、いろんな言語のよい所わかるのは。次はscalaあたりに手を付けてみると全然違う世界が広がると思う。

(kabisukeさん)

 釣ってないです。普段どおり、普通に書いたら、ブックマークいっぱいついちゃっただけです。 

追記

 以下は勘違いしていました。

型推論で解決できるという意見があるかもしれませんが、型推論は不完全だと思います。まず右辺で型が明示されていない場合に使えないので、右辺に型がない場合は、結局型を書かないといけないからです。

 型推論はもっと多くの場合うまく型を推論してくれるようです。型推論に対する利点の議論は取り下げます。

 でもそんなに的外れな議論はしていないと考えていますよ。


読み物へ

faith_and_bravefaith_and_brave 2013/02/27 17:36 > でもスクリプト言語というのは、コンパイルと一緒に実行してしまいます。すると、バグがその時に見つかるので、バグの発見しやすさは変わらないと思います。

いえ、コンパイルは通らないパスも検査しますが、動的型付け言語は、構文エラー以外は通ったパスしか検査しません。網羅的なパスを自動実行するコードを書かない限り、静的型付けと同じエラー検出は期待できません。

nkgt_chkonknkgt_chkonk 2013/02/27 17:48 https://gist.github.com/Shinpeim/5046338

上記のようなコードでは、 動的な言語の場合は $complex_condition が真のときにしかエラーになりません。が、静的な型をもつ言語ではコンパイル時にエラーになります。複雑な条件かつレアなケースでしか通らないようなところにあるバグは、「実行すればわかる」とは言えないだろうと思います。

あるいは、 Perl では Smart::Args などを利用すればそれなりに厳格な型チェックができますが、これも実行時チェックなので実際にその行が実行されなければエラーを見つけることはできません。

もちろん、適切にテストを書くことでこのデメリットはある程度軽減することができますが、このレベルのバグはコンパイラが見つけてくれればそれだけ人間は楽ができるというのが私の主張です。

わたしも Perl は好きですし動的な型付けはサクサク書けるし柔軟で良いなーと思いますが、それは安全性とのトレードオフであるという認識です。

choplinchoplin 2013/02/27 18:22 では気になったところから3つほど

前提として、perlについて書かれているようなので、型がない -> 動的型付け、として捉えています。

> まず右辺で型が明示されていない場合に使えないので、右辺に型がない場合は、結局型を書かないといけないからです。

静的型付け言語が長くなる理由としてこの理由を挙げられていますが、意味がよく分かりません。
もし型名と同じコンストラクタが型推論に必須という意味であれば間違っています。
scalaの例ですが、次の式ではsumはIntと推論されます。Intの+(Int)メソッドがIntを返すと定義されているからです。

val sum = 1 + 2

> 変数に型がないと変更に強い

変更に強いの捉え方によりますが、私は変更に強いとは変更してもプログラムが壊れないという意味だと考えています。
例として挙げられているコードですが、もし$uaがClientBにないメソッドを呼び出していると、そのメソッドを呼ぶパスが実行時に通るまでプログラムが壊れていることに気づきません。

my $ua = $c->client;

$ua->method_only_in_client_a # 変更後に実行されて初めてエラー

テストで意識的にパスを通すことでエラーを拾うしかありません。

静的型付け言語であればコンパイル時に存在しないメソッドの呼び出しでエラーになり、そもそも実行することができません。

> 関数のオーバーロードが不要になる

もしどうしてもオーバーロードしたくないのであれば、scalaであればAnyでうけてisInstanceOf[T]かパターンマッチで挙げられている例と同等のことが可能です。
意図しない型が入ってきた時に弱くなるので、強い理由がない限りやらないと思いますが。

scala> def f(a:Any) = if (a.isInstanceOf[String]) "String" else "other"
f: (a: Any)java.lang.String

scala> f("hoge")
res1: java.lang.String = String

scala> f(1)
res2: java.lang.String = other

全体的に静的型付けであることのメリットデメリットを捉えちがえているように感じます。特に保守性の部分について。

個人的にですが、動的型付け言語のメリットは、プログラム書き始めの試行錯誤のスピードが早い、初学者の学習曲線がなだらかであることだと思っています。

noronoro 2013/02/27 18:47 pythonの2.xで日本語を使ってると型宣言したくなります。書いてるうちにunicodeなのかstrなのかわからなくなってしまうので。

ほげほげ 2013/02/27 18:58 どのような型の値でも代入できる: これ自体は良いとも悪いとも言えない
記述量がとても短くなる: 「型推論」についての理解が不十分
変数に型がないと変更に強い: 「変更に強い」の問題点はchoplinの挙げている通り
オーバーロード・インターフェース・テンプレート: 比較対象の言語の選び方が恣意的
変数に型がないことのメリットは重複を少なくソースコードがかけること: 「保守性」とは?

全体的にズレてます。

ほげほげ 2013/02/27 18:59 「choplinさん」と書いたつもりが、「choplin」と呼び捨てにしてしまっていました。
すみません。

__ 2013/02/27 19:25 この記事で型推論が不完全というのの意味が明らかでないのが気になるところですが、例えば ML という言語に対する健全かつ完全な型推論アルゴリズムが存在するというのは良く知られている事実です。健全性に関する証明は、Milner の A Theory of Type Polymorphism in Programming という論文にあります。(完全性の証明も探しているのですが、良さそうなのがなかなかみつからないのでまた後で)
型推論アルゴリズムの健全性(soundness)とは、そのアルゴリズムにプログラム p を入力したときに型 t が付くと出力されれば、必ずプログラム p は型 t で型付けできるという性質です。
型推論アルゴリズムの完全性(completeness)とは、そのアルゴリズムに型を付けられるプログラムを入力すると、必ず型が付くという出力が得られるという性質です。
これらの性質を満たすとしても"不完全"でしょうか? また、複雑な体系であれば完全性の成り立つ型推論アルゴリズムが存在しないこともありますが(実際に使われているプログラミング言語の型推論器も完全でないことは多い)、代入の右辺に型が明示的に書いてないからというような理由で型を決定できないことは、あまり無いように思います。実用する上ではほとんど困りません。

matsulibmatsulib 2013/02/27 20:00 この記事はCやJavaなどの静的言語とPerlを比較してと言ってるから、関数型言語についてまで言及しているとは思えない。ここで言ってる型推論の不完全性・冗長性というのは、C#の型パラメータが省略できないということに限定されるのでは。なんとかという関数型言語ではできるという指摘はごもっとも。でもちょっと意地悪な気もする。

通りすがり通りすがり 2013/02/27 21:01 長くなったんで、匿名ダイアリーに書いときました。
http://anond.hatelabo.jp/20130227205911

perlcodesampleperlcodesample 2013/02/27 21:40 faith_and_braveさん

>いえ、コンパイルは通らないパスも検査しますが、動的型付け言語は、構文エラー以外は通ったパスしか検査しません。網羅的なパスを自動実行するコードを書かない限り、静的型付けと同じエラー検出は期待できません。

 どうして通らないパスを持つソースコードを書くのでしょうか? 機能として必要だとしたら、その部分にエラーがあれば発見できると思います。また、それほど品質を高めたいのであれば、自動試験を書けばよいと思います。

perlcodesampleperlcodesample 2013/02/27 21:46 nkgt_chkonkさん

>複雑な条件かつレアなケースでしか通らないようなところにあるバグは、「実行すればわかる」とは言えないだろうと思います。

 高品質を求めるのであれば、バグは自動試験で見つけたほうがよいと思います。静的型だから、バグが減ることを期待するのはどうかなと思います。

perlcodesampleperlcodesample 2013/02/27 22:05 _さん、choplinさん

 僕の型推論の理解が不十分でした。ほとんどの場合推論してくれるそうですね。勘違いしていました。これは取り下げです。

perlcodesampleperlcodesample 2013/02/27 22:15 choplinさん

>静的型付け言語であればコンパイル時に存在しないメソッドの呼び出しでエラーになり、そもそも実行することができません。

 実行時に関数名が間違っていたり、存在しなくなって、エラーになったとしても、そこでソースコードを修正すればよいと思います。

 ソースコードを書くときは、試験を行うのですから、バグが見つかるのがコンパイル時か実行時かということは、よい悪いの比較にはならないかと思います。

faith_and_bravefaith_and_brave 2013/02/27 22:16 > それほど品質を高めたいのであれば、自動試験を書けばよいと思います。

そう書いたつもりです。そして以下のコメントに戻ります:

> 型がないことのデメリット:型があれば静的にコンピューターが発見できるバグを発見できないので人間様がやる必要がある というのはあると思う

アプリケーションレイヤーの自動試験のうち、型に関するテストを人間が書かなくて済みます。間違ったコードは静的型によってライブラリ側で保護され、実行することができないので、自動試験をアプリケーション側で書く必要がなくなります。
コンパイル時にできる限りのエラーを検出するのと、とりあえず動くコードを書いてあとで自動試験で保護するのは、開発方針によるトレードオフであって、メリット/デメリットの話ではありません。

また、アプリケーションレイヤーの自動試験は、まだまだ「全てを自動試験可能である」とは言いにくい段階だと思います。一部のコードは、手動で検証する必要がどうしても出てきます。そして人間が試験をする以上、漏れは発生します。そういったところで「通らないパス」は生まれます。

perlcodesampleperlcodesample 2013/02/27 22:18 noroさん

>pythonの2.xで日本語を使ってると型宣言したくなります。書いてるうちにunicodeなのかstrなのかわからなくなってしまうので。

pythonの事情がよくわからないので、なんともいえないのですが、出力してみれば、すぐにわかるのではないかなぁと思います。

perlcodesampleperlcodesample 2013/02/27 22:21 choplinさん

>全体的に静的型付けであることのメリットデメリットを捉えちがえているように感じます。特に保守性の部分について。

 静的型を持つ言語は、保守性が高いというのは、ある種の迷信や神話だと僕は思っています。コード量や、重複が多くなると、保守性は下がると思います。

perlcodesampleperlcodesample 2013/02/27 22:27 ほげさん

>どのような型の値でも代入できる: これ自体は良いとも悪いとも言えない
 これはオーバーロードやテンプレートのような機能を使わないでも書けるという点で、大きなメリットがあると考えています。

>記述量がとても短くなる: 「型推論」についての理解が不十分

 これはそのとおりでした。追記しました。

>変数に型がないと変更に強い: 「変更に強い」の問題点はchoplinの挙げている通り

 たとえば、インターフェースを実装していて、実クラスにメソッドが増えたとすると、インターフェースも直さなくちゃいけないじゃないですか。変数に型がなければ、そもそも意識する必要がなくなるので、変更に強いというのは、本当のことです。

>オーバーロード・インターフェース・テンプレート: 比較対象の言語の選び方が恣意的

 オーバーロードは、C++やJavaを意識しています。インターフェースはJavaです。テンプレートはC++です。静的な型を持つ代表的な言語だと思います。恣意的ではないです。

>変数に型がないことのメリットは重複を少なくソースコードがかけること: 「保守性」とは?

 保守性が高いコードというのは、繰り返しが少ないソースコードのことをいっています。たとえば「Object obj = new Object」よりも「my $obj = Object->new」のほうが、繰り返しが少ないので、保守性が高いという認識です。

perlcodesampleperlcodesample 2013/02/27 22:30 matsulibさん

 型推論については、完全に僕の理解の足りないことが原因でした。言語の比較は「Perl, Ruby, Pytho」vs「C, C++, Java」ですね。

perlcodesampleperlcodesample 2013/02/27 22:41 faith_and_braveさん

>アプリケーションレイヤーの自動試験のうち、型に関するテストを人間が書かなくて済みます。

 型に関する試験は書かないでよいと思います。試験するのは、入力と出力で正しい結果が得らればよいと思います。

>間違ったコードは静的型によってライブラリ側で保護され、実行することができないので、自動試験をアプリケーション側で書く必要がなくなります。

 静的型を持っていればコンパイル時に、型間違いを防いでくれるというのは事実ですが、実際に弊害になる場合をうまくイメージできません。doubleのつもりがintだったとかでしょうか?

 単なる試験不足のように感じますし、潜んでいるバグの多くが型間違いの部分にあるとも思えないです。

oneshotlife_tomoneshotlife_tom 2013/02/27 22:48 myとか、$とか@とか%とか書くの面倒じゃないですか?!その辺もインタープリタが解釈してくれるとめっちゃ楽なんですがね。

perlcodesampleperlcodesample 2013/02/27 22:51 通りすがりさん

>今時のパソコンならコンパイル時間なんて大したことない。

CPU一個あたりの性能は上がりにくくなってきています。コンパイルの遅さが不満になっているというのは、現場の声として実際に存在します。

Scala 2.9.1 final 雑感
http://d.hatena.ne.jp/kmizushima/20110901/1314879295

>実行時間に影響がなく、開発者の待ち時間で済む方が実はよいのでは?

本番でパフォーマンス要求を満たせるならば、開発者の開発速度が上がったほうがよいです。

>実行するまで意図したインスタンスが返ってこなくなった事実に気づかないから。

 これはよく聞かれますが、迷信に近いと思っています。開発中に試験をするのだから、どんなインスタンスが返ってくるかを確認しないわけがないと思います。

>とあるインスタンスしか入ってないつもりのリストに実は全然ちがうものが混ざってた!

 間違ったインスタンスが入っていたら、試験したら、その時にすぐに判明します。そのための試験なんだから、それでいいと思います。

>インスタンスが何を持ってるのかわからない方が可読性に問題がある。

 クラスのドキュメントを読めば、どんなメソッドを持っていて、どんなオブジェクトを返すのかがわかると思います。

faith_and_bravefaith_and_brave 2013/02/27 22:54 > 単なる試験不足のように感じますし、潜んでいるバグの多くが型間違いの部分にあるとも思えないです。

ここから先はもう、コメント欄で一つひとつ型が大事なケースを挙げていくと話が終わりません。strong typedefや依存型など、型によってアプリケーションのエラーを未然に防げる例は数多くありますので、興味があるなら調べてみてください。

perlcodesampleperlcodesample 2013/02/27 22:55 oneshotlife_tomさん

>myとか、$とか@とか%とか書くの面倒じゃないですか?!その辺もインタープリタが解釈してくれるとめっちゃ楽なんですがね。

 歴史的にPerlはそうなっているので、そこは仕方がない部分ですね。タイピングの少なさでいうなら、Rubyのほうがちょっと少ないと思います。「-> が .」「my $num が num」「セミコロン -> なし」なので。

 でもmyがあると変数宣言がどこでされたのかがひと目でわかるし、$がついていると、変数であることがひとめでわかるし、関数名とぶつからないという利点もあります。

...... 2013/02/27 22:56 > 保守性が高いコードというのは、繰り返しが少ないソースコードのことをいっています。たとえば「Object obj = new Object」よりも「my $obj = Object->new」のほうが、繰り返しが少ないので、保守性が高いという認識です。
なんでこれが「保守性が高い」ということになるの?

choplinchoplin 2013/02/27 22:58 > ソースコードを書くときは、試験を行うのですから、バグが見つかるのがコンパイル時か実行時かということは、よい悪いの比較にはならないかと思います。

faith_and_braveさんの繰り返しになりますが、コンパイラが検査してくれることによって、型レベルでは
* 人の手でテストを書く必要がなくなる
* 網羅性が保証される(但しコンパイラが保証してくれる範囲で)
という点で優れています。

後者の網羅性が特に厄介で、カバレッジが100%でない限り本当に全部のパスをテストで通せたか保証することは難しいのではないでしょうか。

> 静的型を持つ言語は、保守性が高いというのは、ある種の迷信や神話だと僕は思っています。

保守性は言語や設計に大きく依存するので、一概に静的型言語だから保守性が高いとはもちろん言えません。
ただ、静的型、動的型という対立軸にのみ注目するのならば、上記の通り型のレベルでの安全性が高まるという点で保守性が高いというのは正しいと思います。
実行時の値レベルまで保証してくれるわけではないので、そこはテストが必要ですが。

> コード量や、重複が多くなると、保守性は下がると思います。

重複についてはその通りですね。ただ、静的型言語であるがゆえに重複が多くなるというのは偽だと思います。
コード量については短ければいいというものではないですが、型推論を始めとして冗長な記述を減らし短く書ける仕組みは色々と取り入れられているので、恐らく考えている程には長くならないと思いますよ。

DryadDryad 2013/02/27 23:02 ○万行規模のコードの”C0 100%”に駆り出されるという悲しみに満ちた経験から、一つ言えることがあります。それは、正常系のパスは何とかなっても、異常系のパスを潰すテストを一つ一つ書いていく作業が、いかに大変な労力を伴うかということです。
この問題に対しては、色々な手法が提唱されています。型を利用するのは、有力な手段のひとつです。うまく型を使うことで、ケアレスミスや異常系の検討漏れを予防し、コードの正しさを証明することができます。その恩恵は、コードの規模が大きくなればなるほど大きいはずです。
具体的な解説については、私より詳しい方が大勢いらっしゃると思うので、他の方のご意見も伺ってみてください。先ほど、ひとつ参考になりそうなリンクがあったので、それだけご紹介しておきます。 https://sites.google.com/site/faithandbrave/error_handling

anonymousanonymous 2013/02/27 23:03 変更に強いというのは全く逆の意見です。
HaskellやOCamlの場合ですが、関数の引数の数や型を変更すると、その型が一致しない箇所がコンパイラで全てピックアップされます。
あとはそこを直していけばいいわけなので、影響を受ける範囲を限定しやすくなります。

「テストを書けばそんなの分かる」と主張されるかもしれませんが、だったら型を書いたほうがラクというのが静的な型付きを好む人達の見解でしょう。

コメントにある、

>>静的型付け言語であればコンパイル時に存在しないメソッドの呼び出しでエラーになり、そもそも実行することができません。
> 実行時に関数名が間違っていたり、存在しなくなって、エラーになったとしても、そこでソースコードを修正すればよいと思います。
> ソースコードを書くときは、試験を行うのですから、バグが見つかるのがコンパイル時か実行時かということは、よい悪いの比較にはならないかと思います。

に関しても、「バグが見つかるのがコンパイル時か実行時か」という差は大きいと思います。
Rubyを書いていてよくであるのが、ある変数やメソッドの返り値が意図せずnilになっていて、そこから生やしたメソッドが見つからず例外が投げられるということです。
変数の値やメソッドの返り値は動的な型付き言語では実行の度に変わる可能性があるので、コンパイル時にこの種のバグを見つけることはできませんし、テストで全てのケースをチェックすることはできませんから本番でこれが起きるかもしれません。

u4u4 2013/02/27 23:15 変数に入ってるインスタンスの型がわからないならテストを書けばいいってのはちょっと乱暴な気がします。
テストを書くのも人間なのでミスはありえますし、カバレッジ100%を目指すとなるとすべてのテストが正しい事を保証するのは至難の業です。
テストの正当性を保証するテストを書けとでもいうのかと。
静的型付け言語であればそもそも型に関する間違いはコンパイラが教えてくれるのでテストを書く必要もありません。
テストを含めた記述量を考えれば静的型付け言語の方がコード量は少なくて済みます。

perlcodesampleperlcodesample 2013/02/27 23:18 ...さん

>なんでこれが「保守性が高い」ということになるの?

DRYという考え方です。

http://ja.wikipedia.org/wiki/Don't_repeat_yourself

「論理的に関連した要素は予測できる形で統一的に変更され、したがってそれらの変更は同期が取れたものとなる。」という意味で、保守性が上がります。

perlcodesampleperlcodesample 2013/02/27 23:21 Dryadさん

>型を利用するのは、有力な手段のひとつです。うまく型を使うことで、ケアレスミスや異常系の検討漏れを予防し、コードの正しさを証明することができます。

 これには根拠がないです。バグを含むのであれば、型間違い以外の部分にももちろんバグが含まれていると思います。なぜ型があれば、バグが少なくなるという発想をするのかが、僕にはちょっとわかりません。

...... 2013/02/27 23:22 OCamlは強力な型推論があるから型を明示する必要は無いし、
参照透過性により移植性は優れているし、
多相型によりオーバーロードに相当する機能も動的型付けと同等に楽にできる。

型クラスのあるHaskellはオーバーロードについてはもっと強い。
しかしこちらはその分型推論は若干弱い。

perlcodesampleperlcodesample 2013/02/27 23:26 anonymousさん

>「テストを書けばそんなの分かる」と主張されるかもしれませんが、だったら型を書いたほうがラクというのが静的な型付きを好む人達の見解でしょう。

 静的な型があれば、バグが減るという見解は信じられません。バグをつぶすのは試験を行う作業だと思います。

>Rubyを書いていてよくであるのが、ある変数やメソッドの返り値が意図せずnilになっていて、そこから生やしたメソッドが見つからず例外が投げられるということです。

 静的に型宣言していても、nullポインタのようなものは、含めることができるので、それを参照しようとすると例外が発生するのではないでしょうか。

 Rubyの場合は、メソッドが呼び出されるときに遅延するだけで、早いか遅いかだけの違いだと思います。

...... 2013/02/27 23:26 > これには根拠がないです。バグを含むのであれば、型間違い以外の部分にももちろんバグが含まれていると思います。なぜ型があれば、バグが少なくなるという発想をするのかが、僕にはちょっとわかりません。
「コンパイルが通れば(型さえ合っていれば)少なくとも実行は可能である」という考え方はあるでしょう。
本当なら依存型 (dependent type) が実装されたマトモな言語があればよいのですが、
そこまで言わなくても例えばOCamlについて言えばコンパイルが通ればあとは実行時に困るのはゼロ除算とスタックオーバーフローぐらいでしょう。

...... 2013/02/27 23:29 どうもこの筆者はテスト工程を過信しすぎている気がする。
コンパイル時に検査できる内容はコンパイラに任せた方が当然バグは減る。

perlcodesampleperlcodesample 2013/02/27 23:30 u4さん

>静的型付け言語であればそもそも型に関する間違いはコンパイラが教えてくれるのでテストを書く必要もありません。

 試験をするときは型の試験なんてしないです。入力と出力のチェックをするだけです。レアケースが、それほど重要な意味を持つなら、ちゃんと自動試験書くべきです。静的な型を持っているから動的な型を持つ言語よりも、安心だとは、僕は思わないです。

oneshotlife_tomoneshotlife_tom 2013/02/27 23:33 書いてあることは理解出来るのだが、はじめてのPythonに書いてある売り文句のようでした。メリットと書いてあるところの恩恵を受けやすいのは、PythonとかRubyのほうじゃないかと。Perlはもっと雑多としているイメージがあります。Perlの良いところは、CPANから持ってこれるところかな・・・。CPANから拝借すれば、自分でコードを書く量が少ないのと、いろいろな人が叩いてくれているため品質が担保される、などなどではないかと。乗りこなしにくい、野生のじゃじゃ馬みたいな感じ。

perlcodesampleperlcodesample 2013/02/27 23:33 ...さん

>どうもこの筆者はテスト工程を過信しすぎている気がする。コンパイル時に検査できる内容はコンパイラに任せた方が当然バグは減る。

 みなさんが、コンパイル時の型チェックを過信しすぎていると思いますよ。結局は、試験をしないと、バグはとれないです。

...... 2013/02/27 23:39 > みなさんが、コンパイル時の型チェックを過信しすぎていると思いますよ。結局は、試験をしないと、バグはとれないです。
誰が試験をしないと言った?
バグを減らす工夫は多いほうが良い、という当たり前の事実を述べているまでである。

DryadDryad 2013/02/27 23:41 >perlcodesampleさん
> なぜ型があれば、バグが少なくなるという発想をするのかが、僕にはちょっとわかりません。

残念ながらあまり広くは知られていませんが、関数型言語を始めとした言語に備わっている強力な型システムを利用すると、ある種のバグを防ぎやすくなるのです。今気づいたのですが、先ほど挙げさせて頂いたドキュメントはfaith_and_braveさんがお書きになったもののようですね。今すぐでなくとも結構ですので、一度、目を通してみることをお勧めします。

u4u4 2013/02/27 23:55 >試験をするときは型の試験なんてしないです。

関数のオーバーロードが不要になる の例で$valueの型で条件分岐してますよね。

静的型付け言語であれば引数の型によって呼び出される関数が静的に決まるので、その関数のテストの際に型の違いを気にしなくていいと思うのですが。


というか自動試験というものに幻想を持ちすぎてるような…
コンパイラに任せられるものは任せたほうがプログラマの負担が減るという話なのに

PocoPoco 2013/02/28 00:04 >>なんでこれが「保守性が高い」ということになるの?

>DRYという考え方です。

違うでしょ。
何か適切な修正例を挙げて
「Object obj = new Object」
より
「my $obj = Object->new」
の方が修正範囲が小さい事示さなきゃ、保守性が高いなんて言えないのでは?
#というかわずか1行のソースで保守性の高さを示すには、上を変更する修正例でかつ下を一切修正しなくて済むような修正例挙げないと。。。
#トークンの出現回数なんて、入力補完の前ではゴミですよね?

YY 2013/02/28 00:05 静的型付けは型チェックしてくれるけどYコンビネータみたいな型付けできない式を書くことができない。表現力を狭めてでも高い品質が必要な時に利用する。動的型付けは逆で、品質を多少下げてでも表現力が必要な時に利用する。

XX 2013/02/28 00:58 再帰型でYコンビネータも型付けできますよ

heisseswasserheisseswasser 2013/02/28 01:58 22:27の時点で回答されているこちらですが
>>変数に型がないと変更に強い: 「変更に強い」の問題点はchoplinの挙げている通り
>
> たとえば、インターフェースを実装していて、実クラスにメソッドが増えたとすると、インターフェースも直さなくちゃいけないじゃないですか。変数に型がなければ、そもそも意識する必要がなくなるので、変更に強いというのは、本当のことです。

そうでしょうか?
僕が思うに、これは単に変更しやすいだけです。逆に言えば、変更されてしまいやすいのです。

もちろん変更しやすいというのはスピーディな開発には役立ち、その点で見れば利点です。

しかし、変更されやすいという点で見れば、
* 自分で実装しているクラスで、メソッドから返すオブジェクトをObjectAからObjectBに内部的に変更したり、
* 使用しているライブラリのクラスで、バージョンアップに伴ってメソッド名が変わった
というようなインターフェースが変更があった場合に、
動的型付け言語では、実行時に、変更があったクラスを使用する側の、変更箇所に関係する部分に運良く実行が到達するまで、インターフェースの変更が検知できません。(*)

変更があった場合に、使う側でそれが分からないのであれば、それは変更に強いとは言えず、むしろ弱いのでは?

もちろん、実行時のテストが必要だというのは、動的型付け/静的型付け言語に関係なく共通ですが、ここで僕が言いたいことは、
動的型付けな言語は、単に変更が容易なだけで、変更に強いという観点はちょっと違うんじゃないかなと。

(*)もちろんテストによっても発見できますが、網羅的にテストしないと発見できない可能性があります。

anonymousanonymous 2013/02/28 05:09 > 静的な型があれば、バグが減るという見解は信じられません。バグをつぶすのは試験を行う作業だと思います。

静的な型はバグが減るとかバグをつぶすというレベルの話ではなく、コードの明らかなミスを無くすものです。
例えば、"foo" + 100みたいなコードはバグ以前にただのミスでしょう。
関数が意図しない型の引数で呼び出されてしまって例外が投げられることはよく経験していらっしゃると思います。
動的な型のチェックではこんなミスも防げなくなってしまいます。
こうしたミスが起きないことを試験で確かめるのはかなり困難でしょう。

> 静的に型宣言していても、nullポインタのようなものは、含めることができるので、それを参照しようとすると例外が発生するのではないでしょうか。
> Rubyの場合は、メソッドが呼び出されるときに遅延するだけで、早いか遅いかだけの違いだと思います。

C言語などのポインタではそうでしょうが、ポインタ以外の変数はもちろんNullポインタにはなりません。int xのxには値がありますし、関数の返り値としてポインタでない型が指定された場合もそうです。
また、HaskellやOCamlなどには望みの値が含まれないかもしれないことを表現できるMaybeやoption型があります。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証