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
,e1
vee2
kısadır. -
e1
vee2
ifadelerin kendileri değildirif/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 elif
ile else
birden çok koşullu, tek satırlı if
ifadelerin kurallarına uydukları zaman ile aynı kapsamda if/then/else
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 if
anahtar sözcüğü, then
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 x :: l
boşluklarla yazın ::
(::
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
, Aligned
ve 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ı ç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 function
let
tanımlanan işlevlerin let rec
kullanımı genel olarak bir match
lehine önlenmelidir. Kullanılırsa, desen kuralları anahtar sözcüğüyle function
hizalanmalı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 with
girintili 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
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 (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 Stroustrup
stili 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). 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ın{
(stil) ilk satırının sonuna da koyabilirsinizStroustrup
:
// ✔️ 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
Literal
ö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ı do
do!
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.
Altı özel tür dışında her zaman .NET stilini tercih edin:
- F# Listeleri için yerine sonek formunu kullanın.
int list
list<int>
- F# Seçenekleri için yerine sonek formunu kullanın.
int option
option<int>
- F# Değer Seçenekleri için yerine sonek formunu kullanın.
int voption
voption<int>
- F# dizileri için veya
int array
yerinearray<int>
sonek formunu kullanın.int[]
- Başvuru Hücreleri için veya
int ref
yerineref<int>
kullanınRef<int>
. - F# Dizileri için,
seq<int>
yerineint seq
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ü 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.