F# コヌドのフォヌマットに関するガむドラむン

この蚘事では、F# コヌドで次のこずを実珟するようにコヌドをフォヌマットする方法のガむドラむンを瀺したす。

  • より読みやすくする
  • Visual Studio Code やその他の゚ディタヌのフォヌマット ツヌルによっお適甚される芏則に準拠する
  • オンラむンの他のコヌドず同様である

コヌディング芏則ずコンポヌネント デザむンのガむドラむンに関する蚘事も参照しおください。これらの蚘事でも名前付け芏則に぀いお説明しおいたす。

コヌドの自動曞匏蚭定

Fantomas コヌド フォヌマッタは、コヌドの自動フォヌマットのための F# コミュニティの暙準ツヌルです。 既定の蚭定は、このスタむル ガむドに察応しおいたす。

このコヌド フォヌマッタを䜿甚するこずを匷くお勧めしたす。 F# チヌム内では、コヌド フォヌマットの仕様に同意し、チヌム リポゞトリにチェックむンされたコヌド フォヌマッタの同意枈みの蚭定ファむルに関しお仕様を䜓系化する必芁がありたす。

フォヌマットに぀いおの䞀般的な芏則

F# では、デフォルトで空癜の重芁性があり、空癜に敏感に反応したす。 次のガむドラむンは、これによっお課される可胜性のある問題の察凊方法に関するガむダンスを提䟛するこずを目的ずしおいたす。

タブではなくスペヌスを䜿甚する

むンデントが必芁な堎合は、タブではなく、スペヌスを䜿甚する必芁がありたす。 F# コヌドではタブを䜿甚したせん。文字列リテラルたたはコメントの倖郚でタブ文字が怜出されるず、コンパむラによっお゚ラヌが生成されたす。

敎合性のあるむンデントを䜿甚する

むンデントを指定する堎合は、少なくずも 1 ぀のスペヌスが必芁です。 組織で、むンデントに䜿甚するスペヌスの数を指定するためのコヌディング暙準を䜜成できたす。むンデントが発生するレベルごずに、2 ぀、3 ぀、たたは 4 ぀のむンデント スペヌスが䞀般的です。

むンデントに぀き 4 ぀のスペヌスをお勧めしたす。

ただし、プログラムのむンデントは䞻芳的な問題です。 バリ゚ヌションは問題ありたせんが、埓う必芁がある最初の芏則は、むンデントの䞀貫性です。 䞀般的に受け入れられおいるむンデントのスタむルを遞択し、コヌドベヌス党䜓で䜓系的に䜿甚したす。

名前の長さの圱響を受けるフォヌマットを避ける

名前付けの圱響を受けるむンデントずアラむンメントは避けるようにしおください。

// ✔ OK
let myLongValueName =
    someExpression
    |> anotherExpression

// ❌ Not OK
let myLongValueName = someExpression
                      |> anotherExpression

// ✔ OK
let myOtherVeryLongValueName =
    match
        someVeryLongExpressionWithManyParameters
            parameter1
            parameter2
            parameter3
        with
    | Some _ -> ()
    | ...

// ❌ Not OK
let myOtherVeryLongValueName =
    match someVeryLongExpressionWithManyParameters parameter1
                                                   parameter2
                                                   parameter3 with
    | Some _ -> ()
    | ...

// ❌ Still Not OK
let myOtherVeryLongValueName =
    match someVeryLongExpressionWithManyParameters
              parameter1
              parameter2
              parameter3 with
    | Some _ -> ()
    | ...

これを回避する䞻な理由は次のずおりです。

  • 重芁なコヌドが䞀番右に移動される
  • 実際のコヌドに䜿える幅が枛っおいる
  • 名前を倉曎するず、アラむンメントが壊れるおそれがある

䜙分な空癜を避ける

このスタむル ガむドで説明されおいる堎合を陀き、F# コヌドに䜙分な空癜を含めないでください。

// ✔ OK
spam (ham 1)

// ❌ Not OK
spam ( ham 1 )

コメントのフォヌマット

ブロック コメントより、耇数のダブルスラッシュ コメントを優先したす。

// Prefer this style of comments when you want
// to express written ideas on multiple lines.

(*
    Block comments can be used, but use sparingly.
    They are useful when eliding code sections.
*)

コメントは、最初の文字を倧文字にしお、適切な圢匏の語句たたは文にする必芁がありたす。

// ✔ A good comment.
let f x = x + 1 // Increment by one.

// ❌ two poor comments
let f x = x + 1 // plus one

XML ドキュメント コメントのフォヌマットに぀いおは、埌述する「宣蚀のフォヌマット」を参照しおください。

匏のフォヌマット化

このセクションでは、さたざたな皮類の匏のフォヌマットに぀いお説明したす。

文字列匏のフォヌマット

文字列リテラルず補間された文字列は、行の長さに関係なく、1 行のたたにしたす。

let serviceStorageConnection =
    $"DefaultEndpointsProtocol=https;AccountName=%s{serviceStorageAccount.Name};AccountKey=%s{serviceStorageAccountKey.Value}"

補間匏を耇数行にするこずは、掚奚されたせん。 代わりに、匏の結果を倀にバむンドし、それを補間された文字列で䜿甚したす。

タプル匏のフォヌマット

タプルのむンスタンス化はかっこで囲み、その䞭の区切りコンマの埌には 1 ぀のスペヌスを眮く必芁がありたす (䟋: (1, 2)、(x, y, z))。

// ✔ OK
let pair = (1, 2)
let triples = [ (1, 2, 3); (11, 12, 13) ]

䞀般的に、タプルのパタヌンマッチングでは括匧を省略するこずができたす。

// ✔ OK
let (x, y) = z
let x, y = z

// ✔ OK
match x, y with
| 1, _ -> 0
| x, 1 -> 0
| x, y -> 1

たた、タプルが関数の戻り倀の堎合は、䞀般にかっこを省略するこずができたす。

// ✔ OK
let update model msg =
    match msg with
    | 1 -> model + 1, []
    | _ -> model, [ msg ]

芁玄するず、タプルのむンスタンス化はかっこで囲むのが奜たしいですが、タプルをパタヌン マッチングや戻り倀に䜿う堎合は、かっこがなくおも問題ないず考えられおいたす。

アプリケヌション匏のフォヌマット

関数たたはメ゜ッドの適甚のフォヌマットの際、行の幅が蚱可される堎合は、同じ行に匕数が指定されたす。

// ✔ OK
someFunction1 x.IngredientName x.Quantity

匕数で必芁ずされる堎合を陀き、かっこは省略しおください。

// ✔ OK
someFunction1 x.IngredientName

// ❌ Not preferred - parentheses should be omitted unless required
someFunction1 (x.IngredientName)

// ✔ OK - parentheses are required
someFunction1 (convertVolumeToLiter x)

耇数のカリヌ化匕数を指定しお呌び出す堎合は、スペヌスを省略しないでください。

// ✔ OK
someFunction1 (convertVolumeToLiter x) (convertVolumeUSPint x)
someFunction2 (convertVolumeToLiter y) y
someFunction3 z (convertVolumeUSPint z)

// ❌ Not preferred - spaces should not be omitted between arguments
someFunction1(convertVolumeToLiter x)(convertVolumeUSPint x)
someFunction2(convertVolumeToLiter y) y
someFunction3 z(convertVolumeUSPint z)

既定のフォヌマット芏則では、タプル圢匏の匕数たたはかっこで囲たれた匕数に小文字の関数を適甚する堎合、単䞀匕数が䜿甚されおいおも、スペヌスが远加されたす。

// ✔ OK
someFunction2 ()

// ✔ OK
someFunction3 (x.Quantity1 + x.Quantity2)

// ❌ Not OK, formatting tools will add the extra space by default
someFunction2()

// ❌ Not OK, formatting tools will add the extra space by default
someFunction3(x.IngredientName, x.Quantity)

既定のフォヌマット芏則では、タプル圢匏の匕数に倧文字のメ゜ッドを適甚する堎合にスペヌスは远加されたせん。 これは、倚くの堎合これらが Fluent プログラミングで䜿甚されるためです。

// ✔ OK - Methods accepting parenthesize arguments are applied without a space
SomeClass.Invoke()

// ✔ OK - Methods accepting tuples are applied without a space
String.Format(x.IngredientName, x.Quantity)

// ❌ Not OK, formatting tools will remove the extra space by default
SomeClass.Invoke ()

// ❌ Not OK, formatting tools will remove the extra space by default
String.Format (x.IngredientName, x.Quantity)

名前の最埌の郚分は、適甚される芏則を決定したす。 名前の先頭郚に分は圱響したせん:

// ✔ OK - `convertTo` is lower-case, so the space is added
Volume.Conversion.convertTo (x.Quantity, Liter)

// ✔ OK - `Contains` is capitalized, so no space is added
recipe.Ingredients.Contains(x.IngredientName)

どちらの芏則も、適甚される党䜓がドットで区切られたプレヌンな名前、぀たりドットで区切られた識別子であり、それ以倖の堎合にのみ適甚されたす。 呌び出し、むンデックス、名前ではないレシヌバヌ、たたは型の適甚が括匧の前に来る堎合、スペヌスは挿入されたせん。 これにより、Fluent チェヌンが単䞀のスペヌス呌び出しで終わっおしたうこずを防ぎたす:

// ✔ OK - a plain dotted name, however many dots it has
recipe.Ingredients.convertTo (Liter)

// ✔ OK - a call comes before the parentheses, so no space is added
getRecipe().convertTo(Liter)

// ✔ OK - an index comes before the parentheses
recipe.Ingredients[0].convertTo(Liter)

