F# kod biçimlendirme yönergeleri

Bu makalede, F# kodunuzun şöyle olması için kodunuzu biçimlendirme yönergeleri sunulmaktadır:

  • Daha okunaklı
  • Visual Studio Code'da ve diğer düzenleyicilerde biçimlendirme araçları tarafından uygulanan kurallar uyarınca
  • Diğer çevrimiçi koda benzer

Ayrıca bkz. Adlandırma kurallarını da kapsayan Kodlama kuralları ve Bileşen tasarımı yönergeleri.

Otomatik kod biçimlendirme

Fantomas kod biçimlendiricisi, otomatik kod biçimlendirme için F# topluluğu standart aracıdır. Varsayılan ayarlar bu stil kılavuzuna karşılık gelir.

Bu kod biçimlendiricinin kullanılmasını kesinlikle öneririz. F# ekiplerinde kod biçimlendirme belirtimleri, ekip deposunda denetlenen kod biçimlendirici için üzerinde anlaşmaya varılan ayarlar dosyası açısından kabul edilmeli ve birleştirilmelidir.

Biçimlendirme için genel kurallar

F# varsayılan olarak önemli boşluk kullanır ve boşluklara duyarlıdır. Aşağıdaki yönergeler, bunun dayatabileceği bazı zorlukların üstesinden nasıl getirebileceğiniz konusunda rehberlik sağlamaya yöneliktir.

Sekmeleri değil boşlukları kullanma

Girintileme gerektiğinde, sekmeleri değil boşlukları kullanmanız gerekir. F# kodu sekmeleri kullanmaz ve bir dize değişmez değeri veya açıklamasının dışında bir sekme karakteriyle karşılaşılırsa derleyici hata verir.

Tutarlı girinti kullanma

Girintileme sırasında en az bir boşluk gerekir. Kuruluşunuz, girintileme için kullanılacak alan sayısını belirtmek üzere kodlama standartları oluşturabilir; girintilemenin gerçekleştiği her düzeyde iki, üç veya dört girinti alanı tipiktir.

Girinti başına dört boşluk öneririz.

Buna göre, programların girintisi öznel bir konudur. Çeşitlemeler tamamdır, ancak izlemeniz gereken ilk kural girinti tutarlılığıdır. Genel olarak kabul edilen bir girinti stili seçin ve bunu kod tabanınız genelinde sistematik olarak kullanın.

Ad uzunluğuna duyarlı biçimlendirmeden kaçının

Adlandırmaya duyarlı girinti ve hizalamadan kaçınmaya bakın:

// ✔️ 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 _ -> ()
    | ...

Bundan kaçınmanın başlıca nedenleri şunlardır:

  • Önemli kod sağa taşınır
  • Gerçek kod için daha az genişlik kaldı
  • Yeniden adlandırma hizalamayı bozabilir

Gereksiz boşluklardan kaçının

Bu stil kılavuzunda açıklanan durumlar dışında F# kodunda fazla boşluk kullanmaktan kaçının.

// ✔️ OK
spam (ham 1)

// ❌ Not OK
spam ( ham 1 )

Açıklamaları biçimlendirme

Blok açıklamaları yerine birden çok çift eğik çizgi açıklamasını tercih edin.

// 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çıklamalar ilk harfi büyük harfe çevirmeli ve iyi biçimlendirilmiş tümcecikler veya cümleler olmalıdır.

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

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

XML belgesi açıklamalarını biçimlendirmek için aşağıdaki "Biçimlendirme bildirimleri" bölümüne bakın.

İfadeleri biçimlendirme

Bu bölümde farklı türlerdeki biçimlendirme ifadeleri ele alınmaktadır.

Dize ifadelerini biçimlendirme

Dize değişmez değerleri ve ilişkilendirilmiş dizeler, çizginin ne kadar uzun olduğuna bakılmaksızın tek bir satırda bırakılabilir.

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

Çok satırlı ilişkilendirmeli ifadeler önerilmez. Bunun yerine, ifade sonucunu bir değere bağlayın ve bunu ilişkilendirilmiş dizede kullanın.

Tanımlama grubu ifadelerini biçimlendirme

Bir tanımlama grubu örneği parantez içine alınmalıdır ve içindeki sınırlandırma virgülleri tek bir boşlukla izlenmelidir, örneğin: (1, 2), (x, y, z).

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

Tanımlama demetlerinin desen eşleştirmesinde parantezlerin atlandığı yaygın olarak kabul edilir:

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

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

