関数型プログラミング言語はどのように機能しますか?


92

関数型プログラミング言語が状態を保存できない場合、ユーザーからの入力を読み取るなどの単純なことをどのように実行しますか?入力をどのように「保存」するか(または、そのためのデータを保存するか)。

たとえば、この単純なCの事柄は、Haskellのような関数型プログラミング言語にどのように変換されますか?

#include<stdio.h>
int main() {
    int no;
    scanf("%d",&no);
    return 0;
}

(私の質問は、この優れた投稿「名詞の王国での実行」に触発されました。これを読むと、オブジェクト指向プログラミングとは何か、Javaが1つの極端な方法でそれを実装する方法、および関数型プログラミング言語がどのように機能するかについて理解が深まりましたコントラスト。)



4
体系的なレベルでは、コンピューターは有用である状態を必要とするため、これは良い質問です。私は、Simon Peyton-Jones(Haskellの開発者の1人)とのインタビューを見て、完全にステートレスなソフトウェアを実行しただけのコンピューターでは、1つのことしか達成できないと言いました。以下の多くの良い答え。2つの主な戦略があります:1)不純な言葉を作る。2)状態を抽象化するための狡猾な計画を立てます。これはHaskellが行うことです。基本的に、古いものを変更するのではなく、少しだけ変更された新しい世界を作成します。
2010年

14
SPJは、状態ではなく副作用について話していませんでしたか?純粋な計算には、引数バインディングとコールスタックに暗黙的な多くの状態がありますが、副作用(I / Oなど)がないと、有用なことは何もできません。2つのポイントは本当にまったく異なります。純粋でステートフルなHaskellコードがたくさんあり、Stateモナドは非常にエレガントです。一方IO、醜くて汚いハックは、不本意ながらしか使われていません。
カリフォルニア州マッキャン、2010年

4
camccannはそれを正しく持っています。関数型言語にはたくさんの状態があります。命令型言語のように「遠くからの不気味なアクション」ではなく、明示的に管理されるだけです。
私の正しい意見だけ正しい

1
ここに混乱があるかもしれません。おそらく、コンピューターが効果を発揮するにはエフェクトが必要ですが、ここでの問題はコンピューターではなくプログラミング言語に関するものだと思います。
Conal

回答:


80

関数型プログラミング言語で状態を保存できない場合、ユーザーからの入力の読み取り(つまり、「保存」)や、データの保存など、単純なことをどのように行うのでしょうか。

収集したように、関数型プログラミングには状態がありませんが、データを格納できないという意味ではありません。違いは、私が(Haskell)ステートメントを

let x = func value 3.14 20 "random"
in ...

の値xは常に同じであることが保証されています...。何も変更できない可能性があります。同様に、関数f :: String -> Integer(文字列を取得して整数を返す関数)があるf場合、その引数を変更したり、グローバル変数を変更したり、ファイルにデータを書き込んだりしないことを確認できます。上記のコメントでsepp2kが述べたように、この非可変性はプログラムについて推論するのに非常に役立ちます。データを折りたたみ、スピンドル化し、切断する関数を記述し、新しいコピーを返すので、それらを一緒にチェーンすることができます。これらの関数呼び出しのうち、「有害」なことは何でも実行できます。あなたはそれxが常にxであることを知っています、そして誰かがx := foo bar宣言の間にどこかに書いたことを心配する必要はありませんx それは不可能だからです。

ユーザーからの入力を読みたい場合はどうなりますか?KennyTMが言ったように、不純な関数は引数として全世界に渡され、その結果と世界の両方を返す純粋な関数であるという考え方です。もちろん、実際にこれを実行する必要はありません。1つはひどく不格好です。もう1つは、同じワールドオブジェクトを再利用するとどうなるでしょうか。したがって、これはどういうわけか抽象化されます。HaskellはIOタイプでそれを処理します:

main :: IO ()
main = do str <- getLine
          let no = fst . head $ reads str :: Integer
          ...