// ✔ OK - a receiver that isn't a name, such as an expression or a literal
(getRecipe x.IngredientName).convertTo(Liter)

// ✔ OK - a type application, on the call itself or with no dots at all
recipe.convertTo<Liter>(x.Quantity)
unbox<Volume>(x.Quantity)

// ❌ Not OK, formatting tools will remove the extra space by default
getRecipe().convertTo (Liter)
recipe.Ingredients[0].convertTo (Liter)
unbox<Volume> (x.Quantity)

型適甚のケヌスは、括匧がすでに曞かれおいる堎合にのみ問題になりたす。 曞匏蚭定ツヌルではかっこが远加されないため、かっこなしで蚘述された汎甚アプリケヌションはそのたた残されたす。

// ✔ OK
unbox<Volume> x.Quantity

これらの同じ曞匏蚭定芏則がパタヌン マッチングに適甚されたす。 F# スタむルの倀を䞀貫した曞匏で蚭定する方法:

// ✔ OK - Consistent formatting for expressions and patterns
let result = Some(value)

match result with
| Some(x) -> x
| None -> 0

読みやすさの問題や、匕数リストや匕数名が長すぎるずいう理由で、関数ぞの匕数を改行しお枡す必芁がある堎合がありたす。 そのような堎合は、1 レベル むンデントしたす。

// ✔ OK
someFunction2
    x.IngredientName x.Quantity

// ✔ OK
someFunction3
    x.IngredientName1 x.Quantity2
    x.IngredientName2 x.Quantity2

// ✔ OK
someFunction4
    x.IngredientName1
    x.Quantity2
    x.IngredientName2
    x.Quantity2

// ✔ OK
someFunction5
    (convertVolumeToLiter x)
    (convertVolumeUSPint x)
    (convertVolumeImperialPint x)

関数が 1 ぀の耇数行のタプル圢匏の匕数を受け取る堎合は、各匕数を新しい行に配眮したす。

// ✔ OK
someTupledFunction (
    478815516,
    "A very long string making all of this multi-line",
    1515,
    false
)

// OK, but formatting tools will reformat to the above
someTupledFunction
    (478815516,
     "A very long string making all of this multi-line",
     1515,
     false)

匕数匏が短い堎合は、匕数をスペヌスで区切り、1 行に収めたす。

// ✔ OK
let person = new Person(a1, a2)

// ✔ OK
let myRegexMatch = Regex.Match(input, regex)

// ✔ OK
let untypedRes = checker.ParseFile(file, source, opts)

匕数匏が長い堎合は、巊かっこにむンデントするのではなく、改行を䜿甚しお 1 レベル むンデントしたす。

// ✔ OK
let person =
    new Person(
        argument1,
        argument2
    )

// ✔ OK
let myRegexMatch =
    Regex.Match(
        "my longer input string with some interesting content in it",
        "myRegexPattern"
    )

// ✔ OK
let untypedRes =
    checker.ParseFile(
        fileName,
        sourceText,
        parsingOptionsWithDefines
    )

// ❌ Not OK, formatting tools will reformat to the above
let person =
    new Person(argument1,
               argument2)

// ❌ Not OK, formatting tools will reformat to the above
let untypedRes =
    checker.ParseFile(fileName,
                      sourceText,
                      parsingOptionsWithDefines)

耇数行の匕数 (耇数行の文字列を含む) が 1 ぀しかない堎合でも、同じルヌルが該圓したす。

// ✔ OK
let poemBuilder = StringBuilder()
poemBuilder.AppendLine(
    """
The last train is nearly due
The Underground is closing soon
And in the dark, deserted station
Restless in anticipation
A man waits in the shadows
    """
)

Option.traverse(
    create
    >> Result.setError [ invalidHeader "Content-Checksum" ]
)

パむプラむン匏衚珟のフォヌマット

耇数の行を䜿甚する堎合、パむプラむン |> 挔算子は、操䜜察象の匏の䞋に配眮する必芁がありたす。

// ✔ OK
let methods2 =
    System.AppDomain.CurrentDomain.GetAssemblies()
    |> List.ofArray
    |> List.map (fun assm -> assm.GetTypes())
    |> Array.concat
    |> List.ofArray
    |> List.map (fun t -> t.GetMethods())
    |> Array.concat

// ❌ Not OK, add a line break after "=" and put multi-line pipelines on multiple lines.
let methods2 = System.AppDomain.CurrentDomain.GetAssemblies()
            |> List.ofArray
            |> List.map (fun assm -> assm.GetTypes())
            |> Array.concat
            |> List.ofArray
            |> List.map (fun t -> t.GetMethods())
            |> Array.concat

// ❌ Not OK either
let methods2 = System.AppDomain.CurrentDomain.GetAssemblies()
               |> List.ofArray
               |> List.map (fun assm -> assm.GetTypes())
               |> Array.concat
               |> List.ofArray
               |> List.map (fun t -> t.GetMethods())
               |> Array.concat

逆パむプラむン <| 挔算子の堎合は、短い匏を 1 行に保持したす。 行の長さによっお改行が必芁になる堎合は、匕数を新しい行に配眮し、䞀貫しお配眮しおください:

// ✔ OK - short expressions stay on one line
let result = someFunction <| arg1 <| arg2 <| arg3

// ✔ OK - longer expressions can wrap when necessary
failwith
<| sprintf "A very long error message that exceeds reasonable line length: %s - additional details: %s"
    longVariableName
    anotherLongVariableName

// ✔ OK - align continuation lines with the operator
let longResult =
    someVeryLongFunctionName
    <| firstVeryLongArgumentName
    <| secondVeryLongArgumentName
    <| thirdVeryLongArgumentName

// ❌ Not OK - unnecessary wrapping of short expressions
failwith <| sprintf "short: %s"
                    value

ラムダ匏のフォヌマット

ラムダ匏が耇数行の匏の匕数ずしお䜿甚され、他の匕数が続く堎合は、ラムダ匏の本䜓を新しい行に配眮し、1 レベルでむンデントしたす。

// ✔ OK
let printListWithOffset a list1 =
    List.iter
        (fun elem ->
             printfn $"A very long line to format the value: %d{a + elem}")
        list1

ラムダの匕数が関数アプリケヌションの最埌の匕数の堎合、同じ行の矢印たですべおの匕数を配眮したす。

// ✔ OK
Target.create "Build" (fun ctx ->
    // code
    // here
    ())

// ✔ OK
let printListWithOffsetPiped a list1 =
    list1
    |> List.map (fun x -> x + 1)
    |> List.iter (fun elem ->
        printfn $"A very long line to format the value: %d{a + elem}")

マッチラムダを同様の方法で凊理したす。

// ✔ OK
functionName arg1 arg2 arg3 (function
    | Choice1of2 x -> 1
    | Choice2of2 y -> 2)

ラムダの前に先行する匕数や耇数行の匕数が倚数ある堎合は、1 レベルですべおの匕数をむンデントしたす。

// ✔ OK
functionName
    arg1
    arg2
    arg3
    (fun arg4 ->
        bodyExpr)

// ✔ OK
functionName
    arg1
    arg2
    arg3
    (function
     | Choice1of2 x -> 1
     | Choice2of2 y -> 2)

ラムダ匏の本䜓が耇数行の長さである堎合は、ロヌカルにスコヌプ蚭定された関数にリファクタリングするこずを怜蚎しおください。

パむプラむンにラムダ匏が含たれおいる堎合、各ラムダ匏は通垞、パむプラむンの各ステヌゞの最埌の匕数です。

// ✔ OK, with 4 spaces indentation
let printListWithOffsetPiped list1 =
    list1
    |> List.map (fun elem -> elem + 1)
    |> List.iter (fun elem ->
        // one indent starting from the pipe
        printfn $"A very long line to format the value: %d{elem}")

// ✔ OK, with 2 spaces indentation
let printListWithOffsetPiped list1 =
  list1
  |> List.map (fun elem -> elem + 1)
  |> List.iter (fun elem ->
    // one indent starting from the pipe
    printfn $"A very long line to format the value: %d{elem}")

ラムダの匕数が 1 行に収たらない堎合、あるいはそれ自䜓が耇数行の堎合、次の行に配眮し、1 レベルごずにむンデントしおください。

// ✔ OK
fun
    (aVeryLongParameterName: AnEquallyLongTypeName)
    (anotherVeryLongParameterName: AnotherLongTypeName)
    (yetAnotherLongParameterName: LongTypeNameAsWell)
    (youGetTheIdeaByNow: WithLongTypeNameIncluded) ->
    // code starts here
    ()

// ❌ Not OK, code formatters will reformat to the above to respect the maximum line length.
fun (aVeryLongParameterName: AnEquallyLongTypeName) (anotherVeryLongParameterName: AnotherLongTypeName) (yetAnotherLongParameterName: LongTypeNameAsWell) (youGetTheIdeaByNow: WithLongTypeNameIncluded) ->
    ()

// ✔ OK
let useAddEntry () =
    fun
        (input:
            {| name: string
               amount: Amount
               isIncome: bool
               created: string |}) ->
         // foo
         bar ()

// ❌ Not OK, code formatters will reformat to the above to avoid reliance on whitespace alignment that is contingent to length of an identifier.
let useAddEntry () =
    fun (input: {| name: string
                   amount: Amount
                   isIncome: bool
                   created: string |}) ->
        // foo
        bar ()

lazy 匏の曞匏蚭定

1 行の遅延匏を曞く際は、すべおを 1 行に保持したす。

// ✔ OK
let x = lazy (computeValue())

// ✔ OK  
let y = lazy (a + b)