Tanımlama grubu bir işlevin dönüş değeriyse parantezlerin atlandığı da yaygın olarak kabul edilir:

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

Özetle, parantezli tanımlama grubu örneklemelerini tercih edin, ancak desen eşleştirme veya dönüş değeri için tanımlama demetleri kullanılırken parantezlerden kaçınmak iyi kabul edilir.

Uygulama ifadelerini biçimlendirme

Bir işlev veya yöntem uygulamasını biçimlendirirken, satır genişliği aşağıdakilere izin verdiğinde bağımsız değişkenler aynı satırda sağlanır:

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

Bağımsız değişkenler gerektirmediği sürece parantezleri atla:

// ✔️ OK
someFunction1 x.IngredientName

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

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

Birden çok curried bağımsız değişkenle çağrılırken boşlukları atmayın:

// ✔️ 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)

Varsayılan biçimlendirme kurallarında, yinelenen veya parantez içinde bağımsız değişkenlere küçük harf işlevleri uygulanırken bir boşluk eklenir (tek bir bağımsız değişken kullanıldığında bile):

// ✔️ 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)

Varsayılan biçimlendirme kurallarında, yinelenen bağımsız değişkenlere büyük harfle yazılmış yöntemler uygulanırken boşluk eklenmez. Bunun nedeni, bunların genellikle akıcı programlama ile kullanılmasıdır:

// ✔️ 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)

Bağımsız değişkenlerin listesi veya bağımsız değişken adları çok uzun olduğundan, bağımsız değişkenleri yeni bir satırdaki bir işleve geçirmeniz gerekebilir. Bu durumda, bir düzey girintile:

// ✔️ 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)

İşlev tek bir çok satırlı tanımlama bağımsız değişkeni aldığında, her bağımsız değişkeni yeni bir satıra yerleştirin:

// ✔️ 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)

Bağımsız değişken ifadeleri kısaysa, bağımsız değişkenleri boşluklarla ayırın ve tek satırda tutun.

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

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

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

Bağımsız değişken ifadeleri uzunsa, sol paranteze girinti eklemek yerine yeni satırlar kullanın ve bir düzey girintili yapın.

// ✔️ 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)

Aynı kurallar, çok satırlı dizeler de dahil olmak üzere yalnızca tek bir çok satırlı bağımsız değişken olsa bile geçerlidir:

// ✔️ 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" ]
)

İşlem hattı ifadelerini biçimlendirme

Birden çok satır kullanırken işlem hattı |> işleçleri üzerinde çalıştıkları ifadelerin altına inmelidir.

// ✔️ 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

Lambda ifadelerini biçimlendirme

Lambda ifadesi çok satırlı bir ifadede bağımsız değişken olarak kullanıldığında ve ardından başka bağımsız değişkenler geldiğinde, lambda ifadesinin gövdesini bir düzey girintili yeni bir satıra yerleştirin:

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

Lambda bağımsız değişkeni bir işlev uygulamasındaki son bağımsız değişkense, ok aynı satıra gelene kadar tüm bağımsız değişkenleri yerleştirin.

// ✔️ 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}")

Eşleştirme lambda'larını benzer şekilde tedavi edin.

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

Lambda tüm bağımsız değişkenleri tek düzeyle girintilemeden önce birçok baştaki veya çok satırlı bağımsız değişken olduğunda.

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

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

Lambda ifadesinin gövdesi birden çok satır uzunluğundaysa, bunu yerel olarak kapsamlı bir işlev olarak yeniden düzenlemeyi düşünmelisiniz.

İşlem hatları lambda ifadeleri içerdiğinde, her lambda ifadesi genellikle işlem hattının her aşamasındaki son bağımsız değişkendir:

// ✔️ 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}")

Lambda bağımsız değişkenlerinin tek bir satıra sığmaması veya çok satırlı olması durumunda, bunları bir düzey girintili bir sonraki satıra yerleştirin.

// ✔️ 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 ()

Aritmetik ve ikili ifadeleri biçimlendirme

İkili aritmetik ifadelerin çevresinde her zaman boşluk kullanın:

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

belirli biçimlendirme seçenekleriyle birleştirildiğinde bir ikili - işlecin çevrelenmesi, bunu birli -olarak yorumlamaya neden olabilir. Birli - işleçlerin her zaman hemen ardından bunları olumsuzladıkları değer gelir:

// ✔️ OK
let negate x = -x

