Aracılığıyla paylaş


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, takım deposuna eklenmiş kod biçimlendirici için üzerinde uzlaşılmalı ve ayarlar dosyasında kodlanmalıdır.

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.

Sekme kullanmak yerine boşluk kullanın.

Girintileme gerektiğinde, sekmeleri değil boşlukları kullanmanız gerekir. F# kodu sekmeleri kullanmaz ve string literal veya yorum dışında bir sekme karakteriyle karşılaşırsa derleyici bir hata verir.

Tutarlı girinti kullan

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.

Ancak, kodlama 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 çalışı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 sabitleri ve interpolasyonlu dizeler, uzunluğ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.

Tuple 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

Bir işlevin dönüş değeri bir demet olduğunda, parantezlerin ihmal edilmesi de yaygın olarak kabul edilir.

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

Özetle, parantezli tuple tanımlamalarını tercih edin, ancak desen eşleştirme veya dönüş değeri için tuple kullanırken parantezlerden kaçınmak uygun 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

Argümanlar gerektirmedikçe parantezleri kullanma.

// ✔️ 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, kümeli veya parantezli değişkenlere küçük harfle işlev uygulandığında bir boşluk eklenir (bu, tek bir değişken için bile geçerlidir).

// ✔️ 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, tuple bağımsız değişkenlere büyük harfle yazılmış metotlar 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)

Bu biçimlendirme kuralları desen eşleştirme için de geçerlidir. F# stili değerleri tutarlı biçimlendirme:

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

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

Bağımsız değişkenlerin listesi veya adları çok uzun olduğundan veya okunabilirlik açısından, bağımsız değişkenleri bir işlevde yeni bir satıra geçirmeniz gerekebilir. Bu durumda, bir seviyeyi girintileyin.

// ✔️ 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ı demet 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, yeni satırlar kullanın ve sol paranteze girinti eklemek yerine bir seviyeli girinti 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

Ters işlem hattı <| işleçleri için kısa ifadeleri tek bir satırda tutun. Satır uzunluğu metnin kaydırılmasını gerektiriyorsa, bağımsız değişkenleri yeni satırlara yerleştirin ve uyumlu bir biçimde hizalayın.

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

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 ele alın.

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

Lambda ifadeden önce birçok öncü veya çok satırlı bağımsız değişken olduğunda, tüm bağımsız değişkenleri bir seviyeye indente edin.

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

Yavaş ifadeleri biçimlendirme

Tek satırlı gecikmeli ifadeler yazarken her şeyi tek satırda tutun:

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

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

Çok satırlı gecikmeli ifadeler için, açma parantezini lazy anahtar kelimesiyle aynı satıra yerleştirin, ifade gövdesi bir düzey girintili ve kapanış parantezi açılış paranteziyle hizalanmış durumda olsun.

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

Bu, çok satırlı bağımsız değişkenlere sahip diğer işlev uygulamalarıyla aynı deseni izler. Sol parantez lazy ile kalır ve ifade bir seviye girintilenir.

Aritmetik ve ikili ifadeleri biçimlendirme

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

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

Bir ikili - operatörün çevrelenmemesi, belirli biçimlendirme seçenekleriyle birleştirildiğinde, bunun birli - olarak yorumlanmasına neden olabilir. Tekil - işleçlerin her zaman hemen ardından olumsuzladıkları değer gelmelidir.

// ✔️ 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ıralanması uygundur.

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

Aralık operatörü ifadelerini biçimlendirme

Yalnızca herhangi bir ifade atomik değilse, .. etrafına boşluk 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 kendileri if/then/else ifadeleri değildir.
// ✔️ 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

elif ve else ile birden çok koşullu, tek satırlık if/then/else ifadelerin kurallarına uyduklarında if ile aynı kapsamda 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. if ve then anahtar sözcükler, 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ı bir let bağlaması veya ayrı bir fonksiyona yeniden düzenlemek daha iyi bir stildir.

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

if performAction then
    e1
else
    e2

Birlik ifade durumlarını biçimlendirme

Ayrımcı birlik durumlarının uygulanması, fonksiyon ve metod uygulamalarıyla aynı kurallara uyar. Bu nedenle, ad büyük harfle yazıldığı için kod biçimlendiriciler bir tuple'dan önceki boşluğu 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

x :: l operatörünü etrafına boşluklar koyarak yazın :: (:: bir infix operatördür, dolayısıyla etrafında boşluklar bulunur).

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, demet 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
} ]

Diziler ve listeler program aracılığıyla oluşturulurken, bir değerin her zaman oluşturulması durumunda ->'i do ... yield'e tercih edin.

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

Eski F# sürümleri, verilerin koşullu olarak oluşturulabileceği veya ardışık ifadelerin değerlendirileceği durumlarda yield belirtilmesini gerektiriyordu. 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ı parantez 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 Aligned de Stroustrup 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ı satırda yer alır, kapatma köşeli ayracı ise kendi satırında yer 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şmedeki her cümle için girintisiz 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 biçimlendirmesi, ifade biçimlendirmesiyle tutarlı olmalıdır. Desen bağımsız değişkenlerinin parantezini açmadan önce boşluk eklemeyin:

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

