3

私はF#で始まり、構文の理解を進めています。しかし、私はF#の機能を使用する最良の方法については不明な点があります。 Pythonでは、私がどこから来ているのか、通常は「最良の」(ほぼ標準的な)方法があります。多分F#の場合もそうですが、私はそれを理解していません。だから私の質問は、F#の構文に関する技術的な質問ではなく、F#を使用する最良の方法についてです。Intellisenseを活用するコードを書くべきでしょうか?

最近、私は博士Eric Meijer (C9 Lectures - Functional Programming Fundamentals Chapter 2 of 13)のビデオを見ました。Meijer博士はOIのドット表記法を賞賛し、Intellisenseが利用可能な方法のリストを表示することを認めました。彼は、そのような施設は純粋なFPで利用できないことを嘆いています。プログラマーが "前進する"のを助けることで、プログラミングがずっと簡単になります。

実験的なことは、もちろんIntellisenseがF#クラスで動作することを示しましたが、クラスと同様にドット表記を使用するF#レコードでも動作します。これは、Intellisenseを利用してクラスを書くことなく、自分のコードを形作ることができることを意味します(私は、F#クラスではレコードよりも重くて遅いと仮定しています。これは純粋なFPとOOPの間の中間の戦略があることを示している

// Create a record type with two values that are functions of two arguments 
type AddSub = {add2: int -> int -> int; sub2: int -> int -> int} 

// Instantiate a record 
let addsub a = 
    {add2 = (fun x y -> a + x + y); sub2 = (fun x y -> a - x - y)} 

// Returns 7, Intellisense works on (addsub 0). 
(addsub 0).add2 3 4 
// Returns 3, Intellisense works on (addsub 10). 
(addsub 10).sub2 3 4 

// Create two functions of three arguments 
let add3 a x y = a + x + y 
let sub3 a x y = a - x - y 

// Also got 7, no Intellisense facility here 
add3 0 3 4 
// Also got 3, no Intellisense facility here 
sub3 10 3 4 

:レコードタイプを作成

次のコードは、同じ操作を実行するコードを書くの2つの方法(「バージョン」と呼ん)を示します上記のように、関数値を持つ。このような戦略は、オブジェクト(レコードインスタンス)を中心とした意味のある単位でコードを編成し、Intellisenseを使用できるようにしますが、継承やサブクラス多型などのクラスによって提供される機能の一部が欠けています。

OOPバックグラウンドから来ている私は、上のコードのaのようなオブジェクトが、パラメータxとyよりも何らかの "重要"(この用語は未定義のままです)のようなものがあれば、コード構成とIntellisenseを使用する能力の根拠に基づいています。一方、OOPの複雑さによって燃え尽きると、私はむしろ「純粋な」FP領域にとどまります。

2つの極端な選択肢(OOPと純粋なFP)の間で、レコードの使用は価値のある妥協点ですか?

一般に、3つの選択肢(純粋なFP、上記のような記録、またはクラス)は、ある選択肢が他の選択肢より優先される状況に関する一般的なガイドラインは何ですか?

最後に、コードを整理したり、Intellisenseを利用したりするのに役立つ他のコーディング戦略がありますか?

答えて

8

IntellisenseはまだF#ではうまく動作しますが、クラスレベルではなくモジュールレベルで動作します。つまり、私はちょうどList.に入力されたと私は、ドットを入力した後、(F#インテリセンスを提供Ionideプラグイン付き)VSコードは私に補完候補のリストを与えた:appendaverageaverageBychoosechunkBySize ...

へモジュールに入れて、独自の関数からその利益を得る:

module AddSub = 
    let add2 x y = x + y 
    let sub2 x y = x - y 
    let add3 a x y = a + x + y 
    let sub3 a x y = a - x - y 

今、あなたはAddSub.を入力すると、あなたがドットを入力した後、IntelliSenseが可能フォローとしてadd2add3sub2、およびsub3を提案します。しかし、あなたは "適切な" F#スタイルで、あなたの機能を「きれいに」カラライナブルに保ちました。

最後に、機能設計に関するもう1つのアドバイス。あなたは、1つのパラメータ(add3sub3の関数の中のaのような)が他のパラメータよりいくらか "重要"であるという関数を持つと述べました。ほとんどの人というスタイルを使用して、

let a = 20 
a |> AddSub.add3 5 10 |> AddSub.sub3 2 3 // Result: 30 

というか:それはそうと同じように、あなたが|>演算子を使用して、関数チェーンにそれを置くことを可能にするため、F#では、どのようなパラメータは、おそらく、最後パラメータでなければなりませんその中の多くの操作があるとき、パイプラインを並べる