// ❌ Not OK
let negateBad x = - x

işlecinden - sonra boşluk karakteri eklemek başkalarının kafa karışıklığına neden olabilir.

İkili işleçleri boşluklarla ayırın. Infix ifadeleri aynı sütunda sıralı olarak tamamdır:

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

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

Bu kural, türlerdeki ölçü birimleri ve sabit ek açıklamalar için de geçerlidir:

// ✔️ 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)> }

Aşağıdaki işleçler F# standart kitaplığında tanımlanır ve eşdeğerleri tanımlamak yerine kullanılmalıdır. Kodu daha okunabilir ve idiomatic hale getirme eğiliminde olduğundan bu işleçlerin kullanılması önerilir. Aşağıdaki listede önerilen F# işleçleri özetlenmiştir.

// ✔️ OK
x |> f // Forward 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

Aralık işleç ifadelerini biçimlendirme

Yalnızca tüm ifadeler atomik olmadığında çevresine .. boşluklar ekleyin. Tamsayılar ve tek sözcük tanımlayıcıları atomik olarak kabul edilir.

// ✔️ 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 ]

Bu kurallar dilimleme için de geçerlidir:

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

eğer ifadelerini biçimlendirme

Koşullu değerlerin girintisi, bunları oluşturan ifadelerin boyutuna ve karmaşıklığına bağlıdır. Bunları şu durumlarda tek satıra yazın:

  • cond, e1ve e2 kısadır.
  • e1 ve e2 ifadelerin kendileri değildir if/then/else .
// ✔️ OK
if cond then e1 else e2

Else ifadesi yoksa, ifadenin tamamını hiçbir zaman tek satırda yazmamak önerilir. Bu, kesinlik temelli kodu işlevden ayırmaktır.

// ✔️ OK
if a then
    ()

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

İfadelerden herhangi biri çok satırlıysa, her koşullu dal çok satırlı olmalıdır.

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

ve else ile elif birden çok koşullu, tek satırlı if/then/else ifadelerin kurallarına uydukları zaman ile aynı kapsamda if girintilenir.

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

Koşullardan veya ifadelerden herhangi biri çok satırlıysa, ifadenin tamamı if/then/else çok satırlı olur:

// ✔️ 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

Bir koşul çok satırlıysa veya tek satırlının varsayılan toleransını aşarsa, koşul ifadesi bir girinti ve yeni bir satır kullanmalıdır. ve then anahtar sözcüğü, if uzun koşul ifadesini kapsüllerken hizalanmalıdır.

// ✔️ 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

Ancak, uzun koşulları izin bağlama veya ayrı bir işleve yeniden düzenleme daha iyi bir stildir:

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

if performAction then
    e1
else
    e2

Birleşim büyük/küçük harf ifadelerini biçimlendirme

Ayrımcı birleşim durumlarının uygulanması, işlev ve yöntem uygulamalarıyla aynı kurallara uyar. Yani, ad büyük harfe dönüştürülmüş olduğundan kod biçimlendiriciler tanımlama grubundan önceki bir alanı kaldırır:

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

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

İşlev uygulamaları gibi, birden çok satıra ayrılan yapılar da girinti kullanmalıdır:

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

Liste ve dizi ifadelerini biçimlendirme

İşlecin etrafındaki :: boşluklarla yazın x :: l (::boşluklarla çevrili olan bir infix işlecidir).

Tek bir satırda bildirilen liste ve diziler, açılış köşeli ayracından sonra ve kapatma köşeli ayracından önce boşluk içermelidir:

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

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

Her zaman iki ayrı ayraç benzeri işleç arasında en az bir boşluk kullanın. Örneğin, ile arasında [{boşluk bırakın.

// ✔️ 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 }]

Aynı kılavuz, tanımlama grubu listeleri veya dizileri için de geçerlidir.

Birden çok satıra bölünmüş listeler ve diziler, kayıtlarla benzer bir kural izler:

// ✔️ 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 |] |]

Kayıtlarda olduğu gibi, açma ve kapatma köşeli ayraçlarını kendi satırlarında bildirmek, kodu hareket ettirmeyi ve işlevlere aktarmayı kolaylaştırır:

// ✔️ 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 |] 
    |]

Bir liste veya dizi ifadesi bağlamanın sağ tarafıysa, stili kullanmayı Stroustrup tercih edebilirsiniz:

// ✔️ 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 |] 
|]

