35

すっごく...

半群、モノイド、モナド、ファンクター、レンズ、カタモルフィズム、アナモルフィズム、アロー... これらはどれも良さそうで、1 回か 2 回 (または 10 回) 練習すれば、それらの本質をつかむことができます。そしてScalaz、あなたはそれらを無料で手に入れます...

ただし、実際のプログラミングに関しては、これらの概念の使用法を見つけるのに苦労しています。はい、もちろん、私はいつもウェブ上で Monads for IO や Scala の Lenses を使っている人を見つけますが...それでも...

私が見つけようとしているのは、パターンの「規範的な」線に沿ったものです。「ここで、あなたはこれを解決しようとしています。それを解決する良い方法の 1 つは、このようにレンズを使用することです!」

提案?


更新: 1 冊か 2 冊の本で、これらの行に沿った何かがあれば、すばらしいでしょう (Paul に感謝します): Java のコア ライブラリの GoF デザイン パターンの例

4

5 に答える 5

19

関数型プログラミングの鍵は、抽象化と、抽象化の構成可能性です。モナド、アロー、レンズ、これらはすべて抽象化されており、主に構成可能であるため、有用であることが証明されています。あなたは「規範的な」回答を求めましたが、私はノーと言うつもりです。おそらく、関数型プログラミングが重要であると確信していませんか?

StackOverflow の多くの人が、FP の方法で特定の問題を解決しようと喜んで手助けしてくれると確信しています。もののリストがあり、リストをトラバースして結果を作成したいですか? 折り目を使用します。XML を解析したいですか? hxtはそのために矢印を使用します。そしてモナド?さて、大量のデータ型がモナドであることが判明したので、モナドについて学び、これらのデータ型を操作する豊富な方法を発見してください。しかし、何もないところから例を引き出して、「レンズはこれを行う正しい方法です」、「モノイドはそれを行うための最良の方法です」などと言うのは難しいです。 forループは?[空白] にしたい場合は、[このように] for ループを使用します。それはとても一般的です。for ループを使用する方法はたくさんあります。

長年の OOP の経験がある場合は、OOP の初心者だったことを忘れないでください。FP のやり方を学ぶには時間がかかり、OOP の傾向を学ぶのにさらに時間がかかります。時間をかけて、関数型アプローチの多くの用途を見つけることができます。

于 2011-12-11T07:55:47.133 に答える
12

私は9月に、 scalaz.Validationを介したモノイドと適用可能なファンクター/モナドの実用化に焦点を当てた講演を行いました。私はスカラリフトオフで同じ話の別のバージョンを提供しました。そこでは検証に重点が置かれました。検証を開始するまで最初の話を見てから、2番目の話にスキップします(27分)。

「実用的な」アプリケーションで検証を使用する方法を示す、私が書いた要点もあります。つまり、ナイトクラブの用心棒用のソフトウェアを設計している場合です。

于 2011-12-11T17:44:53.913 に答える
7

逆のアプローチを取ることができると思います。代わりに、機能の小さな部分を書くときに、それらのいずれかが適用されるかどうかを自問してください: セミグループ、モノイド、モナド、ファンクター、レンズ、カタモルフィズム、アナモルフィズム、アロー...それらの概念の多くローカルな使い方ができます。

そのルートを開始すると、どこでも使用法が見られる場合があります。私にとっては、セミグループ、モノイド、モナド、ファンクターのようなものです。この質問に答える例を見てみましょう How do I populate a list of objects with new values . これは、質問者 (自称 noob) にとっての実際の使用法です。私は簡単な方法で答えようとしていますが、「ここにモノイドがある」というかゆみを掻くのは控えなければなりません。

今それをスクラッチ: usingfoldMapと Int と List がモノイドであり、タプル、マップ、およびオプションを処理するときにモノイド プロパティが保持されるという事実:

// using scalaz
listVar.sliding(2).toList.foldMap{
  case List(prev, i) => Some(Map(i -> (1, Some(List(math.abs(i - prev))))))
  case List(i) => Some(Map(i -> (1, None)))
  case _ => None
}.map(_.mapValues{ case (count, gaps) => (count, gaps.map(_.min)) })

しかし、ハードコアな関数型プログラミングを使用すると考えて、その結果に到達することはありません。scalaz には のようなユーティリティ メソッドがあるという事実と組み合わせてこれらのモノイドを構成すると、これはより単純に見えると考えると、より自然になりますfoldMap。興味深いことに、結果のコードを見ると、完全にモノイドの観点から考えていることが明らかではありません。

于 2011-12-11T15:55:09.140 に答える
6

Chris Marshall によるこのトークが気に入るかもしれません。彼は、いくつかの Scalaz の優れた機能 (Monoid と Validation) を多くの実用的な例とともに取り上げています。Ittay Dror は、Functor、Applicative Functor、および Monad が実際にどのように役立つかについて、非常にアクセスしやすい投稿を書きました。Eric TorreborreDebasish Goshのブログにも、カテゴリ構造のユース ケースを取り上げた記事が多数あります。

この回答は、ここで実際の内容を提供するのではなく、いくつかのリンクをリストするだけです。(書くのが面倒です。) とにかく役に立てば幸いです。

于 2011-12-11T16:07:09.133 に答える
4

I understand your situation, but you will find that to learn functional programming you will need to adjust your point of view to the documentation you find, instead of the other way around. Luckily in Scala you have the possibility of becoming a functional programmer gradually.

To answer your questions and explain the point-of-view difference, I need to distinguish between "type classes" (monoids, functors, arrows), mathematically called "structures", and generic operations or algorithms (catamorphisms or folds, anamorphisms or unfolds, etc.). These two often interact, since many generic operations are defined for specific classes of data types.

You look for prescriptive answers similar to design patterns: when does this concept apply? The truth is that you have surely seen the prescriptive answers, and they are simply the definitions of the different concepts. The problem (for you) is that those answers are intrinsically different from design patterns, but it is so for good reasons.

On the one hand, generic algorithms are not design patterns, which suggest a structure for the code you write; they are abstractions defined in the language which you can directly apply. They are general descriptions for common algorithms which you already implement today, but by hand. For instance, whenever you are computing the maximum element of a list by scanning it, you are hardcoding a fold; when you sum elements, you are doing the same; and so on. When you recognize that, you can declare the essence of the operation you are performing by calling the appropriate fold function. This way, you save code and bugs (no opportunity for off-by-one errors), and you save the reader the effort to read all the needed code.

On the other hand, structures concern not the goal you have in mind but properties of the entities you are modeling. They are more useful for bottom-up software construction, rather than top-down: when defining your data, you can declare that it is a e.g. a monoid. Later, when processing your data, you have the opportunity to use operations on e.g. monoids to implement your processing. In some cases it is useful to strive to express your algorithm in terms of the predefined ones. For instance, very often if you need to reduce a tree to a single value, a fold can do most or all of what you need. Of course, you can also declare that your data type is a monoid when you need a generic algorithm on monoids; but the earlier you notice that, the earlier you can start reusing generic algorithms for monoids.

Last advice is that probably most of the documentation you will find about these concepts concerns Haskell, because this language has been around for much more time and supports them in a quite elegant way. Quite recommended here are Learn you a Haskell for Great Good, a Haskell course for beginners, where among others chapters 11 to 14 focus on some type classes, and Typeclassopedia (which contains links to various articles with specific examples). EDIT: Finally, an example of applications of Monoids, taken from Typeclassopedia, is here: http://apfelmus.nfshost.com/articles/monoid-fingertree.html. I'm not saying there is little documentation for Scala, just that there is more in Haskell, and Haskell is where the application of these concepts to programming was born.

于 2011-12-19T01:22:04.650 に答える