let a = 20 
a 
|> AddSub.add3 5 10 
|> AddSub.sub3 2 3 
// Result: 30 

垂直より重要になります:単一開始値からの操作の「パイプライン」がいたときに好みます。私の経験則では、パイプラインに2つ以上の合計 "余分な"パラメータが指定されている場合(上のパイプラインには4つの "余分な"パラメータがあり、add3sub3ファンクションの2つのパラメータがあります) (例えば、あるパラメータが(fun x -> sprintf "The value of x was %d" x)などのような無名関数である場合)、それを垂直に配置する必要があります。

P.S.まだ読んでいないなら、Scott Wlaschinの優秀なシリーズThinking Functionallyを読んでください。この答えに関する多くのことを説明するのに役立ちます。なぜ、私が最後に「最も重要な」引数を置くことを提案したかのように。 |>パラメータでそれを使用する方法についての簡単なコメントをすぐに理解できなかった場合、またはこの回答について困惑したことが他にもあった場合は、おそらくScottの記事から多くのメリットを得るでしょう。

+0

恐ろしいです!ありがとう。私はモジュールを考えるべきだった。私はMeijer博士がIntellisenseを使用できないと不平を言ったことが私を捨てたものだと思う。しかし、彼はハスケルについて話していました。おそらく、ハスケルにはモジュールも同様の施設もありません。 – Soldalma

+0

ええ、Haskellの標準的なプラクティスは、モジュールから関数をインポートするのではなく、修飾された関数を使用することです。そのため、Intellisenseにはあまり役に立ちません。 – Tarmil

+0

@ Tarmil afaik、Haskell用の自動補完ツールがあります。https://github.com/rikvdkleij/intellij-haskell – Yawar

3

あなたの質問は非常に幅広く、モジュールの特定の側面のいくつかは既に@munnでカバーされています。私はかなり大きなF#コードベースで自分自身の作業から生じるいくつかの考えを、開発チームの変化に合わせて追加したいと思っていました。

コード検出可能性の規則。あなたのコードベースが成長するにつれ、(Intellisenseを介して)オブジェクトに利用可能なメソッドを見ることができることがより重要になっています。しかし、さらに重要なのは、あなたのチームに新しい参加者を助けることです。チームには、クラス/レコード/などのインスタンスを扱うすべてのメソッドを持つモジュールXがあることをまだ知りません。

私はF# component design guideが非常に役に立ちました。 OOPと機能のバランスを取る方法の詳細をたくさん提供しています。あなたの具体的な質問については、それはあなたが提起ポイントへの直接参照で述べているsection on intrinsic operationsを参照してください。

は性質と種類に固有の操作のためのメソッドを使用しています。 これは、関数型プログラミングのバックグラウンドの人々の中には、オブジェクト指向プログラミングを一緒に使用することを避け、型に関連する組み込み関数(foo.Lengthではなく長さfooなど)を定義する関数群を含むモジュールが望ましいため、しかし、次の箇条書きも参照してください。一般に、F#では、ソフトウェア工学装置としてオブジェクト指向プログラミングの使用が好ましい。この戦略では、Visual Studioの "Intellisense"機能のようなツールを使用して、オブジェクトに「ドットをつける」ことで型のメソッドを発見することもできます。

クラス階層を作成しようとすると、継承を区別された共用体に置き換えることができないかどうかを2回考えてください。できるだけレコード、クラスなどでメンバ(静的またはインスタンスメンバー)をタックすることができます:

type Animal = 
    | Cat | Dog 
    member this.Sound = match this with | Cat -> "meow" | Dog -> "bark" 
    static member FromString s = function | "cat" -> Cat | "dog" -> Dog | _ -> failwith "nope." 
+3

Imho、タイプをできるだけシンプルに保ち、タイプとその操作関数を同じものにまとめる方が良いですモジュールをプライマリタイプの周りにしっかりと集中させます。次に、OOPスタイルとFPスタイルを厄介に切り替えるのではなく、どこでも関数を使うことに固執します。例えば、 https://gist.github.com/yawaramin/552c25a23549f15556d54954e39f946d – Yawar

+0

@Anton - 洞察をいただきありがとうございます。私はあなたが引用したアドバイスによってちょっと驚きました。実用的な見地からは賢明に聞こえるが、FPには賢明な選択肢があると思った。 クラス階層では、わかりません。私は専門家ではありませんが、新しい動物(ライオン/轟音)が登場するたびにあなたの動物タイプが修正されなければならないように思えます。私はこれがSOLIDのOpen/Close原則に違反すると信じています。それでも、新しいサブタイプの可能性がないことがわかったときにはうまくいくでしょう。 – Soldalma

関連する問題