これmainは、何も返さないIOアクションであることを示しています。このアクションを実行することが、Haskellプログラムを実行することの意味です。ルールは、IOタイプはIOアクションを決してエスケープできないということです。このコンテキストでは、を使用してそのアクションを紹介しdoます。したがって、2つの方法で考えることができるをgetLine返しIO Stringます。最初に、実行時に文字列を生成するアクションとして。第二に、不純に取得されたためにIOによって「汚染された」文字列として。最初の方がより正確ですが、2番目の方がより役立つ場合があります。<-とりStringの外IO Stringと中に格納しstr、我々はIOアクションにしているので、我々はそれが「エスケープ」することはできませんので、それは、バックアップをラップする必要があります-ブタを。次の行は、整数(reads)の読み取りを試み、最初に成功した一致(fst . head); これはすべて純粋(IOなし)なので、で名前を付けlet no = ...ます。我々は両方使用することができますnoし、str中に...。私たちは、このように(から不純なデータを保存したgetLinestr()、純粋なデータlet no = ...)。

IOを操作するためのこのメカニズムは非常に強力です。これにより、プログラムの純粋なアルゴリズム部分を、純粋でないユーザーインタラクション側から分離し、型レベルでこれを強制できます。あなたのminimumSpanningTree関数は、おそらくあなたのコードのどこかで何かを変更したり、ユーザーにメッセージを書き込み、およびようにすることはできません。安全です。

HaskellでIOを使用するために知っておく必要があるのはこれだけです。それでいい場合は、ここで停止できます。しかし、それが機能する理由を理解したい場合は、読み続けてください。(そして、これはHaskellに固有のものであることに注意してください—他の言語は異なる実装を選択するかもしれません。)

そのため、これはおそらくごまかしのようで、どういうわけか純粋なHaskellに不純物を加えていました。しかし、そうではありませんRealWorld。純粋なHaskell内に完全にIOタイプを実装できることがわかります(が与えられている限り)。アイデアはこれです:IOアクションIO typeは関数と同じでRealWorld -> (type, RealWorld)、現実の世界を取り、型のオブジェクトtypeと変更されたの両方を返しますRealWorld。次に、いくつかの関数を定義して、狂わないようにこの型を使用できるようにします。

return :: a -> IO a
return a = \rw -> (a,rw)