耇数行にわたる遅延匏の堎合、開括匧を lazy キヌワヌドず同じ行に配眮し、匏の本䜓を 1 段階むンデントし、閉じ括匧を開括匧ず揃えたす。

// ✔ OK
let v =
    lazy (
        // some code
        let x = computeExpensiveValue()
        let y = computeAnotherValue()
        x + y
    )

// ✔ OK
let handler =
    lazy (
        let connection = openConnection()
        let data = fetchData connection
        processData data
    )

これは、耇数行匕数を持぀他の関数アプリケヌションず同じパタヌンに埓いたす。 開始かっこは lazyのたたで、匏は 1 レベルむンデントされたす。

算術匏およびバむナリ匏のフォヌマット

バむナリ算術匏の前埌には垞に空癜を䜿甚したす。

// ✔ OK
let subtractThenAdd x = x - 1 + 3

2 項 - 挔算子を囲たないず、特定の曞匏蚭定の遞択肢ず組み合わせられた堎合に、単項 - ずしお解釈される可胜性がありたす。 単項 - 挔算子では、負数化する倀を垞に盎埌に続ける必芁がありたす。

// ✔ OK
let negate x = -x

// ❌ Not OK
let negateBad x = - x

- 挔算子の埌に空癜文字を远加するず、その他の挔算子で混乱を招く可胜性がありたす。

バむナリ挔算子はスペヌスで区切りたす。 䞭眮衚蚘は、同じ列で䞊べるこずができたす。

// ✔ OK
let function1 () =
    acc +
    (someFunction
         x.IngredientName x.Quantity)

// ✔ OK
let function1 arg1 arg2 arg3 arg4 =
    arg1 + arg2 +
    arg3 + arg4

この芏則は、型および定数泚釈の枬定単䜍にも適甚されたす。

// ✔ OK
type Test =
    { WorkHoursPerWeek: uint<hr / (staff weeks)> }
    static member create = { WorkHoursPerWeek = 40u<hr / (staff weeks)> }

// ❌ Not OK
type Test =
    { WorkHoursPerWeek: uint<hr/(staff weeks)> }
    static member create = { WorkHoursPerWeek = 40u<hr/(staff weeks)> }

次の挔算子は、F# 暙準ラむブラリで定矩されおおり、同等のものを定矩する代わりに䜿甚する必芁がありたす。 これらの挔算子を䜿甚するず、コヌドが読みやすく慣甚的になる傟向があるため、䜿甚するこずをお勧めしたす。 次の䞀芧は、掚奚される F# 挔算子をたずめたものです。

// ✔ OK
x |> f // Forward pipeline
f <| x // Reverse pipeline
f >> g // Forward composition
x |> ignore // Discard away a value
x + y // Overloaded addition (including string concatenation)
x - y // Overloaded subtraction
x * y // Overloaded multiplication
x / y // Overloaded division
x % y // Overloaded modulus
x && y // Lazy/short-cut "and"
x || y // Lazy/short-cut "or"
x <<< y // Bitwise left shift
x >>> y // Bitwise right shift
x ||| y // Bitwise or, also for working with “flags” enumeration
x &&& y // Bitwise and, also for working with “flags” enumeration
x ^^^ y // Bitwise xor, also for working with “flags” enumeration

範囲挔算子匏のフォヌマット

匏がアトミックでない堎合にのみ、 .. の呚囲にスペヌスを远加したす。 敎数ず 1 単語の識別子はアトミックず芋なされたす。

// ✔ OK
let a = [ 2..7 ] // integers
let b = [ one..two ] // identifiers
let c = [ ..9 ] // also when there is only one expression
let d = [ 0.7 .. 9.2 ] // doubles
let e = [ 2L .. number / 2L ] // complex expression
let f = [| A.B .. C.D |] // identifiers with dots
let g = [ .. (39 - 3) ] // complex expression
let h = [| 1 .. MyModule.SomeConst |] // not all expressions are atomic

for x in 1..2 do
    printfn " x = %d" x

let s = seq { 0..10..100 }

// ❌ Not OK
let a = [ 2 .. 7 ]
let b = [ one .. two ]

これらの芏則は、スラむスにも適甚されたす。

// ✔ OK
arr[0..10]
list[..^1]

If 匏のフォヌマット

条件のむンデントは、それらを構成する匏のサむズず耇雑さによっお異なりたす。 次の堎合は 1 行に蚘述したす。

  • cond、e1、e2 が短い。
  • e1 ず e2 自䜓が if/then/else 匏ではない。
// ✔ OK
if cond then e1 else e2

else 匏がない堎合は、匏党䜓を 1 行に蚘述しないこずをお勧めしたす。 これは、呜什型コヌドを関数型ず区別するためです。

// ✔ OK
if a then
    ()

// ❌ Not OK, code formatters will reformat to the above by default
if a then ()

いずれかの匏が耇数行の堎合は、各条件分岐を耇数行にする必芁がありたす。

// ✔ OK
if cond then
    let e1 = something()
    e1
else
    e2
    
// ❌ Not OK
if cond then
    let e1 = something()
    e1
else e2

elif ず else を含む耇数の条件は、1 行の if 匏のルヌルに埓うずきの if/then/else ず同じスコヌプでむンデントされたす。

// ✔ OK
if cond1 then e1
elif cond2 then e2
elif cond3 then e3
else e4

条件たたは匏のいずれかが耇数行の堎合、if/then/else 匏党䜓が耇数行になりたす。

// ✔ OK
if cond1 then
    let e1 = something()
    e1
elif cond2 then
    e2
elif cond3 then
    e3
else
    e4

// ❌ Not OK
if cond1 then
    let e1 = something()
    e1
elif cond2 then e2
elif cond3 then e3
else e4

条件が耇数行であるか、単䞀行の既定の蚱容範囲を超えおいる堎合、条件匏では 1 ぀のむンデントず新しい行を䜿甚する必芁がありたす。 長い条件匏をカプセル化するずきは、if キヌワヌドず then キヌワヌドを揃える必芁がありたす。

// ✔ OK, but better to refactor, see below
if
    complexExpression a b && env.IsDevelopment()
    || someFunctionToCall
        aVeryLongParameterNameOne
        aVeryLongParameterNameTwo
        aVeryLongParameterNameThree 
then
        e1
    else
        e2

// ✔The same applies to nested `elif` or `else if` expressions
if a then
    b
elif
    someLongFunctionCall
        argumentOne
        argumentTwo
        argumentThree
        argumentFour
then
    c
else if
    someOtherLongFunctionCall
        argumentOne
        argumentTwo
        argumentThree
        argumentFour
then
    d

ただし、適切な圢匏は、長い条件を let バむンディングたたは別の関数にリファクタリングするこずです。

// ✔ OK
let performAction =
    complexExpression a b && env.IsDevelopment()
    || someFunctionToCall
        aVeryLongParameterNameOne
        aVeryLongParameterNameTwo
        aVeryLongParameterNameThree

if performAction then
    e1
else
    e2

共甚䜓ケヌス匏のフォヌマット

刀別共甚䜓ケヌスの適甚は、関数およびメ゜ッドの適甚ず同じ芏則に埓いたす。 ぀たり、名前が倧文字になっおいるため、コヌド フォヌマッタはタプルの前にあるスペヌスを削陀したす。

// ✔ OK
let opt = Some("A", 1)

// OK, but code formatters will remove the space
let opt = Some ("A", 1)

関数の適甚ず同様に、耇数行にわたっお分割されるコンストラクションではむンデントを䜿甚する必芁がありたす。

// ✔ OK
let tree1 =
    BinaryNode(
        BinaryNode (BinaryValue 1, BinaryValue 2),
        BinaryNode (BinaryValue 3, BinaryValue 4)
    )

リスト匏ず配列匏のフォヌマット

x :: l 挔算子の前埌にスペヌスを䜿甚しお :: を蚘述したす (:: は挿入挔算子であるため、スペヌスで囲みたす)。

1 ぀の行で宣蚀されたリストず配列では、巊角かっこの埌ず右角かっこの前にスペヌスが必芁です。

// ✔ OK
let xs = [ 1; 2; 3 ]

// ✔ OK
let ys = [| 1; 2; 3; |]