Ancak, ifadelerde olduğu gibi desenlerdeki ayrı curried bağımsız değişkenler arasındaki boşlukları kullanın:

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

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 girintileyin, ardından match/|'den.

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

function kullanımı, let veya let rec tarafından tanımlanan işlevlerde genellikle match tercih edilmelidir. Kullanılırsa, kalıp kuralları function anahtar sözcüğüyle uyumlu olmalı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, with ile aynı düzeyde girintilenmiş 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ümce olmadığı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 member bir düzey girintili olacak şekilde hizalanmalıdır.

// ✔️ 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 tarzını kullanmayı 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

Sınırlayıcı simgeleri (<@, @>, <@@, @@>) tırnak içinde ifade çok satırlı bir ifadeyse 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 uzun olduğunda (fonksiyon çağrıları . ile iç içe geçmişse), her fonksiyon çağrısını bir sonraki satıra yerleştirin. Zincirdeki sonraki bağlantıları, önde gelen bağlantıdan itibaren bir düzey girinti oluşturacak şekilde düzenleyin.

// ✔️ 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 olması durumunda birden çok bağlantıdan oluşturulabilir. Örneğin, tam nitelikli 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 sırasında let ve member bildirimleri, genellikle bağlamanın sağ tarafı bir satırda yer alır veya (çok uzunsa) bir seviyeli girinti ile 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ının bulunduğu satırda açılan { ile birlikte Stroustrup stilini kullanmayı 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). İşlevlerde mantıksal bölümleri belirtmek için boş satırları mümkün olduğunca az kullanın.

Fonksiyon ve üye parametrelerini 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, = karakterini ve herhangi bir dönüş türünü 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 girintileyin, etiket listesini aynı satırda başlatın ve varsa üyeleri { belirteci ile hizalayın.

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

Ayrıca, etiketlerin ek dört boşlukla girintili olduğu köşeli parantezleri 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ın ilk satırının sonuna da koyabilirsiniz (Stroustrup stilinde):

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

XML belgeleri kayıt alanları için Aligned veya Stroustrup stili 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}"

Eğer bir kayıtta arabirim uygulamaları veya üyeleri tanımlıyorsanız, açılış belirtecini yeni bir satıra ve kapanış belirtecini yeni bir satıra yerleştirmek daha uygun olur.

// ✔️ 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ü takma 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 | dört boşlukla girintileyin:

// ✔️ 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ı bir birleşim için, | öğesini koruyarak her birleşim alanını yeni bir satıra yerleştirin ve her satırın sonuna * ayıracını ekleyin.

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

Sabit tanımlarını biçimlendirme

F# sabitleri kullanılırken, Literal özniteliği kendi satırına yerleştirilmeli ve PascalCase adlandırması kullanılmalı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

"do bildirimlerini biçimlendirme"

Tür bildirimlerinde, modül bildirimlerinde ve hesaplama ifadelerinde, do veya do! kullanımı bazen yan etki işlemleri için gereklidir. Birden çok satıra yayıldıklarında, let/let! ile uyumlu olacak şekilde girdileme ve yeni bir satır kullanın. İşte bir sınıfta do kullanan bir örnek:

// ✔️ 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ı belirlemelidir. 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, .fsi uzantılı imza dosyalarını biçimlendirmeyi 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#, genel türleri yazarken hem postfix stiline (örneğin, int list) hem de prefix stiline (örneğin, list<int>) izin verir. Postfix stili yalnızca bir tür bağımsız değişkenle kullanılabilir. Altı özel tür dışında her zaman .NET stilini tercih edin:

  1. F# Listeleri için sonek formunu şu şekilde kullanın: int list yerine list<int>.
  2. F# Seçenekleri için, option<int> yerine int option sonek formunu kullanın.
  3. F# Değer Seçenekleri için, voption<int> yerine int voption sonek biçimini kullanın.
  4. F# dizileri için int array sonek formunu, array<int> veya int[] yerine kullanın.
  5. Referans Hücreleri için int ref kullanın, ref<int> veya Ref<int> değil.
  6. F# Dizileri için, int seqyerine seq<int> sonek biçimini kullanın.

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ü notasyonları

Tür ek açıklamalarıyla değerleri veya bağımsız değişkenleri tanımlarken : simgesinden 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

Dönüş tipi anotasyonlarını biçimlendirme

İşlev veya üye dönüş türü ek açıklamalarında, : simgesinden ö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

Fonksiyon türlerini imzalara yazarken, bazen parametreleri birden çok satıra bölmek gerekir. Dönüş türü her zaman girintilidir.

Küme işlevi 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' fonksiyonu da 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 demet tarafından önce gelen türler girintili olarak yazılır.

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

Tip imzalarındaki üyeler için de aynı kurallar geçerlidir.

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

Açık genel tür argümanları ve kısıtlamalarını formatlama

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, her durumda = 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şturucusuna ait bağımsız değişkenler, inherit ifadesindeki bağımsız değişken listesinde görünür. Hükmü 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 Madde, bir kaydın içeriği olduğunda ve kısaysa, aynı satıra koyun. Ve uzun veya çok satırlıysa bir sonraki satıra, bir düzey girintili olarak, 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, onu parametreyle aynı satıra ve ismin ö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.

Teşekkür

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