関数型プログラミング言語Haskell

このエントリーをはてなブックマークに追加
248240
>>241-242
どうもありがとうございます。
左辺と右辺のSMは別物だと捕らえたらなんとか理解できました。

しかし、SMの定義を見てると何か釈然としないものを感じます。
例えば、状態を読み取る時は

runSM s0 readSM

となるんだろうけど、これは(s0,s0)を返すにすぎませんよね?
これでは何の意味があるのだか良く分かりません。
状態を読み取ると言っておきながら、自分で状態を指定してるではない
ですか?また、状態を更新する時なのですが、

runSM s0 (updateSM f)

これは要するに、”前の状態と変化の仕方を引数として、後の状態を返す”
というのと同じことで、普通にそういう関数を作っても全く副作用は
無いと思いますが、なぜモナドにする必要があったのでしょうか?
あと、モナドを返す文と新たな文をくっつけて、またモナドを返すように
するというように、”>>や>>=やreturnを使って、モナドを返す部分を
どんどん伝染させていってる”ように感じるのですが、これは何のために
やってるんでしょうか?

あとは、getCharでちょっとした疑問があるのですが、getCharは場合によって
IO 'a'を返したり IO 'b'を返したりするはずです。これは参照透明性を失って
いるようにみえるけど、なぜ大丈夫なんでしょうか?
249デフォルトの名無しさん:01/09/05 02:57 ID:MClHLUZo
HaskellでOOPするにはどうやるんだ?
Haskellにはオブジェクトの内部状態という概念が無いから困るだろう。
モナドを使うのか?
250デフォルトの名無しさん:01/09/05 12:44 ID:WemS0OIU
>>245
>どんなプログラムを作るのに向いてるの?

たぶん、抽象度の高いものに向いてるんだろう。
記号処理とか。
OOPとかは、シュミレーションなどの実際のデータ
変化を伴うものは書きやすいが、記号処理なんか
はOOPで書くの大変だろう。
251デフォルトの名無しさん:01/09/05 15:32 ID:Bii0lg4I
でも、これだけオブジェクト指向ありきの世の中になってまで、
オブジェクト指向が出来ないってのは相当な痛手だと思うけど。
252デフォルトの名無しさん:01/09/05 16:12 ID:6PT9EWiQ
大学の「情報工学」と名の付く学科を出ていながら、関数型言語やプログラムの基礎理論
のことをなに一つ知らない連中もちょっと問題ありだけどね。
253日曜Haskellerオヤジ:01/09/05 16:17 ID:o26RlTLc
>>251
Haskell は、OOPで実装しても意味がないんじゃよ、時間軸が言語から分離消失してるからね。
考えること事態がナンセンスや、それでも OOP とは違うが似た概念はあるで。
class とか instance とか、実装されとる。
だが、実体はOOPとは全くの別物やね。

まあ、詳しいことは一度やってみるとええ。
プロセス型言語とは決定的に違うHaskellの世界で常識をいっぺん引っくり返してみ。
もっともわし、始めて2ヶ月やからトンデモないことぬかしとるかも知れんが・・・(笑)
254デフォルトの名無しさん:01/09/05 16:42 ID:6AdS52o2
>>248
「同じ文脈(用語は不安ですが)では、同じ変数は同じ値を表す。」
というのが参照透明性ですが、
getChar は同じ文脈では1度しか出現できないので、参照透明性
が保たれていると言えます。
255石敢當:01/09/05 16:55 ID:OAv7PsgU
>>236
>今日はこれから http://www.teu.ac.jp/kougi/koshida/Prog6/text13.html やります。
私も考えてみました。問題2だけ書きます。

2, 3, 5 で順に割ってみて2, 3, 5以外の素因子がないものだけを
拾い出せばokですが、これだととっても時間がかかります。
[2^a * 3^b * 5^c | a <- [0..], b <- [0..], c <- [0..] ]
と書ければ簡単ですが、これだとaとbが永遠に0のままですので細工を
しないといけません。まず、3つ組を順に生成する関数を定義します。

type Triplet = (Int, Int, Int)

nextTriplet :: Triplet -> Triplet
nextTriplet (0, 0, c) = (c + 1, 0, 0)
nextTriplet (a, 0, c) = (a - 1, c + 1, 0)
nextTriplet (a, b, c) = (a, b - 1, c + 1)

triplets :: [Triplet]
triplets = (0,0,0) : [ nextTriplet t | t <- triplets ]

こうすると非負整数から成る全ての3つ組の列 triplets が得られ
ますので、これを用いて