Ancak, bir liste veya dizi ifadesi bağlamanın sağ tarafı olmadığında (örneğin, başka bir liste veya dizinin içindeyse) bu iç ifadenin birden çok satıra yayılması gerekiyorsa köşeli ayraçlar kendi satırlarında olmalıdır:

// ✔️ 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
] ]

Dizilerin/listelerin içindeki kayıt türleri için de aynı kural geçerlidir:

// ✔️ 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
} ]

Program aracılığıyla diziler ve listeler oluştururken, bir değerin her zaman oluşturulduğu zamanı tercih edin ->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 ]

Verilerin koşullu olarak oluşturulabileceği veya değerlendirilecek ardışık ifadelerin olabileceği durumlarda belirtilmesi yield gereken eski F# sürümleri. Daha eski bir F# dil sürümüyle derlemeniz gerekmediği sürece bu yield anahtar sözcükleri atmayı tercih edin:

// ✔️ 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"
    ]

Bazı durumlarda okunabilirliğe do...yield yardımcı olabilir. Bu durumlar öznel olsa da dikkate alınmalıdır.

Kayıt ifadelerini biçimlendirme

Kısa kayıtlar tek satırda yazılabilir:

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

Daha uzun olan kayıtlar etiketler için yeni satırlar kullanmalıdır:

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

Çok satırlı köşeli ayraç biçimlendirme stilleri

Birden çok satıra yayılan kayıtlar için yaygın olarak kullanılan üç biçimlendirme stili vardır: Cramped, Alignedve Stroustrup. Stil Cramped , derleyicinin kodu kolayca ayrıştırmasına olanak sağlayan stilleri tercih etme eğiliminde olduğundan F# kodu için varsayılan stil olmuştur. Hem hem Stroustrup de Aligned stiller üyelerin daha kolay yeniden sıralanmasına olanak tanıyarak, belirli durumların biraz daha ayrıntılı kod gerektirebilmesinin dezavantajı ile yeniden düzenlenmesi daha kolay olabilecek kodlara yol açar.

  • Cramped: Geçmiş standardı ve varsayılan F# kayıt biçimi. Açılış köşeli ayraçları ilk üyeyle aynı satırda, kapanış köşeli ayracı son üyeyle aynı satırda yer alır.

    let rainbow = 
        { Boss1 = "Jeffrey"
          Boss2 = "Jeffrey"
          Boss3 = "Jeffrey"
          Lackeys = [ "Zippy"; "George"; "Bungle" ] }
    
  • Aligned: Köşeli ayraçların her biri aynı sütuna hizalanmış kendi çizgilerini alır.

    let rainbow =
        {
            Boss1 = "Jeffrey"
            Boss2 = "Jeffrey"
            Boss3 = "Jeffrey"
            Lackeys = ["Zippy"; "George"; "Bungle"]
        }
    
  • Stroustrup: Köşeli ayraç açma, bağlamayla aynı çizgiye gider, kapatma köşeli ayracı kendi satırını alır.

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

Aynı biçimlendirme stili kuralları liste ve dizi öğeleri için de geçerlidir.

Kayıt ifadelerini kopyalama ve güncelleştirme biçimlendirme

Kopyalama ve güncelleştirme kayıt ifadesi hala bir kayıt olduğundan benzer yönergeler geçerlidir.

Kısa ifadeler tek satıra sığabilir:

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

Daha uzun ifadelerde yeni satırlar ve yukarıdaki adlandırılmış kurallardan birine göre biçim kullanılmalıdır:

// ✔️ 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 = ""
            }
}

Not: Kopyalama ve güncelleştirme ifadeleri için stil kullanıyorsanız Stroustrup , kopyalanan kayıt adından daha fazla üye girintisi oluşturmanız gerekir :

// ✔️ 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" 
}

Biçimlendirme deseni eşleştirme

Eşleşmenin girintisiz her yan tümcesi için bir | kullanın. İfade kısaysa, her alt ifade de basitse tek bir satır kullanmayı düşünebilirsiniz.

// ✔️ 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"

Desen eşleştirme okunun sağındaki ifade çok büyükse, bu ifadeyi aşağıdaki satıra taşıyın ve bir adım girintili olarak öğesini seçin match/|.

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

Büyük durum koşullarına benzer şekilde, eşleşme ifadesi çok satırlıysa veya tek satırlının varsayılan toleransını aşarsa, eşleşme ifadesi bir girinti ve yeni bir satır kullanmalıdır. match uzun eşleşme ifadesi kapsüllendiğinde ve with anahtar sözcüğü hizalanmalıdır.

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