(>>=) :: IO a -> (a -> IO b) -> IO b
ioa >>= fn = \rw -> let (a,rw') = ioa rw in fn a rw'

最初の1つreturn 3は、何もしないIOアクションについて話すことを可能にします3。これは、現実の世界をクエリせずに単に戻るIOアクションです。>>=「バインド」と発音オペレータは、私たちはIOアクションを実行することができます。IOアクションから値を抽出し、その値と現実の世界を関数に渡し、結果のIOアクションを返します。>>=は、IOアクションの結果が決してエスケープされないようにするというルールを適用していることに注意してください。

次に、上記mainを通常の関数アプリケーションのセットに変換できます。

main = getLine >>= \str -> let no = (fst . head $ reads str :: Integer) in ...

Haskellランタイムmainは最初のからジャンプスタートし、RealWorld準備が整いました!すべてが純粋で、派手な構文を持っているだけです。

[ 編集: @Conalが指摘するように、これは実際にはHaskellがIOを行うために使用するものではありません。このモデルは、並行性を追加した場合、または実際にIOアクションの最中に世界が変化する方法で破損するため、Haskellがこのモデルを使用することは不可能です。逐次計算の場合にのみ正確です。したがって、HaskellのIOは少し覆い隠されているかもしれません。そうでない場合でも、これほどエレガントではありません。@Conalの観察に従って、厄介な分隊[pdf]のセクション3.1、セクション3.1でSimon Peyton-Jonesが言ったことを確認してください。彼はこれらの線に沿って代替モデルに相当するかもしれないものを提示しますが、それからその複雑さのためにそれを落とし、別の工夫をします。]

繰り返しになりますが、これは、HaskellでIOと可変性が一般的にどのように機能するかを(かなり)説明しています。これがあなたが知りたいすべてであるならば、あなたはここで読むのをやめることができます。理論の最後の1つの線量が必要な場合は、読み続けてください。ただし、現時点では、質問からかなり離れています。

つまり、最後に1つあります。それは、この構造(returnandを使用したパラメトリックタイプ)>>=が非常に一般的であることを示しています。これはモナドと呼ばれ、とdo表記されておりreturn>>=それらのどれでも動作します。ここで見たように、モナドは魔法ではありません。魔法のすべては、doブロックが関数呼び出しに変わることです。RealWorldタイプは、私たちがどんな魔法を参照してください唯一の場所です。[]リストコンストラクタのような型もモナドであり、不純なコードとは何の関係もありません。

モナドの概念については(ほとんど)すべて知っています(満たす必要があるいくつかの法則と正式な数学的定義を除く)。しかし、直感に欠けています。オンラインには途方もない数のモナドチュートリアルがあります。私はこれが好きですが、オプションがあります。ただし、これはおそらく役に立たないでしょう。直感を得るための唯一の実際の方法は、それらを使用し、適切なタイミングでいくつかのチュートリアルを読むことを組み合わせることです。

ただし、IOを理解するのにその直感は必要ありません。モナドを完全に理解することは簡単なことですが、今はIOを使用できます。最初のmain機能を紹介した後で使用できます。IOコードを不純な言語であるかのように扱うこともできます!しかし、根本的な機能表現があることを忘れないでください。

(PS:長さについて申し訳ありません。私は少し遠くに行きました。)


6
Haskellについて常に私を惹きつけているのは(私が作り、そして学ぶために勇敢な努力をしています)、構文の醜さです。それは彼らが他のすべての言語の最悪のビットを取り、バケツにそれらを落とし、激しく攪拌したようなものです。そして、これらの人々は、C ++の(場所で)奇妙な構文について不平を言うでしょう!

19
ニール:本当ですか?私は実際にHaskellの構文はとてもきれいだと思います。私は興味がある; 特に何を参照していますか?(それだけの価値があるので> >、テンプレートで行う必要がある場合を除いて、C ++も実際に私を悩ませることはありません。)
Antal Spector-Zabusky

6
私の目には、Haskellの構文は、Schemeほどきれいではありませんが、C ++が最悪のクラスである中括弧の言語の恐ろしい構文と比較することはできません。 。味の説明はないと思います。誰もが構文的に楽しいと思うような言語は存在しないと思います。
2010年

8
@NeilButterworth:あなたの問題は、関数名ほど構文ではないのではないかと思います。関数の代わりにandが呼び出される場所が多い、>>=または$多い場合、haskellコードはperlのようには見えません。つまり、haskellとscheme構文の主な違いは、haskellに中置演算子とオプションの括弧があることです。中置演算子を使いすぎないようにすると、ハスケルは括弧が少ないスキームによく似たものになります。bindapply
sepp2k

5
@camcann:まあ、要点ですが、私が意味したことは、スキームの基本的な構文はです(functionName arg1 arg2)functionName arg1 arg2かっこを削除すると、それがhaskell構文になります。infix演算子に任意のひどい名前を付けると、arg1 §$%&/*°^? arg2haskellのようになります。(私はちょうどちょうどいじめている、私は実際にハスケルが好きです)。
sepp2k

23

ここにはたくさんの良い答えがありますが、それらは長いです。私は役に立つ短い答えを出そうとするつもりです:

  • 関数型言語は、Cと同じ場所、つまり名前付き変数とヒープに割り当てられたオブジェクトに状態を配置します。違いは次のとおりです。

    • 関数型言語では、「変数」は(関数呼び出しまたはletバインディングを介して)スコープに入ったときに初期値を取得し、その値は後で変更されません。同様に、ヒープに割り当てられたオブジェクトは、そのすべてのフィールドの値ですぐに初期化され、その後は変化しません。

    • 「状態の変化」は、既存の変数またはオブジェクトを変更するのではなく、新しい変数をバインドするか、新しいオブジェクトを割り当てることによって処理されました。

  • IOはトリックで機能します。文字列を生成する副作用の計算は、Worldを引数として取り、文字列と新しいWorldを含むペアを返す関数によって記述されます。世界には、すべてのディスクドライブの内容、これまでに送受信されたすべてのネットワークパケットの履歴、画面上の各ピクセルの色などが含まれます。トリックの鍵は、世界へのアクセスが慎重に制限されているため、

    • プログラムは世界のコピーを作成できません(どこに配置しますか?)

    • プログラムは世界を捨てることはできません

    このトリックを使用すると、時間の経過とともに状態が変化する、1つのユニークな世界を作ることができます。関数型言語で記述されていない言語ランタイムシステムは、新しいワールドを返す代わりに、固有のワールドを更新することにより、副作用のある計算を実装します。

    このトリックは、サイモンペイトンジョーンズとフィルワドラーの画期的な論文「命令型関数プログラミング」で美しく説明されています。


4
私の知る限り、Haskellの型には同時実行性が含まれているのに対して、このモデルは純粋に逐次計算のみを説明しているため、Haskellに適用した場合、このIOストーリー(World -> (a,World))は神話IOです。「純粋に連続的」とは、命令計算の開始と終了の間で、その計算以外の理由で、世界(宇宙)でさえ変更を許可されないことを意味します。たとえば、あなたのコンピュータが離れている間、あなたの脳などはできません。並行World -> PowerSet [(a,World)]性は、のようなもので処理できます。これにより、非決定性とインターリーブが可能になります。
Conal

1
@Conal:IOストーリーは、非決定性とインターリーブにかなりうまく一般化すると思います。私の記憶が正しければ、「ぎこちない部隊」のペーパーにはかなり良い説明があります。しかし、真の並列性を明確に説明する優れた論文は知りません。
ノーマンラムジー、

3
私が理解しているように、「厄介な分隊」の論文では、の単純な表記モデルIO、つまりWorld -> (a,World)(私が言及した人気のある永続的な「神話」)を一般化する試みを放棄し、代わりに操作上の説明をしています。運用セマンティクスを好む人もいますが、彼らは私を完全に不満にしています。別の答えで私の長い返事を見てください。
Conal

+1これは私がIOモナドを理解するのに役立つだけでなく、質問に答えるのにも役立ちました。
CaptainCasey

ほとんどのHaskellコンパイラは実際にはIOとして定義しますがRealWorld -> (a,RealWorld)、実際に実際の世界を表すのではなく、単に渡される必要がある抽象値であり、コンパイラによって最適化されます。
ジェレミーリスト14

19

より多くのスペースを確保するために、新しい回答に対するコメントの返信を分割しています。

私が書いた:

私の知る限り、Haskellの型には同時実行性が含まれているのに対して、このモデルは純粋に逐次計算のみを説明しているため、Haskellに適用した場合、このIOストーリー(World -> (a,World))は神話IOです。「純粋に連続的」とは、命令計算の開始と終了の間で、その計算以外の理由で、世界(宇宙)でさえ変更を許可されないことを意味します。たとえば、コンピュータが離れている間、脳などは動きません。並行World -> PowerSet [(a,World)]性は、のようなもので処理できます。これにより、非決定性とインターリーブが可能になります。

ノーマンは書きました:

@Conal:IOストーリーは、非決定性とインターリーブにかなりうまく一般化すると思います。私の記憶が正しければ、「ぎこちない部隊」のペーパーにはかなり良い説明があります。しかし、真の並列性を明確に説明する優れた論文は知りません。

@ノーマン:どのような意味で一般化しますか?非決定性と同時実行性が考慮されていないためWorld -> (a,World)、通常与えられる表記モデル/説明はHaskell IOと一致しないことをお勧めします。など、適合するより複雑なモデルがある可能性がありますが、そのようなモデルがWorld -> PowerSet [(a,World)]適切に作成され、適切で一貫性があることが示されているかどうかはわかりません。個人的にIOは、FFIからインポートされた何千ものAPI呼び出しが多数存在することを考えると、そのような獣が見つかるかもしれません。このように、IOその目的を果たしています:

未解決の問題:IOモナドはHaskellの罪箱になりました。(何かを理解できないときはいつでも、IOモナドでそれを投げます。)

(サイモンPJのPOPLトークヘアシャツの着用ヘアシャツの着用:Haskellの回顧展

厄介な分隊へ取り組みのセクション3.1で、Simonはtype IO a = World -> (a, World)「並行性を追加するとアプローチがうまくスケーリングしない」など、うまくいかないことを指摘しています。次に、可能な代替モデルを提案し、次に、説明的な説明の試みを放棄して、

ただし、代わりに、プロセス計算のセマンティクスへの標準的なアプローチに基づいて、運用セマンティクスを採用します。

正確で有用な表記モデルを見つけることができなかったことは、Haskell IOが「関数型プログラミング」と呼ばれるものの精神と深い利点、またはピーターランディンがより具体的に「代表的なプログラミング」と呼ぶものからの逸脱であると私が考える理由の根本にあります。 ここでコメントを参照してください。


長い回答ありがとうございます。おそらく、私は新しい作戦指揮官によって洗脳されたと思います。左ムーバーや右ムーバーなどにより、いくつかの有用な定理を証明することが可能になりました。非決定性と同時実行性を説明する、あなたが好きな表現モデルを見たことがありますか?私はそうではありません。
ノーマンラムジー

1
私は、World -> PowerSet [World]非決定性とインターリーブスタイルの同時実行性を明確にキャプチャする方法が好きです。このドメイン定義は、主流の並行命令型プログラミング(Haskellのものを含む)は扱いにくい、つまり逐語的よりも指数関数的に複雑であることを示しています。ハスケルIO神話で私が目にする大きな害は、この固有の複雑さを覆い隠して、その転覆を動機づけています。
Conal

なぜWorld -> (a, World)壊れているのかはわかりますが、置換World -> PowerSet [(a,World)]が並行性などを適切にモデル化する理由はわかりません。私には、プログラムがIOリストモナドのようなもので実行され、返されたセットのすべての項目に適用される必要があることを意味しているようですIOアクションによって。何が欠けていますか?
Antal Spector-Zabusky

3
@Absz:まず、私の提案するモデルWorld -> PowerSet [(a,World)]は正しくありません。World -> PowerSet ([World],a)代わりに試してみましょう。 PowerSet可能な結果のセットを提供します(非決定性)。 [World]中間状態(リスト/非決定性モナドではない)のシーケンスであり、インターリーブ(スレッドスケジューリング)を可能にします。また([World],a)aすべての中間状態を通過する前にアクセスできるため、どちらも正しくありません。代わりに、use World -> PowerSet (Computation a)wheredata Computation a = Result a | Step World (Computation a)
Conal

まだ問題は発生しませんWorld -> (a, World)Worldタイプに本当にすべての世界が含まれている場合は、同時に実行されているすべてのプロセスに関する情報と、すべての非決定性の「ランダムシード」​​も含まれます。その結果World、時間は進んでいくらかの相互作用が行われた世界になります。このモデルの唯一の実際の問題は、それが一般的すぎて、の値をWorld構築および操作できないことです。
Rotsor 2011

17

関数型プログラミングはラムダ計算から派生しています。関数型プログラミングを本当に理解したい場合は、http: //worrydream.com/AlligatorEggs/をチェックしてください。

それはラムダ計算を学び、関数型プログラミングのエキサイティングな世界にあなたを連れて行く「楽しい」方法です!

Lambda Calculusを知ることは、関数型プログラミングにどのように役立ちますか。

したがって、ラムダ計算は、Lisp、Scheme、ML、Haskellなどの多くの実際のプログラミング言語の基盤です。

入力に3を追加する関数を記述して、次のように記述したいとします。

plus3 x = succ(succ(succ x)) 

「plus3は、任意の数値xに適用されると、xの後継者の後継者を生み出す関数です」を読んでください。

3を任意の数に加える関数はplus3という名前である必要はないことに注意してください。「plus3」という名前は、この関数に名前を付けるのに便利な省略形です

plus3 x) (succ 0) ≡ ((λ x. (succ (succ (succ x)))) (succ 0))

関数にはラムダ記号を使用していることに注意してください(アリゲーターのようなものだと思います)

ラムダ記号はアリゲーター(関数)で、xはその色です。xを引数として考えることもできます(ラムダ計算関数は実際には引数が1つしかないと仮定しています)残りは関数の本体と考えることができます。

次に、抽象化について考えます。

g  λ f. (f (f (succ 0)))

引数fは、関数の位置(呼び出し)で使用されます。他の関数を入力として受け取るため、ga高次関数を呼び出します。他の関数呼び出しfは「」と考えることができます。作成した2つの関数または「Alligators」を取得すると、次のようなことができます。

(g plus3) =  f. (f (f (succ 0)))(λ x . (succ (succ (succ x)))) 
= ((λ x. (succ (succ (succ x)))((λ x. (succ (succ (succ x)))) (succ 0)))
 = ((λ x. (succ (succ (succ x)))) (succ (succ (succ (succ 0)))))
 = (succ (succ (succ (succ (succ (succ (succ 0)))))))

λfアリゲーターがλxアリゲ​​ーターを食べ、次にλxアリゲ​​ーターを食べて死んでしまうことがわかります。次に、λxアリゲ​​ーターがλfのアリゲーターの卵に生まれ変わります。その後、このプロセスが繰り返され、左側のλxアリゲ​​ーターが右側の他のλxアリゲ​​ーターを食べます。

次に、この " アリゲーター "を食べる " アリゲーター "の単純なルールセットを使用して文法を設計し、関数型プログラミング言語が誕生しました。

したがって、ラムダ計算を知っていれば、関数型言語の仕組みを理解できるでしょう。


@tuckster:私は以前にラムダ計算を何度も研究しました...そしてはい、AlligatorEggsの記事は私にとって意味があります。しかし、それをプログラミングに関連付けることはできません。私にとって、今のところ、ラブダ計算は別の理論のようなものであり、そこにあります。ラムダ計算の概念はプログラミング言語でどのように使用されますか?
Lazer、

3
@eSKay:Haskell ラムダ計算であり、通常のプログラミング言語のように見えるようにシンタックスシュガーの薄層を備えています。Lispファミリの言語も、型なしラムダ計算と非常によく似ています。これは、アリゲーターの卵が表すものです。ラムダ計算自体は、本質的には「関数型プログラミングアセンブリ言語」のような、最小限のプログラミング言語です。
2010年

@eSKay:いくつかの例との関係について少し追加しました。これが役に立てば幸いです!
PJT 2010年

私の回答から差し引く場合は、理由についてコメントを残してください。そうすれば、私の回答を改善することができます。ありがとうございました。
PJT

14

Haskellで状態を処理する手法は非常に簡単です。そして、モナドを理解する必要はありません。

状態のあるプログラミング言語では、通常、どこかに値を格納し、いくつかのコードを実行してから、新しい値を格納します。命令型言語では、この状態は「バックグラウンド」のどこかにあります。(純粋な)関数型言語ではこれを明示的にするので、状態を変換する関数を明示的に記述します。

したがって、タイプXのいくつかの状態を持つ代わりに、XをXにマップする関数を作成します。それだけです!状態について考えることから、その状態に対して実行したい操作について考えることに切り替えます。次に、これらの関数をチェーン化し、さまざまな方法で組み合わせて、プログラム全体を作成できます。もちろん、XをXにマッピングするだけに限定されません。データのさまざまな組み合わせを入力として受け取り、さまざまな組み合わせを最後に返す関数を作成できます。

モナドは、これを整理するのに役立つ多くのツールの1つです。しかし、モナドは実際には問題の解決策ではありません。解決策は、状態ではなく状態変換について考えることです。

これはI / Oでも機能します。実際に何が起こるかはこれです:の直接的な同等のものを使用してユーザーから入力を取得しscanfてどこかに保存する代わりに、代わりに、それがあったかどうかの結果で何をするかを言う関数を記述し、scanfそれを渡しますI / O APIへの関数。Haskellでモナド>>=を使用すると、まさにそのようになりますIO。したがって、I / Oの結果をどこにでも保存する必要はありません。変換方法を示すコードを記述するだけです。


8

(一部の関数型言語は不純な関数を許可します。)

以下のために純粋に機能的な言語、現実世界の相互作用は、通常、このような関数の引数の一つとして含まれています:

RealWorld pureScanf(RealWorld world, const char* format, ...);

プログラマーから世界を抽象化するための言語は、言語によって異なります。たとえば、Haskellはモナドを使用してworld引数を非表示にします。


しかし、関数型言語自体の純粋な部分はすでにチューリング完全です。つまり、Cで実行できることはすべてHaskellでも実行できます。命令型言語との主な違いは、適切な状態を変更することではありません。

int compute_sum_of_squares (int min, int max) {
  int result = 0;
  for (int i = min; i < max; ++ i)
     result += i * i;  // modify "result" in place
  return result;
}

変更部分を関数呼び出しに組み込み、通常はループを再帰に変換します。

int compute_sum_of_squares (int min, int max) {
  if (min >= max)
    return 0;
  else
    return min * min + compute_sum_of_squares(min + 1, max);
}

またはcomputeSumOfSquares min max = sum [x*x | x <- [min..max]];-)
fredoverflow 2010年

@フレッド:リスト内包表記は単なる構文上の砂糖です(リストモナドを詳しく説明する必要があります)。そして、どのように実装しsumますか?再帰はまだ必要です。
kennytm

3

関数型言語状態保存できます!彼らは通常、そうすることをあなたに明示することを奨励または強制するだけです。

たとえば、HaskellのState Monadをチェックしてください。


9
また、状態は何もない、StateまたはMonad有効にするものはないことに注意してください。どちらも単純で一般的な機能的なツールの観点から定義されているためです。それらは関連するパターンをキャプチャするだけなので、ホイールをそれほど再発明する必要はありません。
Conal


1

haskell:

main = do no <- readLn
          print (no + 1)

もちろん、関数型言語の変数に物事を割り当てることができます。それらを変更することはできません(したがって、基本的にすべての変数は関数型言語の定数です)。


@ sepp2k:なぜ、それらを変更することの害は何ですか?
Lazer、

@eSKay変数を変更できない場合、それらは常に同じであることがわかります。これにより、デバッグが容易になり、1つのことだけを行い、非常にうまく機能する単純な関数を作成する必要があります。また、並行性を扱うときにも役立ちます。
Henrik Hansen

9
@eSKay:関数型プログラマーは、ミュータブルな状態はバグの多くの可能性をもたらし、プログラムの動作を推論することを難しくすると信じています。たとえば、関数呼び出しがf(x)あり、xの値を確認したい場合は、xが定義されている場所に移動するだけです。xが可変である場合、その定義とその使用の間にxが変更される可能性がある点があるかどうかも考慮する必要があります(xがローカル変数でない場合、それは重要です)。
sepp2k

6
変更可能な状態や副作用を信用しないのは、関数型プログラマだけではありません。不変オブジェクトとコマンド/クエリの分離は、かなりの数のOOプログラマーから高く評価されており、ほとんどすべての人が、可変グローバル変数は悪い考えだと考えています。Haskellのような言語は、ほとんどのことをさらに理解している...
CA McCann

5
@eSKay:ミューテーションが有害であるということはそれほど多くありません。ミューテーションを回避することに同意すると、モジュール式で再利用可能なコードを書くのがはるかに簡単になることが判明します。変更可能な状態を共有しないと、コードの異なる部分間の結合が明示的になり、設計の理解と維持がはるかに容易になります。ジョン・ヒューズはこれを私よりもよく説明しています。彼の論文「Why Functional Programming Matters」を入手してください
ノーマンラムジー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.