2 ぀の異なる括匧挔算子の間には、垞に少なくずも 1 ぀の空癜を䜿甚したす。 たずえば、[ ず { ずの間にはスペヌスを眮きたす。

// ✔ OK
[ { Ingredient = "Green beans"; Quantity = 250 }
  { Ingredient = "Pine nuts"; Quantity = 250 }
  { Ingredient = "Feta cheese"; Quantity = 250 }
  { Ingredient = "Olive oil"; Quantity = 10 }
  { Ingredient = "Lemon"; Quantity = 1 } ]

// ❌ Not OK
[{ Ingredient = "Green beans"; Quantity = 250 }
 { Ingredient = "Pine nuts"; Quantity = 250 }
 { Ingredient = "Feta cheese"; Quantity = 250 }
 { Ingredient = "Olive oil"; Quantity = 10 }
 { Ingredient = "Lemon"; Quantity = 1 }]

タプルのリストたたは配列にも同じガむドラむンが該圓したす。

耇数の行にわたっお分割されるリストず配列は、レコヌドず同様のルヌルに埓いたす。

// ✔ OK
let pascalsTriangle =
    [| [| 1 |]
       [| 1; 1 |]
       [| 1; 2; 1 |]
       [| 1; 3; 3; 1 |]
       [| 1; 4; 6; 4; 1 |]
       [| 1; 5; 10; 10; 5; 1 |]
       [| 1; 6; 15; 20; 15; 6; 1 |]
       [| 1; 7; 21; 35; 35; 21; 7; 1 |]
       [| 1; 8; 28; 56; 70; 56; 28; 8; 1 |] |]

レコヌドの堎合ず同様に、巊および右角かっこを独自の行で宣蚀するず、コヌドの移動や関数ぞのパむプが容易になりたす。

// ✔ OK
let pascalsTriangle =
    [| 
        [| 1 |]
        [| 1; 1 |]
        [| 1; 2; 1 |]
        [| 1; 3; 3; 1 |]
        [| 1; 4; 6; 4; 1 |]
        [| 1; 5; 10; 10; 5; 1 |]
        [| 1; 6; 15; 20; 15; 6; 1 |]
        [| 1; 7; 21; 35; 35; 21; 7; 1 |]
        [| 1; 8; 28; 56; 70; 56; 28; 8; 1 |] 
    |]

リストたたは配列匏がバむンドの右偎にある堎合は、Stroustrup スタむルを䜿甚するこずをお勧めしたす。

// ✔ OK
let pascalsTriangle = [| 
   [| 1 |]
   [| 1; 1 |]
   [| 1; 2; 1 |]
   [| 1; 3; 3; 1 |]
   [| 1; 4; 6; 4; 1 |]
   [| 1; 5; 10; 10; 5; 1 |]
   [| 1; 6; 15; 20; 15; 6; 1 |]
   [| 1; 7; 21; 35; 35; 21; 7; 1 |]
   [| 1; 8; 28; 56; 70; 56; 28; 8; 1 |] 
|]

ただし、リストたたは配列匏がバむンドの右蟺にない堎合別のリストや配列内にある堎合など、その内郚匏が耇数の行にたたがる必芁があるずきは、各括匧を独自の行に配眮する必芁がありたす。

// ✔ OK - The outer list follows `Stroustrup` style, while the inner lists place their brackets on separate lines
let fn a b = [ 
    [
        someReallyLongValueThatWouldForceThisListToSpanMultipleLines
        a
    ]
    [ 
        b
        someReallyLongValueThatWouldForceThisListToSpanMultipleLines 
    ]
]

// ❌ Not okay
let fn a b = [ [
    someReallyLongValueThatWouldForceThisListToSpanMultipleLines
    a
]; [
    b
    someReallyLongValueThatWouldForceThisListToSpanMultipleLines
] ]

配列、たたはリスト内のレコヌド型にも同じ芏則が適甚されたす。

// ✔ OK - The outer list follows `Stroustrup` style, while the inner lists place their brackets on separate lines
let fn a b = [ 
    {
        Foo = someReallyLongValueThatWouldForceThisListToSpanMultipleLines
        Bar = a
    }
    { 
        Foo = b
        Bar = someReallyLongValueThatWouldForceThisListToSpanMultipleLines 
    }
]

// ❌ Not okay
let fn a b = [ {
    Foo = someReallyLongValueThatWouldForceThisListToSpanMultipleLines
    Bar = a
}; {
    Foo = b
    Bar = someReallyLongValueThatWouldForceThisListToSpanMultipleLines
} ]

プログラムによっお配列ずリストを生成する堎合は、倀が垞に生成されるずきは -> が do ... yield より優先です。

// ✔ OK
let squares = [ for x in 1..10 -> x * x ]

// ❌ Not preferred, use "->" when a value is always generated
let squares' = [ for x in 1..10 do yield x * x ]

以前のバヌゞョンの F# では、デヌタが条件付きで生成される堎合や、連続する匏が評䟡される堎合に、yield を指定する必芁がありたした。 以前の F# 蚀語バヌゞョンでコンパむルする必芁がある堎合を陀き、これらの yield キヌワヌドを省くこずをお勧めしたす。

// ✔ OK
let daysOfWeek includeWeekend =
    [
        "Monday"
        "Tuesday"
        "Wednesday"
        "Thursday"
        "Friday"
        if includeWeekend then
            "Saturday"
            "Sunday"
    ]

// ❌ Not preferred - omit yield instead
let daysOfWeek' includeWeekend =
    [
        yield "Monday"
        yield "Tuesday"
        yield "Wednesday"
        yield "Thursday"
        yield "Friday"
        if includeWeekend then
            yield "Saturday"
            yield "Sunday"
    ]

堎合によっおは、do...yield によっお読みやすくなるこずがありたす。 このようなケヌスは、䞻芳的ではありたすが、考慮する必芁がありたす。

レコヌド衚珟のフォヌマット

短いレコヌドは、1 行に蚘述できたす。

// ✔ OK
let point = { X = 1.0; Y = 0.0 }

より長いレコヌドは、ラベルに新しい行を䜿甚する必芁がありたす。

// ✔ OK
let rainbow =
    { Boss = "Jeffrey"
      Lackeys = ["Zippy"; "George"; "Bungle"] }

耇数行かっこのフォヌマット スタむル

耇数の行にたたがるレコヌドの堎合、䞀般的に䜿甚されるフォヌマット スタむルは、Cramped、Aligned、Stroustrup の 3 ぀です。 Cramped スタむルは、コンパむラがコヌドを簡単に解析できるスタむルが優先される傟向があるため、F# コヌドの既定のスタむルです。 Aligned スタむルず Stroustrup スタむルの䞡方を䜿甚するず、メンバヌの䞊べ替えが容易になり、リファクタリングが容易になる可胜性があるコヌドが生成され、特定の状況ではやや詳现なコヌドが必芁になる堎合があるずいう欠点がありたす。

  • Cramped: 履歎の暙準であり、既定の F# レコヌド圢匏。 開く括匧は最初のメンバヌず同じ行に眮かれ、閉じる括匧は最埌のメンバヌず同じ行に眮かれたす。

    let rainbow = 
        { Boss1 = "Jeffrey"
          Boss2 = "Jeffrey"
          Boss3 = "Jeffrey"
          Lackeys = [ "Zippy"; "George"; "Bungle" ] }
    
  • Aligned: かっこはそれぞれ固有の行に蚘述し、同じ列に配眮したす。

    let rainbow =
        {
            Boss1 = "Jeffrey"
            Boss2 = "Jeffrey"
            Boss3 = "Jeffrey"
            Lackeys = ["Zippy"; "George"; "Bungle"]
        }
    
  • Stroustrup: 開き括匧はバむンディングず同じ行に眮き、閉じ括匧はそれ自䜓の行に配眮したす。

    let rainbow = {
        Boss1 = "Jeffrey"
        Boss2 = "Jeffrey"
        Boss3 = "Jeffrey"
        Lackeys = [ "Zippy"; "George"; "Bungle" ]
    }
    

リストおよび配列芁玠には、同じフォヌマット スタむル ルヌルが適甚されたす。

コピヌず曎新のレコヌド匏のフォヌマット

コピヌず曎新のレコヌド匏もレコヌドであるため、同様のガむドラむンが該圓したす。

短い匏は、1 行に収めるこずができたす。

// ✔ OK
let point2 = { point with X = 1; Y = 2 }

より長い匏では、新しい行を䜿甚し、䞊蚘の名前付き芏則のいずれかに基づいおフォヌマットする必芁がありたす。

// ✔ OK - Cramped
let newState =
    { state with
        Foo =
            Some
                { F1 = 0
                  F2 = "" } }
        
// ✔ OK - Aligned
let newState = 
    {
        state with
            Foo =
                Some
                    {
                        F1 = 0
                        F2 = ""
                    }
    }

// ✔ OK - Stroustrup
let newState = { 
    state with
        Foo =
            Some { 
                F1 = 0
                F2 = ""
            }
}

泚: コピヌず曎新の匏に Stroustrup スタむルを䜿甚する堎合は、コピヌしたレコヌド名よりもさらにメンバヌをむンデントする "必芁がありたす"。

// ✔ OK
let bilbo = {
    hobbit with 
        Name = "Bilbo"
        Age = 111
        Region = "The Shire" 
}

// ❌ Not OK - Results in compiler error: "Possible incorrect indentation: this token is offside of context started at position"
let bilbo = {
    hobbit with 
    Name = "Bilbo"
    Age = 111
    Region = "The Shire" 
}

パタヌン マッチングのフォヌマット

むンデントのない䞀臎の条項ごずに | を䜿甚したす。 匏が短く、それぞれの郚分匏も単玔なものである堎合は、1 行を䜿甚するこずを怜蚎できたす。

// ✔ OK
match l with
| { him = x; her = "Posh" } :: tail -> x
| _ :: tail -> findDavid tail
| [] -> failwith "Couldn't find David"

// ❌ Not OK, code formatters will reformat to the above by default
match l with
    | { him = x; her = "Posh" } :: tail -> x
    | _ :: tail -> findDavid tail
    | [] -> failwith "Couldn't find David"

パタヌン マッチングの曞匏蚭定は、匏の曞匏蚭定ず䞀臎する必芁がありたす。 パタヌン匕数の始めかっこの前にスペヌスを远加しないでください。

// ✔ OK
match x with
| Some(y) -> y
| None -> 0

// ✔ OK
match data with
| Success(value) -> value
| Error(msg) -> failwith msg

// ❌ Not OK, pattern formatting should match expression formatting
match x with
| Some (y) -> y
| None -> 0

ただし、匏の堎合ず同様に、パタヌン内では個別のカリヌ化匕数の間にスペヌスを入れおください:

// ✔ OK - space between curried arguments
match x with
| Pattern arg (a, b) -> processValues arg a b

// ❌ Not OK - missing space between curried arguments
match x with
| Pattern arg(a, b) -> processValues arg a b

パタヌン䞀臎の矢印の右偎にある匏が倧きすぎる堎合は、match/| から 1 ステップ むンデントされた次の行に移動しおください。

// ✔ OK
match lam with
| Var v -> 1
| Abs(x, body) ->
    1 + sizeLambda body
| App(lam1, lam2) ->
    sizeLambda lam1 + sizeLambda lam2

if 条件が倧きい堎合ず同様に、䞀臎匏が耇数行であるか、単䞀行の既定の蚱容範囲を超えおいる堎合は、䞀臎匏で 1 ぀のむンデントず新しい行を䜿甚する必芁がありたす。 長い䞀臎匏をカプセル化するずきは、match キヌワヌドず with キヌワヌドを揃える必芁がありたす。

// ✔ OK, but better to refactor, see below
match
    complexExpression a b && env.IsDevelopment()
    || someFunctionToCall
        aVeryLongParameterNameOne
        aVeryLongParameterNameTwo
        aVeryLongParameterNameThree 
with
| X y -> y
| _ -> 0

ただし、長い䞀臎匏を let バむンディングたたは別の関数にリファクタリングするこずは、より適切な圢匏です。

// ✔ OK
let performAction =
    complexExpression a b && env.IsDevelopment()
    || someFunctionToCall
        aVeryLongParameterNameOne
        aVeryLongParameterNameTwo
        aVeryLongParameterNameThree

match performAction with
| X y -> y
| _ -> 0

パタヌンマッチの矢印を敎列するこずは避けおください。

// ✔ OK
match lam with
| Var v -> v.Length
| Abstraction _ -> 2

// ❌ Not OK, code formatters will reformat to the above by default
match lam with
| Var v         -> v.Length
| Abstraction _ -> 2

キヌワヌド function を䜿甚しお実行されるパタヌン マッチングでは、前の行の先頭から 1 レベル むンデントする必芁がありたす。

// ✔ OK
lambdaList
|> List.map (function
    | Abs(x, body) -> 1 + sizeLambda 0 body
    | App(lam1, lam2) -> sizeLambda (sizeLambda 0 lam1) lam2
    | Var v -> 1)

通垞、function たたは let によっお定矩された関数内で let rec を䜿甚するこずは避け、match を優先しおください。 䜿甚する堎合は、function キヌワヌドに合わせたパタヌン芏則を指定する必芁がありたす。

// ✔ OK
let rec sizeLambda acc =
    function
    | Abs(x, body) -> sizeLambda (succ acc) body
    | App(lam1, lam2) -> sizeLambda (sizeLambda acc lam1) lam2
    | Var v -> succ acc

try/with 匏のフォヌマット

䟋倖の皮類のパタヌン マッチングは、with ず同じレベルでむンデントする必芁がありたす。

// ✔ OK
try
    if System.DateTime.Now.Second % 3 = 0 then
        raise (new System.Exception())
    else
        raise (new System.ApplicationException())
with
| :? System.ApplicationException ->
    printfn "A second that was not a multiple of 3"
| _ ->
    printfn "A second that was a multiple of 3"

句が 1 ぀だけの堎合を陀き、句ごずに | を远加したす。

// ✔ OK
try
    persistState currentState
with ex ->
    printfn "Something went wrong: %A" ex

// ✔ OK
try
    persistState currentState
with :? System.ApplicationException as ex ->
    printfn "Something went wrong: %A" ex

// ❌ Not OK, see above for preferred formatting
try
    persistState currentState
with
| ex ->
    printfn "Something went wrong: %A" ex

// ❌ Not OK, see above for preferred formatting
try
    persistState currentState
with
| :? System.ApplicationException as ex ->
    printfn "Something went wrong: %A" ex

名前付き匕数のフォヌマット

名前付き匕数には、= を囲むスペヌスが必芁です。

// ✔ OK
let makeStreamReader x = new System.IO.StreamReader(path = x)

// ❌ Not OK, spaces are necessary around '=' for named arguments
let makeStreamReader x = new System.IO.StreamReader(path=x)

刀別共甚䜓を䜿甚しおパタヌン マッチングを行う堎合、名前付きパタヌンは同様に曞匏蚭定されたす。以䞋に䟋を瀺したす。

type Data =
    | TwoParts of part1: string * part2: string
    | OnePart of part1: string

// ✔ OK
let examineData x =
    match data with
    | OnePartData(part1 = p1) -> p1
    | TwoPartData(part1 = p1; part2 = p2) -> p1 + p2

// ❌ Not OK, spaces are necessary around '=' for named pattern access
let examineData x =
    match data with
    | OnePartData(part1=p1) -> p1
    | TwoPartData(part1=p1; part2=p2) -> p1 + p2

倉化匏の敎圢

通垞、倉曎匏 location <- expr は 1 行でフォヌマットされたす。 耇数行のフォヌマットが必芁な堎合は、右偎の匏を新しい行に配眮したす。

// ✔ OK
ctx.Response.Headers[HeaderNames.ContentType] <-
    Constants.jsonApiMediaType |> StringValues

ctx.Response.Headers[HeaderNames.ContentLength] <-
    bytes.Length |> string |> StringValues

// ❌ Not OK, code formatters will reformat to the above by default
ctx.Response.Headers[HeaderNames.ContentType] <- Constants.jsonApiMediaType
                                                 |> StringValues
ctx.Response.Headers[HeaderNames.ContentLength] <- bytes.Length
                                                   |> string
                                                   |> StringValues

オブゞェクト匏の敎圢

オブゞェクト匏のメンバヌは、member を1レベルむンデントしお敎列させる必芁がありたす。

// ✔ OK
let comparer =
    { new IComparer<string> with
          member x.Compare(s1, s2) =
              let rev (s: String) = new String (Array.rev (s.ToCharArray()))
              let reversed = rev s1
              reversed.CompareTo (rev s2) }

Stroustrup スタむルを䜿甚するこずもできたす。

let comparer = { 
    new IComparer<string> with
        member x.Compare(s1, s2) =
            let rev (s: String) = new String(Array.rev (s.ToCharArray()))
            let reversed = rev s1
            reversed.CompareTo(rev s2)
}

空の型の定矩は、1 行に曞匏蚭定できたす。

type AnEmptyType = class end

遞択したペヌゞ幅に関係なく、= class end は垞に同じ行に眮く必芁がありたす。

むンデックスおよびスラむス匏の曞匏蚭定

むンデックス匏には、巊および右角かっこの前埌にスペヌスを含めるこずはできたせん。

// ✔ OK
let v = expr[idx]
let y = myList[0..1]

// ❌ Not OK
let v = expr[ idx ]
let y = myList[ 0 .. 1 ]

これは、以前の expr.[idx] 構文にも圓おはたりたす。

// ✔ OK
let v = expr.[idx]
let y = myList.[0..1]

// ❌ Not OK
let v = expr.[ idx ]
let y = myList.[ 0 .. 1 ]

匕甚笊で囲たれた匏の曞匏蚭定

匕甚笊で囲たれた匏が耇数行匏の堎合は、区切り蚘号 (<@、 @>、 <@@、 @@>) を別々の行に配眮する必芁がありたす。

// ✔ OK
<@
    let f x = x + 10
    f 20
@>

// ❌ Not OK
<@ let f x = x + 10
   f 20
@>

単䞀行の匏の堎合、区切り蚘号は匏自䜓ず同じ行に配眮する必芁がありたす。

// ✔ OK
<@ 1 + 1 @>

// ❌ Not OK
<@
    1 + 1
@>

連鎖匏の曞匏蚭定

チェヌン匏 (. ず絡み合う関数アプリケヌション) が長い堎合は、各アプリケヌション呌び出しを次の行に配眮したす。 チェヌン内の埌続のリンクを、先頭のリンクの埌に 1 レベル むンデントしたす。

// ✔ OK
Host
    .CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(fun webBuilder -> webBuilder.UseStartup<Startup>())

// ✔ OK
Cli
    .Wrap("git")
    .WithArguments(arguments)
    .WithWorkingDirectory(__SOURCE_DIRECTORY__)
    .ExecuteBufferedAsync()
    .Task

先頭のリンクは、シンプルな識別子である堎合、耇数のリンクで構成できたす。 たずえば、完党修食名前空間の远加などです。

// ✔ OK
Microsoft.Extensions.Hosting.Host
    .CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(fun webBuilder -> webBuilder.UseStartup<Startup>())

埌続のリンクにはシンプルな識別子も含める必芁がありたす。

// ✔ OK
configuration.MinimumLevel
    .Debug()
    // Notice how `.WriteTo` does not need its own line.
    .WriteTo.Logger(fun loggerConfiguration ->
        loggerConfiguration.Enrich
            .WithProperty("host", Environment.MachineName)
            .Enrich.WithProperty("user", Environment.UserName)
            .Enrich.WithProperty("application", context.HostingEnvironment.ApplicationName))

関数アプリケヌション内の匕数が行の残りの郚分に収たらない堎合は、各匕数を次の行に配眮したす。

// ✔ OK
WebHostBuilder()
    .UseKestrel()
    .UseUrls("http://*:5000/")
    .UseCustomCode(
        longArgumentOne,
        longArgumentTwo,
        longArgumentThree,
        longArgumentFour
    )
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseStartup<Startup>()
    .Build()

// ✔ OK
Cache.providedTypes
    .GetOrAdd(cacheKey, addCache)
    .Value

// ❌ Not OK, formatting tools will reformat to the above
Cache
    .providedTypes
    .GetOrAdd(
        cacheKey,
        addCache
    )
    .Value

関数アプリケヌション内のラムダ匕数は、( ず同じ行に始めるべきです。

// ✔ OK
builder
    .WithEnvironment()
    .WithLogger(fun loggerConfiguration ->
        // ...
        ())

// ❌ Not OK, formatting tools will reformat to the above
builder
    .WithEnvironment()
    .WithLogger(
        fun loggerConfiguration ->
        // ...
        ())

宣蚀のフォヌマット

このセクションでは、さたざたな皮類の宣蚀のフォヌマットに぀いお説明したす。

宣蚀の間に空癜行を远加する

最䞊䜍の関数ずクラスの定矩は、1 行の空癜行で区切りたす。 次に䟋を瀺したす。

// ✔ OK
let thing1 = 1+1

let thing2 = 1+2

let thing3 = 1+3

type ThisThat = This | That

// ❌ Not OK
let thing1 = 1+1
let thing2 = 1+2
let thing3 = 1+3
type ThisThat = This | That

コンストラクトに XML ドキュメント コメントがある堎合は、コメントの前に空癜行を远加したす。

// ✔ OK

/// This is a function
let thisFunction() =
    1 + 1

/// This is another function, note the blank line before this line
let thisFunction() =
    1 + 1

let ず member の宣蚀のフォヌマット

let ず member 宣蚀をフォヌマットする堎合、通垞、バむンディングの右偎は、1 行に収められるか、たたは (長すぎる堎合は) 1 レベル むンデントされ、新しい行に移動されたす。

たずえば、次の䟋は準拠しおいたす。

// ✔ OK
let a =
    """
foobar, long string
"""

// ✔ OK
type File =
    member this.SaveAsync(path: string) : Async<unit> =
        async {
            // IO operation
            return ()
        }

// ✔ OK
let c =
    { Name = "Bilbo"
      Age = 111
      Region = "The Shire" }

// ✔ OK
let d =
    while f do
        printfn "%A" x

これらは非準拠です。

// ❌ Not OK, code formatters will reformat to the above by default
let a = """
foobar, long string
"""

let d = while f do
    printfn "%A" x

レコヌド型のむンスタンス化では、かっこを独自の行に配眮するこずもできたす。

// ✔ OK
let bilbo =
    { 
        Name = "Bilbo"
        Age = 111
        Region = "The Shire" 
    }

たた、バむンド名ず同じ行にStroustrupの開始タグがある{スタむルを䜿甚するこずもできたす。

// ✔ OK
let bilbo = {
    Name = "Bilbo"
    Age = 111
    Region = "The Shire"
}

1 ぀の空癜行ずドキュメントでメンバヌを区切り、ドキュメント コメントを远加したす。

// ✔ OK

/// This is a thing
type ThisThing(value: int) =

    /// Gets the value
    member _.Value = value

    /// Returns twice the value
    member _.TwiceValue() = value*2

関連する関数のグルヌプを区切るために、远加の空癜行を (慎重に) 䜿甚するこずができたす。 関連する䞀連のワンラむナヌ (䞀連のダミヌ実装など) の間では、空癜行を省略するこずができたす。 関数内の論理セクションを瀺すための空癜行は慎重に䜿甚しおください。

関数ずメンバヌの匕数のフォヌマット

関数を定矩するずきは、各匕数の前埌に空癜を䜿甚したす。

// ✔ OK
let myFun (a: decimal) (b: int) c = a + b + c

// ❌ Not OK, code formatters will reformat to the above by default
let myFunBad (a:decimal)(b:int)c = a + b + c

長い関数定矩がある堎合は、パラメヌタヌを新しい行に配眮し、それに続くパラメヌタヌのむンデント レベルを䞀臎させるようにむンデントを蚭定したす。

// ✔ OK
module M =
    let longFunctionWithLotsOfParameters
        (aVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        =
        // ... the body of the method follows

    let longFunctionWithLotsOfParametersAndReturnType
        (aVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        : ReturnType =
        // ... the body of the method follows

    let longFunctionWithLongTupleParameter
        (
            aVeryLongParam: AVeryLongTypeThatYouNeedToUse,
            aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse,
            aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse
        ) =
        // ... the body of the method follows

    let longFunctionWithLongTupleParameterAndReturnType
        (
            aVeryLongParam: AVeryLongTypeThatYouNeedToUse,
            aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse,
            aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse
        ) : ReturnType =
        // ... the body of the method follows

これは、タプルを䜿甚するメンバヌ、コンストラクタヌ、およびパラメヌタヌにも該圓したす。

// ✔ OK
type TypeWithLongMethod() =
    member _.LongMethodWithLotsOfParameters
        (
            aVeryLongParam: AVeryLongTypeThatYouNeedToUse,
            aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse,
            aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse
        ) =
        // ... the body of the method

// ✔ OK
type TypeWithLongConstructor
    (
        aVeryLongCtorParam: AVeryLongTypeThatYouNeedToUse,
        aSecondVeryLongCtorParam: AVeryLongTypeThatYouNeedToUse,
        aThirdVeryLongCtorParam: AVeryLongTypeThatYouNeedToUse
    ) =
    // ... the body of the class follows

// ✔ OK
type TypeWithLongSecondaryConstructor () =
    new
        (
            aVeryLongCtorParam: AVeryLongTypeThatYouNeedToUse,
            aSecondVeryLongCtorParam: AVeryLongTypeThatYouNeedToUse,
            aThirdVeryLongCtorParam: AVeryLongTypeThatYouNeedToUse
        ) =
        // ... the body of the constructor follows

パラメヌタヌがカリヌ化される堎合は、新しい行に戻り倀の型ず共に = 文字を配眮したす。

// ✔ OK
type TypeWithLongCurriedMethods() =
    member _.LongMethodWithLotsOfCurriedParamsAndReturnType
        (aVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        : ReturnType =
        // ... the body of the method

    member _.LongMethodWithLotsOfCurriedParams
        (aVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aSecondVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        (aThirdVeryLongParam: AVeryLongTypeThatYouNeedToUse)
        =
        // ... the body of the method

これは、長すぎる行を回避し (戻り倀の型の名前が長い堎合もありたす)、パラメヌタヌを远加する際の行の砎損を少なくするための方法です。

挔算子宣蚀のフォヌマット

挔算子の定矩を囲むには、必芁に応じお空癜を䜿甚したす。

// ✔ OK
let ( !> ) x f = f x

// ✔ OK
let (!>) x f = f x

* で始たり、耇数の文字を含むカスタム挔算子に぀いおは、コンパむラのあいたいさを回避するために、定矩の先頭に空癜を远加する必芁がありたす。 このため、すべおの挔算子の定矩を 1 ぀の空癜文字で囲むこずをお勧めしたす。

レコヌド宣蚀のフォヌマット

レコヌド宣蚀の堎合、既定では、型定矩の { を 4 ぀のスペヌスでむンデントし、同じ行でラベル リストを開始し、メンバヌがある堎合は、{ トヌクンを䜿甚しお配眮する必芁がありたす。

// ✔ OK
type PostalAddress =
    { Address: string
      City: string
      Zip: string }

たた、ラベルをさらに 4 ぀のスペヌスでむンデントしお、独自の行に角かっこを蚘述するのも䞀般的です。

// ✔ OK
type PostalAddress =
    { 
        Address: string
        City: string
        Zip: string 
    }

型定矩 ({ スタむル) の最初の行の末尟に Stroustrup を配眮するこずもできたす。

// ✔ OK
type PostalAddress = {
    Address: string
    City: string
    Zip: string
}

远加のメンバヌが必芁な堎合は、可胜な限り with/end を䜿甚しないでください。

// ✔ OK
type PostalAddress =
    { Address: string
      City: string
      Zip: string }
    member x.ZipAndCity = $"{x.Zip} {x.City}"

// ❌ Not OK, code formatters will reformat to the above by default
type PostalAddress =
    { Address: string
      City: string
      Zip: string }
  with
    member x.ZipAndCity = $"{x.Zip} {x.City}"
  end
  
// ✔ OK
type PostalAddress =
    { 
        Address: string
        City: string
        Zip: string 
    }
    member x.ZipAndCity = $"{x.Zip} {x.City}"
    
// ❌ Not OK, code formatters will reformat to the above by default
type PostalAddress =
    { 
        Address: string
        City: string
        Zip: string 
    }
    with
        member x.ZipAndCity = $"{x.Zip} {x.City}"
    end

このスタむル ルヌルの䟋倖は、Stroustrup スタむルに埓っおレコヌドをフォヌマットする堎合です。 この状況では、コンパむラ芏則により、むンタヌフェむスを実装する堎合や、さらにメンバヌを远加する堎合は、with キヌワヌドが必芁です。

// ✔ OK
type PostalAddress = {
    Address: string
    City: string
    Zip: string
} with
    member x.ZipAndCity = $"{x.Zip} {x.City}"
   
// ❌ Not OK, this is currently invalid F# code
type PostalAddress = {
    Address: string
    City: string
    Zip: string
} 
member x.ZipAndCity = $"{x.Zip} {x.City}"

レコヌド フィヌルドに XML ドキュメントを远加する堎合は、Aligned たたは Stroustrup スタむルが優先され、メンバヌ間に空癜文字を远加する必芁がありたす。

// ❌ Not OK - putting { and comments on the same line should be avoided
type PostalAddress =
    { /// The address
      Address: string

      /// The city
      City: string

      /// The zip code
      Zip: string }

    /// Format the zip code and the city
    member x.ZipAndCity = $"{x.Zip} {x.City}"

// ✔ OK
type PostalAddress =
    {
        /// The address
        Address: string

        /// The city
        City: string

        /// The zip code
        Zip: string
    }

    /// Format the zip code and the city
    member x.ZipAndCity = $"{x.Zip} {x.City}"

// ✔ OK - Stroustrup Style
type PostalAddress = {
    /// The address
    Address: string

    /// The city
    City: string

    /// The zip code
    Zip: string
} with
    /// Format the zip code and the city
    member x.ZipAndCity = $"{x.Zip} {x.City}"

レコヌドでむンタヌフェむスの実装やメンバヌを宣蚀する堎合は、開始トヌクンを新しい行に配眮し、終了トヌクンを新しい行に配眮するこずをお勧めしたす。

// ✔ OK
// Declaring additional members on PostalAddress
type PostalAddress =
    {
        /// The address
        Address: string

        /// The city
        City: string

        /// The zip code
        Zip: string
    }

    member x.ZipAndCity = $"{x.Zip} {x.City}"

// ✔ OK
type MyRecord =
    {
        /// The record field
        SomeField: int
    }
    interface IMyInterface

匿名レコヌド型の゚むリアスには、これらの同じ芏則が適甚されたす。

刀別共甚䜓宣蚀のフォヌマット

刀別共甚䜓宣蚀の堎合は、型定矩の | を 4 ぀のスペヌスでむンデントしたす。

// ✔ OK
type Volume =
    | Liter of float
    | FluidOunce of float
    | ImperialPint of float

// ❌ Not OK
type Volume =
| Liter of float
| USPint of float
| ImperialPint of float

短い共甚䜓が 1 ぀の堎合は、先頭の | を省略できたす。

// ✔ OK
type Address = Address of string

長い共甚䜓たたは耇数行の共甚䜓の堎合は、| を保持し、各共甚䜓のフィヌルドを新しい行に配眮したす。各行の末尟には区切りのための * が配眮されたす。

// ✔ OK
[<NoEquality; NoComparison>]
type SynBinding =
    | SynBinding of
        accessibility: SynAccess option *
        kind: SynBindingKind *
        mustInline: bool *
        isMutable: bool *
        attributes: SynAttributes *
        xmlDoc: PreXmlDoc *
        valData: SynValData *
        headPat: SynPat *
        returnInfo: SynBindingReturnInfo option *
        expr: SynExpr *
        range: range *
        seqPoint: DebugPointAtBinding

ドキュメント コメントが远加される堎合は、各 /// コメントの前に空の行を䜿甚したす。

// ✔ OK

/// The volume
type Volume =

    /// The volume in liters
    | Liter of float

    /// The volume in fluid ounces
    | FluidOunce of float

    /// The volume in imperial pints
    | ImperialPint of float

リテラル宣蚀のフォヌマット

F# リテラルを䜿甚するLiteral属性は、属性をそれ自䜓の行に配眮し、PascalCase 呜名を䜿甚する必芁がありたす。

// ✔ OK

[<Literal>]
let Path = __SOURCE_DIRECTORY__ + "/" + __SOURCE_FILE__

[<Literal>]
let MyUrl = "www.mywebsitethatiamworkingwith.com"

属性を倀ず同じ行に配眮するこずは避けおください。

モゞュヌル宣蚀のフォヌマット

ロヌカル モゞュヌル内のコヌドは、モゞュヌルを基準ずしおむンデントする必芁がありたすが、最䞊䜍のモゞュヌル内のコヌドはむンデントするこずができたせん。 名前空間芁玠をむンデントする必芁はありたせん。

// ✔ OK - A is a top-level module.
module A

let function1 a b = a - b * b
// ✔ OK - A1 and A2 are local modules.
module A1 =
    let function1 a b = a * a + b * b

module A2 =
    let function2 a b = a * a - b * b

do 宣蚀のフォヌマット

型宣蚀、モゞュヌル宣蚀、およびコンピュテヌション匏では、副䜜甚ありの操䜜に察しお do たたは do! を䜿甚しなければならない堎合がありたす。 これらが耇数の行にたたがる堎合は、むンデントず新しい行を䜿甚しお、むンデントず let/let! の敎合性を維持したす。 クラスで do を䜿う䟋を以䞋に瀺したす。

// ✔ OK
type Foo() =
    let foo =
        fooBarBaz
        |> loremIpsumDolorSitAmet
        |> theQuickBrownFoxJumpedOverTheLazyDog

    do
        fooBarBaz
        |> loremIpsumDolorSitAmet
        |> theQuickBrownFoxJumpedOverTheLazyDog

// ❌ Not OK - notice the "do" expression is indented one space less than the `let` expression
type Foo() =
    let foo =
        fooBarBaz
        |> loremIpsumDolorSitAmet
        |> theQuickBrownFoxJumpedOverTheLazyDog
    do fooBarBaz
       |> loremIpsumDolorSitAmet
       |> theQuickBrownFoxJumpedOverTheLazyDog

do! をスペヌス 2 ぀でむンデントした䟋です (do! では偶然にも、スペヌス 4 ぀でむンデントしたやり方ずでは違いがないため)。

// ✔ OK
async {
  let! foo =
    fooBarBaz
    |> loremIpsumDolorSitAmet
    |> theQuickBrownFoxJumpedOverTheLazyDog

  do!
    fooBarBaz
    |> loremIpsumDolorSitAmet
    |> theQuickBrownFoxJumpedOverTheLazyDog
}

// ❌ Not OK - notice the "do!" expression is indented two spaces more than the `let!` expression
async {
  let! foo =
    fooBarBaz
    |> loremIpsumDolorSitAmet
    |> theQuickBrownFoxJumpedOverTheLazyDog
  do! fooBarBaz
      |> loremIpsumDolorSitAmet
      |> theQuickBrownFoxJumpedOverTheLazyDog
}

コンピュテヌション匏の挔算のフォヌマット

コンピュテヌション匏のカスタム操䜜を䜜成する堎合は、キャメルケヌスの名前付けを䜿甚するこずをお勧めしたす。

// ✔ OK
type MathBuilder() =
    member _.Yield _ = 0

    [<CustomOperation("addOne")>]
    member _.AddOne (state: int) =
        state + 1

    [<CustomOperation("subtractOne")>]
    member _.SubtractOne (state: int) =
        state - 1

    [<CustomOperation("divideBy")>]
    member _.DivideBy (state: int, divisor: int) =
        state / divisor

    [<CustomOperation("multiplyBy")>]
    member _.MultiplyBy (state: int, factor: int) =
        state * factor

let math = MathBuilder()

let myNumber =
    math {
        addOne
        addOne
        addOne
        subtractOne
        divideBy 2
        multiplyBy 10
    }

モデル化されるドメむンは、最終的に名前付け芏則を決定するべきです。 別の芏則を䜿甚するのが慣甚的である堎合は、その芏則を代わりに䜿甚する必芁がありたす。

匏の戻り倀がコンピュテヌション匏の堎合は、コンピュテヌション匏のキヌワヌド名を独自の行に配眮するこずをお勧めしたす。

// ✔ OK
let foo () = 
    async {
        let! value = getValue()
        do! somethingElse()
        return! anotherOperation value 
    }

コンピュテヌション匏をバむンド名ず同じ行に配眮するこずもできたす。

// ✔ OK
let foo () = async {
    let! value = getValue()
    do! somethingElse()
    return! anotherOperation value 
}

どの方法を䜿甚しおも、コヌドベヌス党䜓で䞀貫性を保぀こずを目指す必芁がありたす。 フォヌマッタを䜿甚するず、この蚭定を䞀貫性を維持するように指定できたす。

タむプずタむプ泚釈のフォヌマット

このセクションでは、フォヌマットの皮類ず型泚釈に぀いお説明したす。 これには、拡匵子が .fsi のシグネチャ ファむルのフォヌマットも含たれたす。

型の堎合は、ゞェネリック (Foo<T>) の前眮構文を優先する (いく぀かの特定の䟋倖あり)

F# では、ゞェネリック型の蚘述の埌眮のスタむル (int list など) ず、前眮のスタむル (list<int> など) の䞡方を蚱可しおいたす。 埌眮のスタむルは、1 ぀の型匕数ず共にのみ䜿甚できたす。 6 ぀の特定の型を陀き、垞に .NET スタむルを優先したす。

  1. F# リストの堎合は、int list ではなく埌眮圢匏 list<int> を䜿甚したす。
  2. F# オプションの堎合は、int option ではなく埌眮圢匏 option<int> を䜿甚したす。
  3. F# の倀のオプションの堎合は、int voption ではなく埌眮圢匏 voption<int> を䜿甚したす。
  4. F# 配列の堎合は、int array や array<int> ではなく埌眮圢匏 int[] を䜿甚したす。
  5. 参照セルの堎合は、int ref や ref<int> ではなく Ref<int> を䜿甚したす。
  6. F# シヌケンスの堎合は、int seqではなく、埌眮圢匏の seq<int> を䜿甚したす。

それ以倖のすべおの型には、前眮圢匏を䜿甚したす。

関数型のフォヌマット

関数のシグネチャを定矩するずきは、-> シンボルの前埌に空癜を䜿甚したす。

// ✔ OK
type MyFun = int -> int -> string

// ❌ Not OK
type MyFunBad = int->int->string

匕数ず倀の型に関する泚釈のフォヌマット

型のコメントを持぀倀たたは匕数を定矩する堎合は、: シンボルの埌に空癜を䜿甚し、前には䜿甚したせん。

// ✔ OK
let complexFunction (a: int) (b: int) c = a + b + c

let simpleValue: int = 0 // Type annotation for let-bound value

type C() =
    member _.Property: int = 1

// ❌ Not OK
let complexFunctionPoorlyAnnotated (a :int) (b :int) (c:int) = a + b + c
let simpleValuePoorlyAnnotated1:int = 1
let simpleValuePoorlyAnnotated2 :int = 2

耇数行の型の泚釈の曞匏蚭定

型の泚釈が長い、たたは耇数行のずき、次の行に配眮し、1 レベルごずにむンデントしおください。

type ExprFolder<'State> =
    { exprIntercept: 
        ('State -> Expr -> 'State) -> ('State -> Expr -> 'State -> 'State -> Exp -> 'State }
        
let UpdateUI
    (model:
#if NETCOREAPP2_1
        ITreeModel
#else
        TreeModel
#endif
    )
    (info: FileInfo) =
    // code
    ()

let f
    (x:
        {|
            a: Second
            b: Metre
            c: Kilogram
            d: Ampere
            e: Kelvin
            f: Mole
            g: Candela
        |})
    =
    x.a

type Sample
    (
        input: 
            LongTupleItemTypeOneThing * 
            LongTupleItemTypeThingTwo * 
            LongTupleItemTypeThree * 
            LongThingFour * 
            LongThingFiveYow
    ) =
    class
    end

むンラむン匿名レコヌドの皮類の堎合は、Stroustrup スタむルを䜿甚するこずもできたす。

let f
    (x: {|
        x: int
        y: AReallyLongTypeThatIsMuchLongerThan40Characters
     |})
    =
    x

戻り倀型泚釈のフォヌマット

関数たたはメンバヌの戻り倀の型の泚釈では、: 蚘号の前埌に空癜文字を䜿甚したす。

// ✔ OK
let myFun (a: decimal) b c : decimal = a + b + c

type C() =
    member _.SomeMethod(x: int) : int = 1

// ❌ Not OK
let myFunBad (a: decimal) b c:decimal = a + b + c

let anotherFunBad (arg: int): unit = ()

type C() =
    member _.SomeMethodBad(x: int): int = 1

眲名の皮類のフォヌマット

シグネチャで完党な関数型を蚘述するずきは、匕数を耇数の行に分割しなければならない堎合がありたす。 戻り倀の型は垞にむンデントされたす。

タプル圢匏の関数の堎合、匕数は * で区切り、各行の末尟に配眮したす。

䟋ずしお、次の実装を含む関数に぀いお考えおみたしょう。

let SampleTupledFunction(arg1, arg2, arg3, arg4) = ...

察応するシグネチャ ファむル (拡匵子 .fsi) では、耇数行のフォヌマットが必芁な堎合に、関数を次のようにフォヌマットできたす。

// ✔ OK
val SampleTupledFunction:
    arg1: string *
    arg2: string *
    arg3: int *
    arg4: int ->
        int list

同様に、カリヌ化された関数に぀いお考えおみたしょう。

let SampleCurriedFunction arg1 arg2 arg3 arg4 = ...

察応するシグネチャ ファむルでは、各行の末尟に -> が配眮されたす。

// ✔ OK
val SampleCurriedFunction:
    arg1: string ->
    arg2: string ->
    arg3: int ->
    arg4: int ->
        int list

同様に、カリヌ化された匕数ずタプル圢匏の匕数の組み合わせを受け取る関数に぀いお考えおみたしょう。

// Typical call syntax:
let SampleMixedFunction
        (arg1, arg2)
        (arg3, arg4, arg5)
        (arg6, arg7)
        (arg8, arg9, arg10) = ..

察応するシグネチャ ファむルでは、タプルが先行する型がむンデントされたす

// ✔ OK
val SampleMixedFunction:
    arg1: string *
    arg2: string ->
        arg3: string *
        arg4: string *
        arg5: TType ->
            arg6: TType *
            arg7: TType ->
                arg8: TType *
                arg9: TType *
                arg10: TType ->
                    TType list

同じ芏則が型シグネチャのメンバヌに適甚されたす。

type SampleTypeName =
    member ResolveDependencies:
        arg1: string *
        arg2: string ->
            string

明瀺的なゞェネリック型匕数ず制玄のフォヌマット

以䞋のガむドラむンは、関数定矩、メンバヌ定矩、および型定矩、および関数適甚に適甚されたす。

長すぎない堎合は、ゞェネリック型匕数ず制玄は 1 ぀の行に収めたす。

// ✔ OK
let f<'T1, 'T2 when 'T1: equality and 'T2: comparison> param =
    // function body

ゞェネリック型匕数/制玄ず関数パラメヌタヌの䞡方は収たらないが、型パラメヌタヌ/制玄のみは収たる堎合、パラメヌタヌは新しい行に配眮したす。

// ✔ OK
let f<'T1, 'T2 when 'T1: equality and 'T2: comparison>
    param
    =
    // function body

型パラメヌタヌたたは制玄が長すぎる堎合は、次のように分割しお配眮したす。 䞀連の型パラメヌタヌは、その長さに関わらず、関数ず同じ行に収めたす。 制玄の堎合は、when を最初の行に配眮し、その長さに関わらず各制玄を 1 ぀の行に保持したす。 最埌の行の末尟には > を配眮したす。 制玄を 1 レベル むンデントしたす。

// ✔ OK
let inline f< ^T1, ^T2
    when ^T1: (static member Foo1: unit -> ^T2)
    and ^T2: (member Foo2: unit -> int)
    and ^T2: (member Foo3: string -> ^T1 option)>
    arg1
    arg2
    =
    // function body

型パラメヌタヌ/制玄は分割したが、通垞の関数パラメヌタヌがない堎合は、関係なく新しい行に = を配眮したす。

// ✔ OK
let inline f< ^T1, ^T2
    when ^T1: (static member Foo1: unit -> ^T2)
    and ^T2: (member Foo2: unit -> int)
    and ^T2: (member Foo3: string -> ^T1 option)>
    =
    // function body

同じ芏則が関数適甚に適甚されたす。

// ✔ OK
myObj
|> Json.serialize<
    {| child: {| displayName: string; kind: string |}
       newParent: {| id: string; displayName: string |}
       requiresApproval: bool |}>

// ✔ OK
Json.serialize<
    {| child: {| displayName: string; kind: string |}
       newParent: {| id: string; displayName: string |}
       requiresApproval: bool |}>
    myObj

継承の曞匏蚭定

基底クラスのコンストラクタヌの匕数は、inherit 句の匕数リストに衚瀺されたす。 新しい行に inherit 句を配眮し、1 レベルごずにむンデントしたす。

type MyClassBase(x: int) =
   class
   end

// ✔ OK
type MyClassDerived(y: int) =
   inherit MyClassBase(y * 2)

// ❌ Not OK
type MyClassDerived(y: int) = inherit MyClassBase(y * 2)

コンストラクタヌが長い、たたは耇数行の堎合は、次の行に配眮し、1 レベルごずにむンデントしたす。
耇数行関数アプリケヌションの芏則に埓い、この耇数行コンストラクタヌを曞匏蚭定したす。

type MyClassBase(x: string) =
   class
   end

// ✔ OK
type MyClassDerived(y: string) =
    inherit 
        MyClassBase(
            """
            very long
            string example
            """
        )
        
// ❌ Not OK
type MyClassDerived(y: string) =
    inherit MyClassBase(
        """
        very long
        string example
        """)

プラむマリ コンストラクタヌの曞匏蚭定

既定の曞匏蚭定芏則では、プラむマリ コンストラクタヌの型名ずかっこの間にスペヌスは远加されたせん。

// ✔ OK
type MyClass() =
    class
    end

type MyClassWithParams(x: int, y: int) =
    class
    end
        
// ❌ Not OK
type MyClass () =
    class
    end

type MyClassWithParams (x: int, y: int) =
    class
    end

耇数のコンストラクタヌ

inherit 句がレコヌドの䞀郚のずき、短ければ同じ行に配眮したす。 長い堎合や耇数行の堎合は、その行を次の行に配眮し、1レベル分むンデントしたす。

type BaseClass =
    val string1: string
    new () = { string1 = "" }
    new (str) = { string1 = str }

type DerivedClass =
    inherit BaseClass

    val string2: string
    new (str1, str2) = { inherit BaseClass(str1); string2 = str2 }
    new () = 
        { inherit 
            BaseClass(
                """
                very long
                string example
                """
            )
          string2 = str2 }

フォヌマット属性

属性 はコンストラクトの䞊に配眮されたす。

// ✔ OK
[<SomeAttribute>]
type MyClass() = ...

// ✔ OK
[<RequireQualifiedAccess>]
module M =
    let f x = x

// ✔ OK
[<Struct>]
type MyRecord =
    { Label1: int
      Label2: string }

これらは、XML ドキュメントの埌に配眮する必芁がありたす。

// ✔ OK

/// Module with some things in it.
[<RequireQualifiedAccess>]
module M =
    let f x = x

パラメヌタヌの属性のフォヌマット

属性は、パラメヌタヌにも配眮できたす。 この堎合、「then」をパラメヌタヌず同じ行で名前の前に配眮したす。

// ✔ OK - defines a class that takes an optional value as input defaulting to false.
type C() =
    member _.M([<Optional; DefaultParameterValue(false)>] doSomething: bool)

耇数の属性のフォヌマット

パラメヌタヌではないコンストラクトに耇数の属性を適甚する堎合は、各属性を別々の行に配眮したす。

// ✔ OK

[<Struct>]
[<IsByRefLike>]
type MyRecord =
    { Label1: int
      Label2: string }

パラメヌタヌに適甚する堎合は、同じ行に属性を配眮し、; 区切り蚘号で区切りたす。

謝蟞

これらのガむドラむンは、Anh-Dung Phan による「A comprehensive guide to F# Formatting Conventions\(F# の曞匏芏則に関する包括的なガむド)\」に基づいおいたす。