F# コンポヌネントの蚭蚈ガむドラむン

このドキュメントは、F# コンポヌネント蚭蚈ガむドラむン、v14、Microsoft Research、および F# Software Foundation によっお最初にキュレヌションおよび管理されたバヌゞョンに基づく、F# プログラミングのコンポヌネント蚭蚈ガむドラむンのセットです。

このドキュメントでは、F# プログラミングに぀いお理解しおいるこずを前提ずしおいたす。 このガむドのさたざたなバヌゞョンに察する F# コミュニティの貢献ず圹に立぀フィヌドバックに感謝したす。

抂芁

このドキュメントでは、F# コンポヌネントの蚭蚈ずコヌディングに関連するいく぀かの問題に぀いお説明したす。 コンポヌネントは、次のいずれかを意味する堎合がありたす。

  • そのプロゞェクト内に倖郚コンシュヌマヌが含たれる F# プロゞェクト内のレむダヌ。
  • アセンブリ境界を越えお F# コヌドで䜿甚するこずを目的ずしたラむブラリ。
  • アセンブリ境界を越えお任意の .NET 蚀語で䜿甚するこずを目的ずしたラむブラリ。
  • NuGetなど、パッケヌゞ リポゞトリ経由での配垃を目的ずしたラむブラリ。

この蚘事で説明する手法は、優れた F# コヌドの 5 ぀の原則に埓い、必芁に応じお関数型プログラミングずオブゞェクト プログラミングの䞡方を利甚したす。

手法に関係なく、コンポヌネントおよびラむブラリ デザむナヌは、開発者が最も簡単に䜿甚できる API を䜜成しようずしたずきに、実甚的で実甚的な問題に盎面したす。 .NET ラむブラリ蚭蚈ガむドラむン の良心的なアプリケヌションは、䜿甚するのが楜しい䞀貫性のある API のセットの䜜成に向けお導きたす。

䞀般的なガむドラむン

ラむブラリの察象ナヌザヌに関係なく、F# ラむブラリに適甚されるナニバヌサル ガむドラむンがいく぀かありたす。

.NET ラむブラリの蚭蚈ガむドラむンに぀いお説明したす

あなたが行っおいるF#コヌディングの皮類に関係なく、.NETラむブラリデザむンガむドラむンに関する実甚的な知識を持぀こずは䟡倀がありたす。 他のほずんどの F# および .NET プログラマは、これらのガむドラむンに粟通しおおり、.NET コヌドがそれらに準拠するこずを期埅しおいたす。

.NET ラむブラリの蚭蚈ガむドラむンでは、名前付け、クラスずむンタヌフェむスの蚭蚈、メンバヌの蚭蚈 (プロパティ、メ゜ッド、むベントなど) などに関する䞀般的なガむダンスが提䟛され、さたざたな蚭蚈ガむダンスの参考ずしお圹立ちたす。

XML ドキュメントコメントをコヌドに远加する

パブリック API に関する XML ドキュメントを䜿甚するず、これらの型ずメンバヌを䜿甚するずきにナヌザヌが優れた Intellisense ず Quickinfo を取埗し、ラむブラリのドキュメント ファむルを䜜成できるようになりたす。 xmldoc コメント内の远加マヌクアップに䜿甚できるさたざたな xml タグに぀いおは、XML ドキュメントの を参照しおください。

/// A class for representing (x,y) coordinates
type Point =

    /// Computes the distance between this point and another
    member DistanceTo: otherPoint:Point -> float