Ancak, uzun eşleme ifadelerini let bağlama veya ayrı bir işlevle yeniden düzenlemek daha iyi bir stildir:

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

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

Desen eşleşmesinin oklarını hizalamaktan kaçınılmalıdır.

// ✔️ 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

anahtar sözcüğü function kullanılarak sunulan desen eşleştirmesi, önceki satırın başından bir düzey girintili olmalıdır:

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

veya tarafından letlet rec tanımlanan işlevlerin function kullanımı genel olarak bir matchlehine önlenmelidir. Kullanılırsa, desen kuralları anahtar sözcüğüyle functionhizalanmalıdır:

// ✔️ 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 ifadelerini biçimlendirme

Özel durum türündeki desen eşleştirmesi ile aynı düzeyde withgirintili olmalıdır.

// ✔️ 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"

Yalnızca tek bir | yan tümcesi olması dışında her yan tümce için bir ekleyin:

// ✔️ 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

Adlandırılmış bağımsız değişkenleri biçimlendirme

Adlandırılmış bağımsız değişkenlerin çevresinde =boşluklar olmalıdır:

// ✔️ 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)

Ayrımcı birleşimler kullanılarak desen eşleştirmesi yapıldığında, adlandırılmış desenler benzer şekilde biçimlendirilir, örneğin.

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

Mutasyon ifadelerini biçimlendirme

Mutasyon ifadeleri location <- expr normalde tek bir satırda biçimlendirilir. Çok satırlı biçimlendirme gerekiyorsa, sağ taraftaki ifadeyi yeni bir satıra yerleştirin.

// ✔️ 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

Nesne ifadelerini biçimlendirme

Nesne ifadesi üyeleri, bir düzey girintili olacak şekilde hizalanmalıdır member .

// ✔️ 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) }

Stili kullanmayı Stroustrup da tercih edebilirsiniz:

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)
}

Boş tür tanımları tek satırda biçimlendirilmiş olabilir:

type AnEmptyType = class end

Seçilen sayfa genişliği ne olursa olsun, = class end her zaman aynı satırda olmalıdır.

Dizin/dilim ifadelerini biçimlendirme

Dizin ifadeleri, açma ve kapatma köşeli ayraçlarının çevresinde boşluk içermemelidir.

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

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

Bu, eski expr.[idx] söz dizimi için de geçerlidir.

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

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

Tırnak içine alınmış ifadeleri biçimlendirme

Alıntılanan ifade çok satırlı bir ifadeyse sınırlayıcı simgeleri (<@@>, <@@@@>) ayrı satırlara yerleştirilmelidir.

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

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

Tek satırlı ifadelerde sınırlayıcı simgeleri ifadenin kendisiyle aynı satıra yerleştirilmelidir.

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

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

Zincirlenmiş ifadeleri biçimlendirme

Zincirlenmiş ifadeler (ile .iç içe geçmiş işlev uygulamaları) uzun olduğunda, her uygulama çağrısını bir sonraki satıra yerleştirin. Zincirdeki sonraki bağlantıları, baştaki bağlantıdan sonra bir düzey girintili yapın.

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

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

Baştaki bağlantı, basit tanımlayıcılar ise birden çok bağlantıdan oluşturulabilir. Örneğin, tam ad alanının eklenmesi.

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

Sonraki bağlantılar da basit tanımlayıcılar içermelidir.

// ✔️ 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))

İşlev uygulamasının içindeki bağımsız değişkenler satırın geri kalanına sığmadığında, her bağımsız değişkeni bir sonraki satıra yerleştirin.

// ✔️ 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

