例外が起きるような状況は確かに減らせる。でも減らしていくと、あるところを境に急激にコーディング量が増える。 head :: [a] -> Maybe a, floor :: a -> Maybe b はなんとか譲るとしても、div :: a -> a -> Maybe a なんてとてもやってられない。 パフォーマンスにも致命的な影響が出る。だから標準ライブラリは div :: a -> a -> a とか head :: [a] -> a で妥協してあるわけだ。 実際には絶対に空のリストではないと仮定できる場合もあるから、 head :: [a] -> Maybe
『例外』を発生しないように Maybe や Either を徹底的に使うとしても、バグがなくなるわけじゃないよ。 bottom やメモリ不足で落ちることもあるし、undefined だってあらゆる値に紛れ込む可能性がある。 だいたい、head や undefined が存在したり div :: a -> a -> Maybe a じゃないことから見ても、Haskell はそういう設計思想を持つわけじゃない。 失敗しうるからといって div :: a -> a -> Maybe a にしたりすると、パフォーマンスにも致命的な影響が出る。 そして『例外』を徹底的に防ごう
確かに NonEmpty なリストを使えば head でコケるような心配はなくなる。俺もどんどん使ったほうがいいと思う。 だけど、undefined だってあるし、bottom ( x = x とかね ) があるから、どんなに型システムで頑張っても絶対に落ちないということはない。 そもそも、クラッシュしないほうのバグは型システムではどうにもならない。
バグを抑えやすい、かー (スコア:2)
Haskellを触ってみた感じ、デバッグが難しそうなんだけど、バグ自体が少ないから構わないって言うのかな?
Re:バグを抑えやすい、かー (スコア:4, 参考になる)
バグ自体が少ないと言っているのは,
・プロジェクト数が少ない
・実行時バグの余地が少ない
と2通りあります.
前者は仕方のないことで,
別に批判されるべきことじゃありませんよね.
後者については型システム(およびコンパイラ)が,
性質を静的に保障してくれることが多く,
これは開発のうえで非常に強力です.
デバッグの難しさについては,
ちょっと真面目にすごいH [amazon.co.jp]で勉強した私はそうは思いません.
まず, テストのしやすさは手続型言語よりも容易です.
また, 従来再現しにくいバグの発見に至っては, そもそも入力が同じであれば必ず再現できます.
「触ってみた感じ」ということですから, あまり体系的に勉強していないんじゃないでしょうか?
もし単に謙遜しているだけで,
実はベテランということだったら本当にごめんなさい.でも, その場合はデバッグがどう難しいかをコメントしてくれれば,
多くの人にとって, とてもinformativeなコメントになりますよ
Re:バグを抑えやすい、かー (スコア:2)
変数が無く、動かしながら確認するっていうのが難しいから、ひたすらソースコードを見てバグを突き止めざるを得ないと思った。
参照透明性があるからテストは容易なのだろうけど、printf的な出し方が面倒。
Re:バグを抑えやすい、かー (スコア:1)
Debug.Traceモジュールとか便利ですよ。
http://www.haskell.org/ghc/docs/latest/html/libraries/base-4.6.0.1/Deb... [haskell.org]
Re: (スコア:0)
すごいH本にデバッギングの解説ってあったっけ?あんまり精読してないので、見落としたかも。
今ちょっと日本語版が手元にないので英語版 [learnyouahaskell.com](無料で全部読めるよ!)の目次見たけど、
それっぽいのが見当たらない
Re:バグを抑えやすい、かー (スコア:2)
デバッギングの解説はないですが,
実行中の挙動について表示させる例はありましたよね.
そう, 12章の"Walk in the line"の例です.
失敗するかもしれない関数landLeft,landRightの仕様をMaybeという文脈つけて変更していました.
でも実はこれだけじゃなくて,
純粋な関数であったとしても, トレースを使えます.
山本先生のブログ記事 [hatena.ne.jp]
究極的には, プログラムがstuckするのは,
パターンマッチに転けた場合が多いです.
そのときパターンマッチした際の値を得ることで,
原因は大体わかりますよね
Re:バグを抑えやすい、かー (スコア:5, 参考になる)
自分の書いたコードのパターンマッチングで落っこちるとちゃんと行番号が出てくるし、オプションつければ漏れがあっても警告出るから問題ないね。
困るのはゼロ除算や head [] で落ちたりとか、クラッシュせずに誤った結果を返してくるときとかかな。
head [] なんて、エラーメッセージは "Prelude.head: empty list" だけだからね。どうやって原因を突き止めろと。
trace を埋め込む場所がわかるなら、バグは半分見つけたようなもので……。
自分はその山本さんの記事も読んだことあるんだけど、
あと、例外がどこから来たのか知りたい場合は、GHC のデバッガを使うといいでしょう。
という一文にがっかりした覚えが。その GHCi のデバッガが、ほとんど役に立たないんだよ……。
だいたい GHCi (Haskell 処理系のデファクトスタンダードだね)のデバッガのマニュアルがこんな調子なんだよ。
プログラムをデバッグしている時にしばしば問われる問いの一つに、「どうやってここに来たんだ?」というものがある。伝統的な命令的デバッガは通常何らかのスタックトレースの機能を持っていて、それを使ってアクティブな関数呼び出しのスタック(字句的呼び出しスタックと呼ばれることもある)を確認することができる。このスタックによって、現在の地点に至るまでのコード上の道のりが分かる。残念なことに、これをHaskellで用意するのは難しい。正格な言語と違って、実行は深さ優先ではなく必要に応じて進むからである。
もう一つデバッグ中によく発生する疑問として、「この例外はどこから来たんだ?」というものがある。errorやhead []によって発生する例外には文脈情報が付属していない。プログラム中のどのheadの呼び出しがエラーの原因になったかを調べるのは骨の折れる作業であり、通常Debug.Trace.traceを使ったり、プロファイル付きでコンパイルして+RTS -xc(5.3. 時間及び確保量のプロファイルを取るを見よ)を使ったりすることになる。
GHCiデバッガの提供する方法を使うと、このようなエラーを素早く、かつコードを再コンパイルすることなしに突き止めることが、うまくすればできるかもしれない。
http://www.kotha.net/ghcguide_ja/7.0.4/ghci-debugger.html [kotha.net]
うまくすれば (hopefully) っておい……。Haskell のデバッグの難しさを知りたければ、このマニュアル読むのが参考になると思う。
Re: (スコア:0)
場当たり的なコメントで申し訳ないですが,
head []はパターンマッチで書く方が筋が良い.
(listToMaybe的なものをつかっても一緒)
ゼロ除算はcatchするよりも,
Integerを使うか最初から除算に気をつければいい.
失敗するかもしれない演算なんだから.
手続き型言語の例外は最初から想定しているもので,
それぞれcatchすればいいよね, という風潮だけど,
関数型言語の例外は, 発生しないように書くという前提.
究極的には関数型言語を好きになるかどうかって,
例外が発生しうる範囲が非常に限定されているんだから, 最初から避けて設計するよね,
というのを受け入れるか否かの違いだと思う.
Re:バグを抑えやすい、かー (スコア:1)
んで、その「究極的には」の内容を実践できる人だけが、「いいねーいいねー」って言いながら使っているから、バグが少ないって「思われている」だけなんじゃなかろか?
VBやPHPなんかで、バグがそりゃもうひどいことになるのは、言語設計がどうのと言うよりも、技術屋のレベルの問題に落とし込める気がしてなぁ。
Re: (スコア:0)
最初から除算に気をつければいい.
「気を付ける」っていうのは具体的にどうすればいいの?
例外が起きるような状況は確かに減らせる。でも減らしていくと、あるところを境に急激にコーディング量が増える。
head :: [a] -> Maybe a, floor :: a -> Maybe b はなんとか譲るとしても、div :: a -> a -> Maybe a なんてとてもやってられない。
パフォーマンスにも致命的な影響が出る。だから標準ライブラリは div :: a -> a -> a とか head :: [a] -> a で妥協してあるわけだ。
実際には絶対に空のリストではないと仮定できる場合もあるから、 head :: [a] -> Maybe
Re: (スコア:0)
最初から除算に気をつければいい.
『気を付ければいい』って具体的に何をすればいいの?
『例外』を発生しないように Maybe や Either を徹底的に使うとしても、バグがなくなるわけじゃないよ。
bottom やメモリ不足で落ちることもあるし、undefined だってあらゆる値に紛れ込む可能性がある。
だいたい、head や undefined が存在したり div :: a -> a -> Maybe a じゃないことから見ても、Haskell はそういう設計思想を持つわけじゃない。
失敗しうるからといって div :: a -> a -> Maybe a にしたりすると、パフォーマンスにも致命的な影響が出る。
そして『例外』を徹底的に防ごう
Re:バグを抑えやすい、かー (スコア:1)
つまりだ、Haskellで開発するから技術者集まれ!ってやると優秀なのだけ集まってくるって訳だな。
素晴らしい。
# 実際身の回りの関数型言語を好む人って優秀な言語フリークが多いイメージ。
# もしくは、そういう優秀なフリークに英才教育されてる感じ。
# ちゃんと生き残ってついて行ければ優秀に育つだろうなぁ。
# 数学的な訓練を受けていないとあまり直感的ではないので
# そもそも優秀なフリークが身近に居ないとはまりにくいと言うのと、
# コミュニティが狭いので優秀なフリークが直接手ほどき出来る程度の規模で
# 布教に熱心になりやすいというのはあると思う。
Re: (スコア:0)
> つまりだ、Haskellで開発するから技術者集まれ!ってやると優秀なのだけ集まってくるって訳だな。
そしてPHPを書かされるというHaskellのパラドックス
Re: (スコア:0)
あっ コメント消えたかと思ってたら、投稿できててかぶっちゃった。↑は気にしないでね
Re: (スコア:0)
転ける可能性がある計算はMaybe使えばいいのは譲れるわけですよね?
貴方が言っているのは, コケないことが自明な場合には,
それを書くことは無駄な手間とオーバーヘッドになるんじゃないか, でしょうか?
# だからそういう意味ではdivであろうとheadであろうと問題は一緒で,
# コケないはずのものに手間をかけたくない, と
だったら型システムで保障してやりましょう.
絶対コケないはずなんでしょ? だったらできます.
例えば, Data.List.NonEmptyモジュールを使えば,
データを作る側でNonEmptyであることを保障しますよ
Emptyである可能性があればコンパイルでコケます.
つまり, 何かの関数がリストを返すとき,
それが空になるパスがある限り,
NonEmptyなリストを返す関数としてコンパイルできません.
Re: (スコア:0)
確かに NonEmpty なリストを使えば head でコケるような心配はなくなる。俺もどんどん使ったほうがいいと思う。
だけど、undefined だってあるし、bottom ( x = x とかね ) があるから、どんなに型システムで頑張っても絶対に落ちないということはない。
そもそも、クラッシュしないほうのバグは型システムではどうにもならない。
それに、NonEmpty のような特殊化された型を検討するのはいいんだけど、その方向で安全性を保証しようとするときりがない。
リストのインデックスのために自然数型を定義すべきだろうか?除算を安全にするために『ゼロ以外の数』の型を定義すべ
Re: (スコア:0)
型システムに関しては手続き型言語でも同じことが言えると思うけど。
個人的には、関数型言語のいいところは、行き当たりばったりで書いても、参照透過性のおかげでコードが破綻しない事であり、事前に厳密な設計を必要としないところだと思っているので、Haskellみたいに静的にがちがちなのには魅力を感じない。