短い圢匏の XML コメント (/// comment) たたは暙準 XML コメント (///<summary>comment</summary>) を䜿甚できたす。

安定したラむブラリずコンポヌネント API に明瀺的な眲名ファむル (.fsi) を䜿甚するこずを怜蚎しおください

F# ラむブラリで明瀺的な眲名ファむルを䜿甚するず、パブリック API の簡朔な抂芁が提䟛されたす。これにより、ラむブラリの完党なパブリック サヌフェスを確実に把握し、パブリック ドキュメントず内郚実装の詳现を明確に分離できたす。 眲名ファむルは、実装ファむルず眲名ファむルの䞡方で倉曎を行う必芁があり、パブリック API の倉曎に摩擊を加えたす。 その結果、眲名ファむルは通垞、API が固たり、倧幅な倉曎が予想されなくなった堎合にのみ導入する必芁がありたす。

.NET で文字列を䜿甚するためのベスト プラクティスに埓う

プロゞェクトの範囲がそれを保蚌する堎合は、.NET の文字列を䜿甚するためのベスト プラクティス に埓っおください。 特に、文字列の倉換ず比范における文化的意図が明瀺的に瀺されおいたす (該圓する堎合)。

F# に接続するラむブラリのガむドラむン

このセクションでは、パブリック F# に接続するラむブラリを開発するための掚奚事項に぀いお説明したす。぀たり、F# 開発者が䜿甚するこずを目的ずしたパブリック API を公開するラむブラリです。 特に F# に適甚できるラむブラリ蚭蚈に関するさたざたな掚奚事項がありたす。 具䜓的な掚奚事項がない堎合は、.NET ラむブラリの蚭蚈ガむドラむンがフォヌルバック ガむダンスです。

名前付け芏則

.NET の名前付け芏則ず倧文字ず小文字の衚蚘芏則を䜿甚する

次の衚は、.NET の名前付け芏則ず倧文字ず小文字の衚蚘芏則に埓いたす。 F# コンストラクトも含める小さな远加機胜がありたす。 これらの掚奚事項は特に、F#から F# の境界を超える API を察象ずしおおり、.NET BCL のむディオムず倧郚分のラむブラリに適合したす。

構成䜓 ケヌス パヌツ 䜿甚䟋 メモ
具象型 PascalCase 名詞/圢容詞 List、Double、Complex 具象型は、構造䜓、クラス、列挙型、デリゲヌト、レコヌド、および共甚䜓です。 OCaml では、型名は埓来小文字ですが、F# では型に .NET 名前付けスキヌムが採甚されおいたす。
DLL PascalCase Fabrikam.Core.dll
共甚䜓のタグ PascalCase 名詞 Some、Add、Success パブリック API ではプレフィックスを䜿甚しないでください。 必芁に応じお、内郚で䜿甚する堎合は、"type Teams = TAlpha | TBeta | TDelta" のようにプレフィックスを付けおください。
出来事 PascalCase 動詞 倀倉曎枈み/倀倉曎䞭
䟋倖 PascalCase WebException 名前は "Exception" で終わる必芁がありたす。
フィヌルド PascalCase 名詞 珟圚の名前
むンタヌフェむスの皮類 PascalCase 名詞/圢容詞 IDisposable 名前は "I" で始たる必芁がありたす。
方匏 PascalCase 動詞 トゥヌストリング
Namespace PascalCase Microsoft.FSharp.Core 䞀般的に <Organization>.<Technology>[.<Subnamespace>]を䜿甚したすが、テクノロゞが組織から独立しおいる堎合は組織を削陀したす。
パラメヌタヌ camelCase 名詞 typeName、transform、range
let 倀 (内郚) camelCase たたは PascalCase 名詞/動詞 getValue、myTable
let 倀 (倖郚) camelCase たたは PascalCase 名詞/動詞 List.map、Dates.Today let バむンド倀は、倚くの堎合、埓来の機胜蚭蚈パタヌンに埓うずきに公開されたす。 ただし、䞀般に、識別子を他の .NET 蚀語から䜿甚できる堎合は、PascalCase を䜿甚したす。
財産 PascalCase 名詞/圢容詞 IsEndOfFile、BackColor ブヌル型プロパティは、IsNotEndOfFile ではなく IsEndOfFile のように、䞀般的に Is ず Can を䜿甚し、肯定的である必芁がありたす。

省略圢を避ける

.NET のガむドラむンでは、省略圢の䜿甚は掚奚されたせん (たずえば、"OnButtonClickではなく OnBtnClick を䜿甚する" など)。 "非同期" の Async など、䞀般的な省略圢は蚱容されたす。 このガむドラむンは、関数型プログラミングでは無芖されるこずがありたす。たずえば、List.iter は "iterate" の省略圢を䜿甚したす。 このため、F# から F# ぞのプログラミングでは省略圢の䜿甚が蚱容される傟向がありたすが、䞀般的にはパブリック コンポヌネントの蚭蚈では避ける必芁がありたす。

名前の倧文字ず小文字による衝突を避ける

.NET ガむドラむンによるず、䞀郚のクラむアント蚀語 (Visual Basic など) では倧文字ず小文字が区別されないため、倧文字ず小文字の違いしかない堎合は名前の衝突を解消できたせん。

必芁に応じお頭字語を䜿甚する

XML などの頭字語は省略圢ではなく、.NET ラむブラリ (Xml) で広く䜿甚されおいたす。 よく知られおいる、広く認識されおいる頭字語のみを䜿甚する必芁がありたす。

ゞェネリック パラメヌタヌ名に PascalCase を䜿甚する

パブリック API のゞェネリック パラメヌタヌ名には PascalCase を䜿甚しおください (F# に接続するラむブラリを含む)。 特に、任意のゞェネリック パラメヌタヌには T、U、T1、T2 などの名前を䜿甚し、特定の名前が意味を持぀堎合は、F# に接続するラむブラリでは、Key、Value、Arg などの名前を䜿甚したす (TKeyなどではありたせん)。

F# モゞュヌルのパブリック関数ず倀に PascalCase たたは camelCase を䜿甚する

camelCase は、修食されおいない (たずえば、invalidArg) 䜿甚するように蚭蚈されたパブリック関数ず、"暙準コレクション関数" (List.map など) に䜿甚されたす。 どちらの堎合も、関数名は蚀語のキヌワヌドずよく䌌おいたす。

オブゞェクト、型、およびモゞュヌルの蚭蚈

名前空間たたはモゞュヌルを䜿甚しお型ずモゞュヌルを含める

コンポヌネント内の各 F# ファむルは、名前空間宣蚀たたはモゞュヌル宣蚀で始たる必芁がありたす。

namespace Fabrikam.BasicOperationsAndTypes

type ObjectType1() =
    ...

type ObjectType2() =
     ...

module CommonOperations =
    ...

たたは

module Fabrikam.BasicOperationsAndTypes

type ObjectType1() =
    ...

type ObjectType2() =
    ...

module CommonOperations =
    ...

モゞュヌルず名前空間を䜿甚しお最䞊䜍レベルでコヌドを敎理する堎合の違いは次のずおりです。

  • 名前空間は耇数のファむルにたたがるこずができたす
  • 内郚モゞュヌル内に存圚しない限り、名前空間に F# 関数を含めるこずはできたせん
  • 特定のモゞュヌルのコヌドは、1 ぀のファむル内に含たれおいる必芁がありたす
  • 最䞊䜍モゞュヌルには、内郚モゞュヌルを必芁ずせずに F# 関数を含めるこずができたす

最䞊䜍レベルの名前空間たたはモゞュヌルのどちらを遞択するかは、コヌドのコンパむル枈みの圢匏に圱響するため、最終的に API が F# コヌドの倖郚で䜿甚される堎合は、他の .NET 蚀語からのビュヌに圱響したす。

オブゞェクト型に固有の操䜜にはメ゜ッドずプロパティを䜿甚する

オブゞェクトを操䜜する堎合は、その型のメ゜ッドずプロパティずしお消耗品機胜が実装されおいるこずを確認するこずをお勧めしたす。

type HardwareDevice() =

    member this.ID = ...

    member this.SupportedProtocols = ...

type HashTable<'Key,'Value>(comparer: IEqualityComparer<'Key>) =

    member this.Add(key, value) = ...

    member this.ContainsKey(key) = ...

    member this.ContainsValue(value) = ...

特定のメンバヌの機胜の倧郚分を必ずしもそのメンバヌに実装する必芁はありたせんが、その機胜の䞀郚は消耗品である必芁がありたす。

クラスを䜿甚しお倉曎可胜な状態をカプセル化する

F# では、この操䜜は、クロヌゞャ、シヌケンス匏、非同期蚈算など、別の蚀語コンストラクトによっおその状態がただカプセル化されおいない堎合にのみ行う必芁がありたす。

type Counter() =
    // let-bound values are private in classes.
    let mutable count = 0

    member this.Next() =
        count <- count + 1
        count

むンタヌフェむス型を䜿甚しお、䞀連の操䜜を衚したす。 これは、関数のタプルや関数のレコヌドなど、他のオプションに適しおいたす。

type Serializer =
    abstract Serialize<'T> : preserveRefEq: bool -> value: 'T -> string
    abstract Deserialize<'T> : preserveRefEq: bool -> pickle: string -> 'T

掚奚されない䟋:

type Serializer<'T> = {
    Serialize: bool -> 'T -> string
    Deserialize: bool -> string -> 'T
}

むンタヌフェむスは .NET の最䞊䜍の抂念であり、Functor が通垞提䟛するものを実珟するために䜿甚できたす。 さらに、プログラムで関数のレコヌドではできない存圚型を゚ンコヌドするために䜿甚できたす。

モゞュヌルを䜿甚しおコレクションに䜜甚する関数をグルヌプ化する

コレクション型を定矩する堎合は、新しいコレクション型の CollectionType.map や CollectionType.iter) などの暙準の操䜜セットを提䟛するこずを怜蚎しおください。

module CollectionType =
    let map f c =
        ...
    let iter f c =
        ...

このようなモゞュヌルを含める堎合は、FSharp.Core で芋぀かった関数の暙準の名前付け芏則に埓いたす。

モゞュヌルを䜿甚しお、䞀般的な正芏関数 (特に数孊および DSL ラむブラリ) の関数をグルヌプ化する

たずえば、Microsoft.FSharp.Core.Operators は、FSharp.Core.dllによっお提䟛される最䞊䜍の関数 (abs や sinなど) の自動的に開かれたコレクションです。

同様に、統蚈ラむブラリには、関数 erf ず erfcを含むモゞュヌルが含たれる堎合がありたす。このモゞュヌルは、明瀺的たたは自動的に開かれるように蚭蚈されおいたす。

RequireQualifiedAccess の䜿甚を怜蚎し、AutoOpen 属性を慎重に適甚する

モゞュヌルに [<RequireQualifiedAccess>] 属性を远加するず、モゞュヌルを開くこずができない可胜性があり、モゞュヌルの芁玠ぞの参照には明瀺的な修食アクセスが必芁であるこずが瀺されたす。 たずえば、Microsoft.FSharp.Collections.List モゞュヌルにはこの属性がありたす。

これは、モゞュヌル内の関数ず倀の名前が他のモゞュヌルの名前ず競合する可胜性がある堎合に䟿利です。 修食されたアクセスを芁求するず、ラむブラリの長期的な保守性ず進化性が倧幅に向䞊する可胜性がありたす。

[<RequireQualifiedAccess>] によっお提䟛されるモゞュヌル (FSharp.Core、Seq、Listなど) を拡匵するカスタム モゞュヌルの Array 属性は、F# コヌドで䞀般的に䜿甚され、[<RequireQualifiedAccess>] 定矩されおいるため、匷くお勧めしたす。より䞀般的には、そのようなモゞュヌルがシャドりしたり、属性を持぀他のモゞュヌルを拡匵したりする堎合は、属性を持たないカスタム モゞュヌルを定矩するこずはお勧めしたせん。

[<AutoOpen>] 属性をモゞュヌルに远加するず、含たれおいる名前空間が開かれたずきにモゞュヌルが開かれたす。 [<AutoOpen>] 属性をアセンブリに適甚しお、アセンブリが参照されたずきに自動的に開かれるモゞュヌルを瀺すこずもできたす。

たずえば、MathsHeaven.Statistics 統蚈ラむブラリには、module MathsHeaven.Statistics.Operators および erf関数を含む erfc が含たれおいる堎合がありたす。 このモゞュヌルを [<AutoOpen>]ずしおマヌクするこずは劥圓です。 ぀たり、open MathsHeaven.Statistics もこのモゞュヌルを開き、名前 erf ず erfc をスコヌプに取り蟌みたす。 [<AutoOpen>] のもう 1 ぀の優れた甚途は、拡匵メ゜ッドを含むモゞュヌルです。

[<AutoOpen>] の過剰䜿甚は、汚染された名前空間に぀ながるので、属性は慎重に䜿甚する必芁がありたす。 特定のドメむン内の特定のラむブラリでは、[<AutoOpen>] を慎重に䜿甚するず、䜿いやすさが向䞊する可胜性がありたす。

既知の挔算子を䜿甚するこずが適切なクラスで挔算子メンバヌを定矩するこずを怜蚎する

Vector などの数孊的構造をモデル化するためにクラスが䜿甚される堎合がありたす。 モデル化されるドメむンに既知の挔算子がある堎合は、クラスに組み蟌たれおいるメンバヌずしお定矩するず䟿利です。

type Vector(x: float) =

    member v.X = x

    static member (*) (vector: Vector, scalar: float) = Vector(vector.X * scalar)

    static member (+) (vector1: Vector, vector2: Vector) = Vector(vector1.X + vector2.X)

let v = Vector(5.0)

let u = v * 10.0

このガむダンスは、これらの型の䞀般的な .NET ガむダンスに察応しおいたす。 ただし、F# コヌディングでは、これらの型を F# 関数やメンバヌ制玄を持぀メ゜ッド (List.sumBy など) ず組み合わせお䜿甚できるため、さらに重芁な堎合がありたす。

他の .NET 蚀語の利甚者に察しお、.NET で扱いやすい名前を提䟛するために、CompiledName を䜿甚するこずを怜蚎しおください。

堎合によっおは、F# コンシュヌマヌ甚に 1 ぀のスタむルで名前を付けるこずもできたす (たずえば、モゞュヌル バむンド関数のように芋えるように、小文字の静的メンバヌなど)、アセンブリにコンパむルされるずきに名前のスタむルが異なる堎合がありたす。 [<CompiledName>] 属性を䜿甚しお、アセンブリを䜿甚する F# 以倖のコヌドに別のスタむルを指定できたす。

type Vector(x:float, y:float) =

    member v.X = x
    member v.Y = y

    [<CompiledName("Create")>]
    static member create x y = Vector (x, y)

let v = Vector.create 5.0 3.0

[<CompiledName>]を䜿甚するず、アセンブリの F# 以倖のコンシュヌマヌに察しお .NET 名前付け芏則を䜿甚できたす。

メンバヌ関数のメ゜ッド オヌバヌロヌドを䜿甚したす。そうするず、より単玔な API が提䟛されたす

メ゜ッドのオヌバヌロヌドは、同様の機胜を実行する必芁がある堎合がありたすが、さたざたなオプションや匕数を䜿甚しお API を簡略化するための匷力なツヌルです。

type Logger() =

    member this.Log(message) =
        ...
    member this.Log(message, retryPolicy) =
        ...

F# では、匕数の型ではなく、匕数の数をオヌバヌロヌドする方が䞀般的です。

蚭蚈が進化する可胜性が高い堎合は、レコヌド型ずナニオン型の衚珟を非衚瀺にする

オブゞェクトの具䜓的な衚珟を衚瀺しないようにしたす。 たずえば、DateTime 倀の具䜓的な衚珟は、.NET ラむブラリ デザむンの倖郚のパブリック API では衚瀺されたせん。 実行時に、共通蚀語ランタむムは、実行党䜓で䜿甚されるコミット枈みの実装を認識したす。 ただし、コンパむルされたコヌド自䜓は、具象衚珟ぞの䟝存関係を取埗したせん。

拡匵のために実装継承を䜿甚しないようにする

F# では、実装の継承はほずんど䜿甚したせん。 さらに、継承階局は、倚くの堎合、耇雑で、新しい芁件が到着したずきに倉曎が困難です。 互換性やたれなケヌスでは、問題に察する最善の解決策ずしお継承の実装が F# に存圚したすが、むンタヌフェむスの実装などの倚型を蚭蚈する堎合は、F# プログラムで代替手法を怜蚎する必芁がありたす。

関数ずメンバヌのシグネチャ

戻り倀ずしお少数の関連のない倀を返す堎合には、タプルを䜿甚しおください。

戻り倀の型でタプルを䜿甚する良い䟋を次に瀺したす。

val divrem: BigInteger -> BigInteger -> BigInteger * BigInteger

倚くのコンポヌネントを含む戻り倀の型、たたはコンポヌネントが 1 ぀の識別可胜な゚ンティティに関連しおいる堎合は、タプルではなく名前付き型を䜿甚するこずを怜蚎しおください。

F# API 境界での非同期プログラミングに Async<T> を䜿甚する

Operationを返す T ずいう名前の察応する同期操䜜がある堎合は、非同期操䜜が AsyncOperation を返す堎合は Async<T>、OperationAsyncを返す堎合は Task<T> する必芁がありたす。 Begin/End メ゜ッドを公開する䞀般的に䜿甚される .NET 型の堎合は、Async.FromBeginEnd を䜿甚しお拡匵メ゜ッドをファサヌドずしお蚘述し、それらの .NET API に F# 非同期プログラミング モデルを提䟛するこずを怜蚎しおください。

type SomeType =
    member this.Compute(x:int): int =
        ...
    member this.AsyncCompute(x:int): Async<int> =
        ...

type System.ServiceModel.Channels.IInputChannel with
    member this.AsyncReceive() =
        ...

䟋倖

䟋倖、結果、およびオプションの適切な䜿甚方法に぀いおは、「゚ラヌ管理の」を参照しおください。

拡匵メンバヌ

F# 同士のコンポヌネントぞの F# 拡匵メンバヌの適甚は慎重にする

F# 拡匵メンバヌは、通垞、その䜿甚モヌドの倧郚分で型に関連付けられおいる組み蟌み操䜜の終了䞭の操䜜にのみ䜿甚する必芁がありたす。 䞀般的な甚途の 1 ぀は、さたざたな .NET 型に察しお F# に慣甚的な API を提䟛する方法です。

type System.ServiceModel.Channels.IInputChannel with
    member this.AsyncReceive() =
        Async.FromBeginEnd(this.BeginReceive, this.EndReceive)

type System.Collections.Generic.IDictionary<'Key,'Value> with
    member this.TryGet key =
        let ok, v = this.TryGetValue key
        if ok then Some v else None

共甚䜓型

ツリヌ構造デヌタのクラス階局の代わりに刀別共甚䜓を䜿甚する

ツリヌのような構造䜓は再垰的に定矩されたす。 これは継承には䞍䟿ですが、刀別共甚䜓には掗緎されおいたす。

type BST<'T> =
    | Empty
    | Node of 'T * BST<'T> * BST<'T>

ディスクリミネヌティッドナニオンを䜿甚しお朚構造のデヌタを衚珟するず、パタヌンマッチングで網矅性のメリットが埗られたす。

ケヌス名が十分に䞀意でない共甚䜓型には [<RequireQualifiedAccess>] を䜿甚する

刀別共甚䜓のケヌスのように、同じ名前が、別のものには最適な名前になるドメむンがあるこずに気付くこずがありたす。 [<RequireQualifiedAccess>] を䜿甚しおケヌス名を明確にするこずで、open ステヌトメントの順序に䟝存するシャドりむングによっお発生する混乱を招く゚ラヌを防ぐこずができたす。

これらの型の蚭蚈が進化する可胜性が高い堎合は、バむナリ互換 API の刀別共甚䜓の衚珟を非衚瀺にする

ナニオン型は、簡朔なプログラミングモデルを実珟するために、F#のパタヌンマッチング構文を䜿甚したす。 前述のように、これらの型の蚭蚈が進化する可胜性が高い堎合は、具䜓的なデヌタ衚珟を明らかにしないようにする必芁がありたす。

たずえば、刀別共甚䜓の衚珟は、非公開たたは内郚の宣蚀を䜿甚するか、シグネチャ ファむルを䜿甚しお隠すこずができたす。

type Union =
    private
    | CaseA of int
    | CaseB of string

刀別共甚䜓を無差別に明らかにするず、ナヌザヌ コヌドを壊すこずなくラむブラリのバヌゞョンを管理するこずが困難になる可胜性がありたす。 代わりに、1 ぀以䞊のアクティブなパタヌンを明らかにしお、型の倀に察するパタヌン マッチングを蚱可するこずを怜蚎しおください。

アクティブ パタヌンは、F# 共甚䜓型を盎接公開するこずを回避しながら、F# コンシュヌマヌにパタヌン マッチングを提䟛する別の方法を提䟛したす。

むンラむン関数ずメンバヌ制玄

暗黙的なメンバヌ制玄ず静的に解決されたゞェネリック型を持぀むンラむン関数を䜿甚しおゞェネリック数倀アルゎリズムを定矩する

算術メンバヌ制玄ず F# 比范制玄は、F# プログラミングの暙準です。 たずえば、次のコヌドを考えおみたしょう。

let inline highestCommonFactor a b =
    let rec loop a b =
        if a = LanguagePrimitives.GenericZero<_> then b
        elif a < b then loop a (b - a)
        else loop (a - b) b
    loop a b

この関数の型は次のずおりです。

val inline highestCommonFactor : ^T -> ^T -> ^T
                when ^T : (static member Zero : ^T)
                and ^T : (static member ( - ) : ^T * ^T -> ^T)
                and ^T : equality
                and ^T : comparison

これは、数孊ラむブラリのパブリック API に適した関数です。

型クラスずダック タむピングをシミュレヌトするためにメンバヌ制玄を䜿甚しない

F# メンバヌ制玄を䜿甚しお、"duck typing" をシミュレヌトできたす。 ただし、これを利甚するメンバヌは、通垞、F# 同士のラむブラリ デザむンには䜿甚しないでください。 これは、未知の暗黙的制玄たたは非暙準の暗黙的制玄に基づくラむブラリ 蚭蚈では、ナヌザヌ コヌドが柔軟性を持たなくなり、特定のフレヌムワヌク パタヌンに関連付けられおいる傟向があるためです。

さらに、この方法でメンバヌ制玄を頻繁に䜿甚するず、コンパむル時間が非垞に長くなるこずがありたす。

挔算子の定矩

カスタム シンボリック挔算子の定矩を避ける

カスタム挔算子は状況によっおは䞍可欠であり、実装コヌドの倧芏暡な本文内で非垞に䟿利な衚蚘デバむスです。 ラむブラリの新しいナヌザヌの堎合、名前付き関数の方が䜿いやすいこずがよくありたす。 さらに、カスタムシンボリック挔算子は文曞化するのが難しい堎合があり、IDE ず怜玢゚ンゞンの既存の制限により、ナヌザヌは挔算子のヘルプを怜玢するのが難しくなりたす。

その結果、機胜を名前付き関数およびメンバヌずしお公開し、さらに、衚蚘䞊の利点がそれらを持぀こずのドキュメントずコグニティブ コストを䞊回る堎合にのみ、この機胜の挔算子を公開するこずをお勧めしたす。

枬定単䜍

F# コヌドにおける型の安党性を高めるために、単䜍を慎重に䜿甚する

他の .NET 蚀語で衚瀺するず、枬定単䜍に関する远加の入力情報は消去されたす。 .NET コンポヌネント、ツヌル、およびリフレクションでは、単䜍なしの型が衚瀺されるこずに泚意しおください。 たずえば、C# コンシュヌマヌには、floatではなく float<kg> が衚瀺されたす。

型略称

型の省略圢を慎重に䜿甚しお F# コヌドを簡略化する

.NET コンポヌネント、ツヌル、リフレクションには、型の省略名は衚瀺されたせん。 型の省略圢を倧幅に䜿甚するず、ドメむンが実際よりも耇雑に芋える可胜性があり、コンシュヌマヌを混乱させる可胜性がありたす。

メンバヌずプロパティが、省略されおいる型で䜿甚できるものず本質的に異なる堎合、パブリック型には型の省略圢を䜿甚しない

この堎合、省略される型は、定矩されおいる実際の型の衚珟に関しおあたりにも倚くを明らかにしたす。 代わりに、省略圢をクラス型たたは単䞀ケヌスの刀別共甚䜓でラップするこずを怜蚎しおください (たたは、パフォヌマンスが䞍可欠な堎合は、構造䜓型を䜿甚しお省略圢をラップするこずを怜蚎しおください)。

たずえば、F# マップの特殊なケヌスずしおマルチマップを定矩したくなる堎合がありたす。次に䟋を瀺したす。

type MultiMap<'Key,'Value> = Map<'Key,'Value list>

ただし、この型に察する論理ドット衚蚘挔算は、Map での操䜜ず同じではありたせん。たずえば、キヌがディクショナリにない堎合は、ルックアップ挔算子 map[key] 空のリストを返すのが劥圓です。䟋倖を発生させるのではなくしたす。

他の .NET 蚀語から䜿甚するためのラむブラリのガむドラむン

他の .NET 蚀語から䜿甚するラむブラリを蚭蚈する堎合は、.NET ラむブラリ蚭蚈ガむドラむンに埓うこずが重芁です。 このドキュメントでは、これらのラむブラリは、F# コンストラクトを制限なしで䜿甚する F# 向けラむブラリずは察照的に、バニラ .NET ラむブラリずしおラベル付けされおいたす。 バニラ .NET ラむブラリを蚭蚈するこずは、パブリック API での F# 固有のコンストラクトの䜿甚を最小限に抑えるこずで、.NET Framework の残りの郚分ず䞀貫性のある䜿い慣れた慣甚 API を提䟛するこずを意味したす。 芏則に぀いおは、次のセクションで説明したす。

名前空間ず型の蚭蚈 (他の .NET 蚀語から䜿甚するラむブラリ甚)

コンポヌネントのパブリック API に .NET 名前付け芏則を適甚する

省略名ず .NET の倧文字ず小文字のガむドラむンの䜿甚に特に泚意しおください。

type pCoord = ...
    member this.theta = ...

type PolarCoordinate = ...
    member this.Theta = ...

コンポヌネントの䞻芁な組織構造ずしお名前空間、型、およびメンバヌを䜿甚する

パブリック機胜を含むすべおのファむルは namespace 宣蚀で始たる必芁があり、名前空間内の公開゚ンティティは型のみである必芁がありたす。 F# モゞュヌルは䜿甚しないでください。

非パブリック モゞュヌルを䜿甚しお、実装コヌド、ナヌティリティ型、およびナヌティリティ関数を保持したす。

静的型はモゞュヌルよりも優先する必芁がありたす。これにより、API の将来の進化によっお、F# モゞュヌル内では䜿甚できない可胜性のあるオヌバヌロヌドやその他の .NET API 蚭蚈抂念を䜿甚できるようになりたす。

たずえば、次のパブリック API の代わりに、

module Fabrikam

module Utilities =
    let Name = "Bob"
    let Add2 x y = x + y
    let Add3 x y z = x + y + z

代わりに次のこずを怜蚎しおください。

namespace Fabrikam

[<AbstractClass; Sealed>]
type Utilities =
    static member Name = "Bob"
    static member Add(x,y) = x + y
    static member Add(x,y,z) = x + y + z

型の蚭蚈が進化しない堎合は、バニラ .NET API で F# レコヌド型を䜿甚する

F# レコヌド型は、単玔な .NET クラスにコンパむルされたす。 これらは、API の䞀郚の単玔で安定した型に適しおいたす。 [<NoEquality>] 属性ず [<NoComparison>] 属性を䜿甚しお、むンタヌフェむスの自動生成を抑制するこずを怜蚎しおください。 たた、パブリック フィヌルドが公開されるため、バニラ .NET API で倉曎可胜なレコヌド フィヌルドを䜿甚するこずは避けおください。 クラスが API の将来の進化に察しおより柔軟なオプションを提䟛するかどうかを垞に怜蚎しおください。

たずえば、次の F# コヌドは、パブリック API を C# コンシュヌマヌに公開したす。

F#:

[<NoEquality; NoComparison>]
type MyRecord =
    { FirstThing: int
        SecondThing: string }

C#:

public sealed class MyRecord
{
    public MyRecord(int firstThing, string secondThing);
    public int FirstThing { get; }
    public string SecondThing { get; }
}

暙準の .NET API で F# 共甚䜓の衚珟を非衚瀺にする

F# 共甚䜓の型は、F#から F# ぞのコヌディングの堎合でも、コンポヌネントの境界を越えお䞀般的に䜿甚されたせん。 これらは、コンポヌネントずラむブラリ内で内郚的に䜿甚する堎合に優れた実装デバむスです。

バニラ .NET API を蚭蚈する堎合は、プラむベヌト宣蚀たたは眲名ファむルを䜿甚しお、共甚䜓の型の衚珟を非衚瀺にするこずを怜蚎しおください。

type PropLogic =
    private
    | And of PropLogic * PropLogic
    | Not of PropLogic
    | True

たた、内郚で共甚䜓衚珟を䜿甚しおいる型をメンバヌを䜿甚しお拡匵しお、目的の .NET 察応 API を甚意するこずもできたす。

type PropLogic =
    private
    | And of PropLogic * PropLogic
    | Not of PropLogic
    | True

    /// A public member for use from C#
    member x.Evaluate =
        match x with
        | And(a,b) -> a.Evaluate && b.Evaluate
        | Not a -> not a.Evaluate
        | True -> true

    /// A public member for use from C#
    static member CreateAnd(a,b) = And(a,b)

フレヌムワヌクの蚭蚈パタヌンを䜿甚しお GUI やその他のコンポヌネントを蚭蚈する

.NET には、WinForms、WPF、ASP.NET など、さたざたなフレヌムワヌクがありたす。 これらのフレヌムワヌクで䜿甚するコンポヌネントを蚭蚈する堎合は、それぞれの名前付け芏則ず蚭蚈芏則を䜿甚する必芁がありたす。 たずえば、WPF プログラミングの堎合は、蚭蚈するクラスに WPF デザむン パタヌンを採甚したす。 ナヌザヌ むンタヌフェむス プログラミングのモデルの堎合は、むベントや通知ベヌスのコレクションなどの蚭蚈パタヌン (System.Collections.ObjectModelで芋぀かったものなど) を䜿甚したす。

オブゞェクトずメンバヌのデザむン (他の .NET 蚀語から䜿甚するラむブラリ甚)

CLIEvent 属性を䜿甚しお .NET むベントを公開する

オブゞェクトず DelegateEvent を受け取る特定の .NET デリゲヌト型 (既定で EventArgs 型のみを䜿甚する Eventではなく) を䜿甚しお FSharpHandler を構築し、むベントが他の .NET 蚀語に察しお䜿い慣れた方法で発行されるようにしたす。

type MyBadType() =
    let myEv = new Event<int>()

    [<CLIEvent>]
    member this.MyEvent = myEv.Publish

type MyEventArgs(x: int) =
    inherit System.EventArgs()
    member this.X = x

    /// A type in a component designed for use from other .NET languages
type MyGoodType() =
    let myEv = new DelegateEvent<EventHandler<MyEventArgs>>()

    [<CLIEvent>]
    member this.MyEvent = myEv.Publish

.NET タスクを返すメ゜ッドずしお非同期操䜜を公開する

タスクは、アクティブな非同期蚈算を衚すために .NET で䜿甚されたす。 䞀般に、タスクは F# Async<T> オブゞェクトよりもコンポゞションが少なくなりたす。これは、"既に実行されおいる" タスクを衚し、䞊列コンポゞションを実行する方法や、キャンセルシグナルやその他のコンテキスト パラメヌタヌの䌝達を隠す方法では䞀緒に構成できないためです。

ただし、これに関わらず、Tasks を返すメ゜ッドは、.NET での非同期プログラミングの暙準的な衚珟です。

/// A type in a component designed for use from other .NET languages
type MyType() =

    let compute (x: int): Async<int> = async { ... }

    member this.ComputeAsync(x) = compute x |> Async.StartAsTask

たた、明瀺的なキャンセル トヌクンを受け入れるこずも頻繁に必芁になりたす。

/// A type in a component designed for use from other .NET languages
type MyType() =
    let compute(x: int): Async<int> = async { ... }
    member this.ComputeAsTask(x, cancellationToken) = Async.StartAsTask(compute x, cancellationToken)

F# 関数型の代わりに .NET デリゲヌト型を䜿甚する

ここでは、"F# 関数型" は、int -> intのような "矢印" 型を意味したす。

代わりに、次の操䜜を行いたす。

member this.Transform(f: int->int) =
    ...

これを行いたす。

member this.Transform(f: Func<int,int>) =
    ...

F# 関数の型は、他の .NET 蚀語に class FSharpFunc<T,U> ずしお衚瀺され、デリゲヌト型を理解する蚀語機胜やツヌルには適しおいなくなりたす。 .NET Framework 3.5 以降を察象ずする䞊䜍のメ゜ッドを䜜成する堎合、.NET 開発者がこれらの API を䜎摩擊で䜿甚できるようにするために、System.Func および System.Action デリゲヌトが発行に適した API です。 (.NET Framework 2.0 を察象ずする堎合、システム定矩デリゲヌト型の方が制限されたす。System.Converter<T,U> や特定のデリゲヌト型の定矩など、定矩枈みのデリゲヌト型の䜿甚を怜蚎しおください)。

逆に、.NET デリゲヌトは、F# 向けラむブラリでは自然ではありたせん (F# に接続するラむブラリの次のセクションを参照しおください)。 その結果、バニラ .NET ラむブラリの䞊䜍のメ゜ッドを開発する堎合の䞀般的な実装戊略は、F# 関数型を䜿甚しおすべおの実装を䜜成し、実際の F# 実装の䞊にデリゲヌトを薄いファサヌドずしお䜿甚しおパブリック API を䜜成するこずです。

F# オプション倀を返す代わりに TryGetValue パタヌンを䜿甚し、F# オプション倀を匕数ずしお受け取るよりもメ゜ッドのオヌバヌロヌドを優先したす

API の F# オプション型に察する䞀般的な䜿甚パタヌンは、暙準の .NET 蚭蚈手法を䜿甚しお、バニラ .NET API に実装するこずをお勧めしたす。 F# オプション倀を返す代わりに、「TryGetValue」パタヌンのように、bool 型の戻り倀ず out パラメヌタヌを䜿甚するこずを怜蚎しおください。 たた、F# オプション倀をパラメヌタヌずしお受け取る代わりに、メ゜ッドのオヌバヌロヌドたたは省略可胜な匕数の䜿甚を怜蚎しおください。

member this.ReturnOption() = Some 3

member this.ReturnBoolAndOut(outVal: byref<int>) =
    outVal <- 3
    true

member this.ParamOption(x: int, y: int option) =
    match y with
    | Some y2 -> x + y2
    | None -> x

member this.ParamOverload(x: int) = x

member this.ParamOverload(x: int, y: int) = x + y

パラメヌタヌず戻り倀の .NET コレクション むンタヌフェむス型 IEnumerable<T> および IDictionary<Key、Value> を䜿甚する

.NET 配列 T[]、F# 型 list<T>、Map<Key,Value>、および Set<T>、たた Dictionary<Key,Value>などの .NET 具象コレクション型の䜿甚は避けおください。 .NET ラむブラリの蚭蚈ガむドラむンには、IEnumerable<T>などのさたざたなコレクション型を䜿甚するタむミングに関する適切なアドバむスがありたす。 パフォヌマンス䞊の理由から、状況によっおは配列 (T[]) の䜿甚が蚱容される堎合がありたす。 特に、seq<T> は IEnumerable<T>の F# ゚むリアスに過ぎないため、倚くの堎合、seq はバニラ .NET API に適した型であるこずに泚意しおください。

掚奚されない F# の䞀芧の䟋:

member this.PrintNames(names: string list) =
    ...

F# シヌケンスを䜿甚したす。

member this.PrintNames(names: seq<string>) =
    ...

れロ匕数メ゜ッドを定矩するメ゜ッドの唯䞀の入力型ずしお、たたは void を返すメ゜ッドを定矩する唯䞀の戻り倀の型ずしお、単䜍型を䜿甚したす。

ナニット型の他の䜿甚は避けおください。 これらは適切です。

✔ member this.NoArguments() = 3

✔ member this.ReturnVoid(x: int) = ()

これは悪いです:

member this.WrongUnit( x: unit, z: int) = ((), ())

バニラ .NET API の境界で null 倀を確認する

F# の実装コヌドでは、倉曎できない蚭蚈パタヌンず F# 型の null リテラルの䜿甚に関する制限があるため、null 倀が少なくなる傟向がありたす。 他の .NET 蚀語では、倚くの堎合、倀ずしお null を䜿甚する頻床が高くなりたす。 このため、バニラ .NET API を公開しおいる F# コヌドでは、API の境界でパラメヌタヌに null があるかどうかを確認し、これらの倀が F# 実装コヌドに深く入らないようにする必芁がありたす。 isNull パタヌンの null 関数たたはパタヌン マッチングを䜿甚できたす。

let checkNonNull argName (arg: obj) =
    match arg with
    | null -> nullArg argName
    | _ -> ()

let checkNonNull' argName (arg: obj) =
    if isNull arg then nullArg argName
    else ()

F# 9 以降では、新しい | null構文 を利甚しお、コンパむラが可胜な null 倀ず、凊理が必芁な堎所を瀺すこずができたす。

let checkNonNull argName (arg: obj | null) =
    match arg with
    | null -> nullArg argName
    | _ -> ()

let checkNonNull' argName (arg: obj | null) =
    if isNull arg then nullArg argName 
    else ()

F# 9 では、可胜性のある null 倀が凊理されないこずが怜出されるず、コンパむラによっお譊告が出力されたす。

let printLineLength (s: string) =
    printfn "%i" s.Length

let readLineFromStream (sr: System.IO.StreamReader) =
    // `ReadLine` may return null here - when the stream is finished
    let line = sr.ReadLine()
    // nullness warning: The types 'string' and 'string | null'
    // do not have equivalent nullability
    printLineLength line

これらの譊告には、マッチングで F# の null パタヌンを䜿甚しお察凊する必芁がありたす。

let printLineLength (s: string) =
    printfn "%i" s.Length

let readLineFromStream (sr: System.IO.StreamReader) =
    let line = sr.ReadLine()
    match line with
    | null -> ()
    | s -> printLineLength s

戻り倀ずしおタプルを䜿甚しないようにする

代わりに、集蚈デヌタを保持する名前付き型を返すか、out パラメヌタヌを䜿甚しお耇数の倀を返したす。 タプルず構造䜓タプルは .NET に存圚したす (構造䜓タプルの C# 蚀語サポヌトを含む)、ほずんどの堎合、.NET 開発者にずっお理想的で期埅される API は提䟛されたせん。

パラメヌタヌのカリヌ化を䜿甚しない

代わりに、Method(arg1,arg2,
,argN).NET 呌び出し芏則を䜿甚したす。

member this.TupledArguments(str, num) = String.replicate num str

ヒント: 任意の .NET 蚀語から䜿甚するラむブラリを蚭蚈しおいる堎合は、実際に実隓甚の C# ず Visual Basic プログラミングを行っお、ラむブラリがこれらの蚀語から "正しいず感じる" ようにする代わりにはありたせん。 .NET Reflector や Visual Studio オブゞェクト ブラりザヌなどのツヌルを䜿甚しお、ラむブラリずそのドキュメントが開発者に期埅どおりに衚瀺されるようにするこずもできたす。

付録

他の .NET 蚀語で䜿甚する F# コヌドを蚭蚈する゚ンド ツヌ ゚ンドの䟋

次のクラスに぀いお考えおみたしょう。

open System

type Point1(angle,radius) =
    new() = Point1(angle=0.0, radius=0.0)
    member x.Angle = angle
    member x.Radius = radius
    member x.Stretch(l) = Point1(angle=x.Angle, radius=x.Radius * l)
    member x.Warp(f) = Point1(angle=f(x.Angle), radius=x.Radius)
    static member Circle(n) =
        [ for i in 1..n -> Point1(angle=2.0*Math.PI/float(n), radius=1.0) ]

このクラスの掚論された F# 型は次のずおりです。

type Point1 =
    new : unit -> Point1
    new : angle:double * radius:double -> Point1
    static member Circle : n:int -> Point1 list
    member Stretch : l:double -> Point1
    member Warp : f:(double -> double) -> Point1
    member Angle : double
    member Radius : double

別の .NET 蚀語を䜿甚しお、この F# 型がプログラマにどのように衚瀺されるかを芋おみたしょう。 たずえば、C# のおおよその "シグネチャ" は次のようになりたす。

// C# signature for the unadjusted Point1 class
public class Point1
{
    public Point1();

    public Point1(double angle, double radius);

    public static Microsoft.FSharp.Collections.List<Point1> Circle(int count);

    public Point1 Stretch(double factor);

    public Point1 Warp(Microsoft.FSharp.Core.FastFunc<double,double> transform);

    public double Angle { get; }

    public double Radius { get; }
}

ここでは、F# がコンストラクトをどのように衚しおいるかに぀いお泚意すべき重芁な点がいく぀かありたす。 䟋えば

  • 匕数名などのメタデヌタは保持されおいたす。

  • 2 ぀の匕数を受け取る F# メ゜ッドは、2 ぀の匕数を受け取る C# メ゜ッドになりたす。

  • 関数ずリストは、F# ラむブラリ内の察応する型ぞの参照になりたす。

次のコヌドは、これらのこずを考慮するようにこのコヌドを調敎する方法を瀺しおいたす。

namespace SuperDuperFSharpLibrary.Types

type RadialPoint(angle:double, radius:double) =

    /// Return a point at the origin
    new() = RadialPoint(angle=0.0, radius=0.0)

    /// The angle to the point, from the x-axis
    member x.Angle = angle

    /// The distance to the point, from the origin
    member x.Radius = radius

    /// Return a new point, with radius multiplied by the given factor
    member x.Stretch(factor) =
        RadialPoint(angle=angle, radius=radius * factor)

    /// Return a new point, with angle transformed by the function
    member x.Warp(transform:Func<_,_>) =
        RadialPoint(angle=transform.Invoke angle, radius=radius)

    /// Return a sequence of points describing an approximate circle using
    /// the given count of points
    static member Circle(count) =
        seq { for i in 1..count ->
                RadialPoint(angle=2.0*Math.PI/float(count), radius=1.0) }

掚定されるコヌドの F# 型は次のずおりです。

type RadialPoint =
    new : unit -> RadialPoint
    new : angle:double * radius:double -> RadialPoint
    static member Circle : count:int -> seq<RadialPoint>
    member Stretch : factor:double -> RadialPoint
    member Warp : transform:System.Func<double,double> -> RadialPoint
    member Angle : double
    member Radius : double

C# シグネチャは次のようになりたす。

public class RadialPoint
{
    public RadialPoint();

    public RadialPoint(double angle, double radius);

    public static System.Collections.Generic.IEnumerable<RadialPoint> Circle(int count);

    public RadialPoint Stretch(double factor);

    public RadialPoint Warp(System.Func<double,double> transform);

    public double Angle { get; }

    public double Radius { get; }
}

バニラ .NET ラむブラリの䞀郚ずしお䜿甚するためにこの型を準備するために行われた修正は次のずおりです。

  • いく぀かの名前を調敎したした:Point1、n、l、および f はそれぞれ RadialPoint、count、factor、および transformになりたした。

  • seq<RadialPoint> を䜿ったリスト構築を RadialPoint listを䜿ったシヌケンス構築に倉曎するこずで、[ ... ] ではなく IEnumerable<RadialPoint> の戻り倀の型を甚いたした。

  • F# 関数型の代わりに System.Func .NET デリゲヌト型を䜿甚したした。

C# コヌドでの䜿甚がはるかに䜿いやすくなりたす。