humming2 :: [Integer]
humming2 = [ 2^a * 3^b * 5^c | (a, b, c) <- triplets ]

と定義すれば、順に割っていく方式のものよりははるかに高速です。
(ただし、このリストは小さい順には並んでいないです)
256デフォルトの名無しさん:01/09/05 17:26 ID:4nU3LYhU
Haskell の拡張だけど一応
http://www.cs.chalmers.se/~nordland/ohaskell/
http://www.cs.chalmers.se/~rjmh/Software/h++.html

俺は Haskell で OOP なんてしたいと思わないけど。

>>254
俺は、getChar というか IO レベルでは
同じ式でも違う値を返し、参照透過性を保ってないと理解しているのだけど。

でも、IO a 型のデータ構築子は定義されてないので
a の値だけを取り出すことは出来ないため、
IO ってタグが付かない場所では IO を使うことが出来ないので
IO 以外のレベルでは参照透過性は保たれるので大丈夫。
って事だろうと理解している。

間違ってたら訂正きぼん > 識者
257デフォルトの名無しさん:01/09/05 17:31 ID:4nU3LYhU
追記。
IO a 型のデータ構築子云々ってのは
IO ってデータ構築子があるのなら
fromIO :: IO a -> a
fromIO (IO x) = x
って書けるって話。
258デフォルトの名無しさん:01/09/05 23:31 ID:.VljIIMQ
オブジェクト指向が出来ない言語なら実用性に疑問が残るね。
259司馬乱:01/09/06 01:34 ID:FsBmKxpM
OOPとは何ができることか考え直してみればHaskellでもかなりのことはできます.
大まかに言えばOOPの静的な側面,例えば継承,多相型,抽象型などは
type classやmoduleなどで大体可能です.以下にも例があげられています.
http://www.sampou.org/haskell/tutorial-j/classes.html
http://www.sampou.org/haskell/tutorial-j/modules.html
難しいのは動的な側面,例えば並列的なメッセージパッシングに伴う
非決定性などですが,開発手法としてのOOPは主に前者に関わるので
かなりのことができるというのが相応しい評価だと思います.
260司馬乱:01/09/06 01:35 ID:FsBmKxpM
モナドについては,図がかけないとちょっと説明が面倒ですが,,,.
ちょっとだけ言葉で説明してみます.

248さんが言うようにどんどん伝染していっているという感覚は正しいです.
前にも書きましたがモナド関数はA->M(B)という形を基本にして考えて,
通常A->B,B->C,C->Dという関数を合成していく代わりに
A->M(B),B->M(C),C->M(D)というモナド関数をつなげていきます.
この操作が>>=の果たしている役目です.
これが通常の合成ではないところがポイントであり,
IOなどのモナド計算はこのつないでゆく所で起こると考えることができます.

さらにもう一つ必要になるのが通常の関数A->Bを
モナド関数A->M(B)に持ち上げる操作ですが,
これはB->M(B)のある関数と普通に合成してやればできます.
これがreturnの果たす役目です.
計算のモナドで本質的に重要な操作はこの二つです.

main,getChar,getLineなどはこのような形をしていないように見えますが
これは一般に()->AとAは理論的には同じと見なせるのでこのようにしています.
(これらが関数であると上記のチュートリアルにも書いてあるのはこのためです).
これは例えば,ある集合Sと,一点からなる集合{*}からSへの関数全体の集合とは,
各関数において*がSの一つの要素を決定するので
実質的に同じ集合になるのと同じようなことだと考えてください.
261司馬乱:01/09/06 01:36 ID:FsBmKxpM
もうひとつ.例えばgetCharの返す値はIO Char型であり,
非決定的な動作はまだ起こっていません.この段階では参照透過性は保たれています.
このIOの結果を直接参照できるならば参照透過性が失われます.例えば
unsafePerformIO :: IO a -> a
のような結果を取り出す関数があると参照透過性が失われます.
do(つまり>>=)で取り出せているように見えるのに
参照透過性が失われないのはその参照がdoの中だけだから,
言い換えれば>>=の第二引数である関数における仮引数の参照に過ぎないからです.
262司馬乱:01/09/06 02:49 ID:3rpwMJKc
ついでに
>>257
データ構築子と関数は別物です.
データ構築子はタグみたいなものであってデータの一部です.
実行時に評価されるわけではありません.
(言いたい事はわかりますが).
263司馬乱:01/09/06 02:56 ID:AQT9jsyM
>>262
ごめんなさい,寝ぼけてました.関数はfromIOのほうでしたね.
もう落ちた方がよさそうです.