İşlev uygulamasının içindeki Lambda bağımsız değişkenleri, açma (ile aynı satırda başlamalıdır.

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

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

Biçimlendirme bildirimleri

Bu bölümde farklı türlerdeki biçimlendirme bildirimleri açıklanmıştır.

Bildirimler arasına boş satırlar ekleme

Üst düzey işlev ve sınıf tanımlarını tek bir boş satırla ayırın. Örneğin:

// ✔️ 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

Bir yapıda XML belgesi açıklamaları varsa, açıklamadan önce boş bir satır ekleyin.

// ✔️ 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 ve üye bildirimlerini biçimlendirme

Biçimlendirme let ve member bildirimler sırasında, genellikle bağlamanın sağ tarafı bir satıra gider veya (çok uzunsa) bir düzey girintili yeni bir satıra geçer.

Örneğin, aşağıdaki örnekler uyumludur:

// ✔️ 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

Bunlar uyumlu değil:

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

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

Kayıt türü örneklemeleri köşeli ayraçları kendi satırlarına da yerleştirebilir:

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

Ayrıca, bağlama adıyla aynı satırda açılan { stili kullanmayı Stroustrup da tercih edebilirsiniz:

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

Tek bir boş satır ve belgeyle üyeleri ayırın ve bir belge açıklaması ekleyin:

// ✔️ OK

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

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

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

İlişkili işlev gruplarını ayırmak için fazladan boş satırlar kullanılabilir ... Bir grup ilgili tek satır arasında boş satırlar atlanabilir (örneğin, bir dizi sahte uygulama). Mantıksal bölümleri belirtmek için işlevlerde boş çizgiler kullanın.

İşlev ve üye bağımsız değişkenlerini biçimlendirme

İşlev tanımlarken, her bağımsız değişkenin çevresinde boşluk kullanın.

// ✔️ 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

Uzun bir işlev tanımınız varsa, parametreleri yeni satırlara yerleştirin ve sonraki parametrenin girinti düzeyiyle eşleşecek şekilde girintileyin.

// ✔️ 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

Bu, tanımlama kümelerini kullanan üyeler, oluşturucular ve parametreler için de geçerlidir:

// ✔️ 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

Parametreler curried ise, karakteri herhangi bir dönüş türüyle birlikte yeni bir satıra yerleştirin = :

// ✔️ 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

Bu, parametre eklerken çok uzun satırlardan (dönüş türünün uzun ada sahip olması durumunda) ve daha az satır hasarından kaçınmanın bir yoludur.

Biçimlendirme işleci bildirimleri

İsteğe bağlı olarak bir işleç tanımını çevrelemek için boşluk kullanın:

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

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

ile * başlayan ve birden fazla karakter içeren herhangi bir özel işleç için, derleyici belirsizliğini önlemek için tanımın başına bir boşluk eklemeniz gerekir. Bu nedenle, tüm işleçlerin tanımlarını tek bir boşluk karakteriyle çevrelemenizi öneririz.

Kayıt bildirimlerini biçimlendirme

Kayıt bildirimleri için, varsayılan olarak tür tanımında öğesini dört boşlukla girintili { hale getirmeniz, etiket listesini aynı satırda başlatmanız ve varsa üyeleri belirteçle { hizalamanız gerekir:

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

Ayrıca, etiketlerin ek dört boşlukla girintili olduğu köşeli ayraçları kendi satırlarına yerleştirmeyi tercih etmek de yaygın bir durumdur:

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

öğesini, tür tanımınınStroustrup (stil) ilk satırının sonuna da koyabilirsiniz{:

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

Ek üyeler gerekiyorsa mümkün olduğunca kullanmayın 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

Bu stil kuralının istisnası, kayıtları stile Stroustrup göre biçimlendirmenizdir. Bu durumda, derleyici kuralları nedeniyle bir with arabirim uygulamak veya ek üyeler eklemek istiyorsanız anahtar sözcüğü gereklidir:

// ✔️ 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}"

Kayıt alanları Aligned için XML belgeleri eklendiğinde veya Stroustrup stil tercih edildiğinde ve üyeler arasına ek boşluk eklenmelidir:

// ❌ 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}"

Kayıtta arabirim uygulamaları veya üyeler bildiriyorsanız, açılış belirtecini yeni bir satıra ve kapanış belirtecini yeni bir satıra yerleştirmek tercih edilir:

// ✔️ 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

Bu kurallar anonim kayıt türü diğer adları için de geçerlidir.

Ayrımcı birleşim bildirimlerini biçimlendirme

Ayrımcı birleşim bildirimleri için tür tanımında | girintiyi dört boşlukla girin:

// ✔️ 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

Tek bir kısa birleşim olduğunda, baştaki |öğesini atlayabilirsiniz.

// ✔️ OK
type Address = Address of string

Daha uzun veya çok satırlı birleşim için, her birleşim alanını yeni bir satıra yerleştirin | ve her satırın sonundaki * ayırma ile birlikte yerleştirin.

// ✔️ 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

Belge açıklamaları eklendiğinde, her /// açıklamadan önce boş bir satır kullanın.

// ✔️ 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

Değişmez değer bildirimlerini biçimlendirme

özniteliğini Literal kullanan F# değişmez değerleri özniteliğini kendi satırına yerleştirmeli ve PascalCase adlandırmasını kullanmalıdır:

// ✔️ OK

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

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

özniteliğini değerle aynı satıra yerleştirmekten kaçının.

Modül bildirimlerini biçimlendirme

Yerel modüldeki kodun, modüle göre girintili olması gerekir, ancak en üst düzey modüldeki kod girintili olmamalıdır. Ad alanı öğelerinin girintili olması gerekmez.

// ✔️ 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

Biçimlendirme do bildirimleri

Tür bildirimlerinde, modül bildirimlerinde ve hesaplama ifadelerinde, veya kullanımı dodo! bazen yan etki işlemleri için gereklidir. Bunlar birden çok satıra yayıldığında, girintiyi ile let/let!tutarlı tutmak için girinti ve yeni bir satır kullanın. Aşağıda bir sınıfta kullanan do bir örnek verilmişti:

// ✔️ 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

Aşağıda iki girinti alanı kullanma örneği do! verilmiştir (çünkü do! dört girinti alanı kullanılırken yaklaşımlar arasında tesadüfi bir fark yoktur):

// ✔️ 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
}

Hesaplama ifade işlemlerini biçimlendirme

Hesaplama ifadeleri için özel işlemler oluştururken camelCase adlandırması kullanılması önerilir:

// ✔️ 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
    }

Modellenen etki alanı, adlandırma kuralının sonunda yönlendirilmelidir. Farklı bir kural kullanmak idiyomatikse, bunun yerine bu kural kullanılmalıdır.

bir ifadenin dönüş değeri bir hesaplama ifadesiyse, hesaplama ifadesi anahtar sözcük adını kendi satırına yerleştirmeyi tercih edin:

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

Hesaplama ifadesini bağlama adıyla aynı satıra yerleştirmeyi de tercih edebilirsiniz:

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

Tercihiniz ne olursa olsun, kod tabanınız genelinde tutarlı kalmayı hedeflemeniz gerekir. Biçimlendiriciler tutarlı kalmak için bu tercihi belirtmenize izin verebilir.

Biçimlendirme türleri ve tür ek açıklamaları

Bu bölümde biçimlendirme türleri ve tür ek açıklamaları açıklanmıştır. Bu, uzantılı imza dosyalarını biçimlendirmeyi .fsi içerir.

Türler için, bazı özel durumlar dışında, genel türler için ön ek söz dizimlerini ()Foo<T> tercih edin

F# hem genel türler yazma sonek stiline (örneğin, int list) hem de ön ek stiline (örneğin, list<int>) izin verir. Sonek stili yalnızca tek tür bağımsız değişkenle kullanılabilir. Beş özel tür dışında her zaman .NET stilini tercih edin:

  1. F# Listeleri için yerine sonek formunu kullanın. int listlist<int>
  2. F# Seçenekleri için yerine sonek formunu kullanın. int optionoption<int>
  3. F# Değer Seçenekleri için yerine sonek formunu kullanın. int voptionvoption<int>
  4. F# dizileri için veya int[]yerine array<int> sonek formunu kullanın. int array
  5. Başvuru Hücreleri için veya Ref<int>yerine ref<int> kullanınint ref.

Diğer tüm türler için ön ek formunu kullanın.

İşlev türlerini biçimlendirme

bir işlevin imzasını tanımlarken simgenin -> çevresinde boşluk kullanın:

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

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

Biçimlendirme değeri ve bağımsız değişken türü ek açıklamaları

Tür ek açıklamalarıyla değerleri veya bağımsız değişkenleri tanımlarken, simgeden : sonra boşluk kullanın, ancak önce kullanmayın:

// ✔️ 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

Çok satırlı tür ek açıklamalarını biçimlendirme

Tür ek açıklaması uzun veya çok satırlı olduğunda, bunları bir düzey girintili bir sonraki satıra yerleştirin.

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

Satır içi anonim kayıt türleri için stil de kullanabilirsiniz Stroustrup :

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

Biçimlendirme dönüş türü ek açıklamaları

İşlev veya üye dönüş türü ek açıklamalarında, simgeden önce ve sonra : boşluk kullanın:

// ✔️ 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

İmzalardaki biçimlendirme türleri

İmzalara tam işlev türleri yazarken, bazen bağımsız değişkenleri birden çok satıra bölmek gerekir. Dönüş türü her zaman girintilidir.

Yinelenen bir işlev için bağımsız değişkenler ile ayrılır *ve her satırın sonuna yerleştirilir.

Örneğin, aşağıdaki uygulamaya sahip bir işlevi göz önünde bulundurun:

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

İlgili imza dosyasında (.fsi uzantı) işlev, çok satırlı biçimlendirme gerektiğinde aşağıdaki gibi biçimlendirilebilir:

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

Benzer şekilde, curried işlevini de göz önünde bulundurun:

let SampleCurriedFunction arg1 arg2 arg3 arg4 = ...

İlgili imza dosyasında, -> her satırın sonuna yerleştirilir:

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

Benzer şekilde, curried ve tupled bağımsız değişkenlerinin bir karışımını alan bir işlev düşünün:

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

karşılık gelen imza dosyasında, bir tanımlama grubu tarafından önce gelen türler girintili

// ✔️ 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

Tür imzalarındaki üyeler için de aynı kurallar geçerlidir:

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

Açık genel tür bağımsız değişkenlerini ve kısıtlamalarını biçimlendirme

Aşağıdaki yönergeler işlev tanımları, üye tanımları, tür tanımları ve işlev uygulamaları için geçerlidir.

Çok uzun değilse, genel tür bağımsız değişkenlerini ve kısıtlamalarını tek bir satırda tutun:

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

Hem genel tür bağımsız değişkenleri/kısıtlamaları hem de işlev parametreleri sığmıyorsa ancak yalnızca tür parametreleri/kısıtlamaları uyuyorsa, parametreleri yeni satırlara yerleştirin:

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

Tür parametreleri veya kısıtlamaları çok uzunsa, aşağıda gösterildiği gibi bunları kırın ve hizalayın. Uzunluğu ne olursa olsun, tür parametrelerinin listesini işlevle aynı satırda tutun. Kısıtlamalar için ilk satıra yerleştirin when ve uzunluğu ne olursa olsun her kısıtlamayı tek bir satırda tutun. Son satırın sonuna yerleştirin > . Kısıtlamaları bir düzey girintili olarak belirleyin.

// ✔️ 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

Tür parametreleri/kısıtlamaları ayrılmışsa ancak normal işlev parametresi yoksa, ne olursa olsun öğesini yeni bir satıra = yerleştirin:

// ✔️ 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

aynı kurallar işlev uygulamaları için de geçerlidir:

// ✔️ 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

Devralmayı biçimlendirme

Temel sınıf oluşturucusunun bağımsız değişkenleri yan tümcesindeki bağımsız değişken listesinde inherit görünür. Yan tümcesini inherit bir düzey girintili yeni bir satıra yerleştirin.

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)

Oluşturucu uzun veya çok satırlı olduğunda, bunları bir düzey girintili bir sonraki satıra yerleştirin.
Bu çok satırlı oluşturucuyu çok satırlı işlev uygulamalarının kurallarına göre biçimlendirin.

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
        """)

Birincil oluşturucuyu biçimlendirme

Varsayılan biçimlendirme kurallarında, tür adı ile birincil oluşturucunun parantezleri arasına boşluk eklenmez.

// ✔️ 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

Birden çok oluşturucu

inherit Yan tümcesi bir kaydın parçası olduğunda, kısaysa aynı satıra koyun. Ayrıca, uzun veya çok satırlıysa bir düzey girintili bir sonraki satıra yerleştirin.

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 }

Öznitelikleri biçimlendirme

Öznitelikler bir yapının üzerine yerleştirilir:

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

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

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

Herhangi bir XML belgesinin arkasına geçmeleri gerekir:

// ✔️ OK

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

Parametrelerdeki öznitelikleri biçimlendirme

Öznitelikler parametrelere de yerleştirilebilir. Bu durumda, ardından parametresiyle aynı satıra ve adın önüne yerleştirin:

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

Birden çok özniteliği biçimlendirme

Parametre olmayan bir yapıya birden çok öznitelik uygulandığında, her özniteliği ayrı bir satıra yerleştirin:

// ✔️ OK

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

Bir parametreye uygulandığında, öznitelikleri aynı satıra yerleştirin ve bir ; ayırıcı ile ayırın.

İlgili kaynaklar

Bu yönergeler, Anh-Dung Phan tarafından F# Biçimlendirme Kuralları için kapsamlı bir kılavuza dayanır.