Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Ez a cikk útmutatást nyújt a kód úgy történő formázásához, hogy az F#-kód a következő legyen:
- Olvashatóbb
- A Visual Studio Code és más szerkesztők formázási eszközei által alkalmazott konvencióknak megfelelően
- Hasonló más online elérhető kódhoz
Lásd még a kódolási konvenciókat és az összetevők tervezési irányelveit, amelyek az elnevezési konvenciókat is ismertetik.
Automatikus kódformázás
A Fantomas kódformázó az automatikus kódformázás F# közösségi szabványeszköze. Az alapértelmezett beállítások megfelelnek ennek a stíluskalauznak.
Nagyon ajánljuk ennek a kódformázó eszköznek a használatát. Az F#-csapatokban a kódformázási specifikációkat meg kell egyeztetni és kodifikálni kell a csoportadattárba bevett kódformázási fájlban.
Általános formázási szabályok
Az F# alapértelmezés szerint jelentős üres területet használ, és a szabad terület érzékeny. Az alábbi iránymutatások útmutatást nyújtanak arra vonatkozóan, hogyan háríthatók el az ilyen jellegű kihívások.
Használj szóközöket, ne tabulátorokat
Ha behúzás szükséges, szóközöket kell használnia, nem tabulátorokat. Az F#-kód nem használ tabulátorokat, és a fordító hibaüzenetet ad, ha egy tabulátorkarakter egy sztring literálon vagy kommenten kívül jelenik meg.
Használj konzisztens behúzást
Behúzáskor legalább egy szóköz szükséges. A szervezet kódolási szabványokat hozhat létre a behúzáshoz használandó szóközök számának meghatározásához; Két, három vagy négy behúzási szóköz minden olyan szinten, ahol a behúzás történik, jellemző.
Behúzásonként négy szóközt ajánlunk.
Igaz, a programok behúzása szubjektív kérdés. A változatok rendben vannak, de az első szabály, amelyet követnie kell, a behúzás konzisztenciája. Válasszon egy általánosan elfogadott behúzási stílust, és használja azt szisztematikusan a kódbázisban.
Kerülje a névhosszra érzékeny formázást
Kerülje az elnevezésekre érzékeny igazítást és behúzást.
// ✔️ 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 _ -> ()
| ...
Ennek elkerülésének elsődleges okai a következők:
- A fontos kód jobbra van helyezve
- A tényleges kódnak kevesebb szélessége maradt
- Az átnevezés megszakíthatja az igazítást
Kerülje a felesleges szóközt
Az F#-kódban kerülje a felesleges szóközt, kivéve az ebben a stíluskalauzban leírtakat.
// ✔️ OK
spam (ham 1)
// ❌ Not OK
spam ( ham 1 )
Megjegyzések formázása
Több dupla perjeles megjegyzést részesíts előnyben a blokkhozzászólásokkal szemben.
// 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 megjegyzéseknek nagybetűssé kell tenni az első betűt, és jól formázott kifejezéseknek vagy mondatoknak kell lenniük.
// ✔️ A good comment.
let f x = x + 1 // Increment by one.
// ❌ two poor comments
let f x = x + 1 // plus one
Az XML-dokumentum megjegyzéseinek formázásához lásd alább a "Formázási deklarációk" című témakört.
Formázási kifejezések
Ez a szakasz a különböző típusú formázási kifejezéseket ismerteti.
Sztringkifejezések formázása
A karakterlánc literálok és az interpolált karakterláncok egyetlen sorban hagyhatók, függetlenül attól, hogy milyen hosszú a sor.
let serviceStorageConnection =
$"DefaultEndpointsProtocol=https;AccountName=%s{serviceStorageAccount.Name};AccountKey=%s{serviceStorageAccountKey.Value}"
A többsoros interpolált kifejezések nem támogatottak. Ehelyett kösse össze a kifejezés eredményét egy értékkel, és használja azt az interpolált sztringben.
Többjegyű kifejezések formázása
A tuple példányt zárójelbe kell tenni, és a benne lévő elválasztó vesszőket egyetlen szóköznek kell követnie, például: (1, 2), (x, y, z).
// ✔️ OK
let pair = (1, 2)
let triples = [ (1, 2, 3); (11, 12, 13) ]
Általánosan elfogadott, hogy kihagyni a zárójeleket a tuplák mintaillesztésében.
// ✔️ OK
let (x, y) = z
let x, y = z
// ✔️ OK
match x, y with
| 1, _ -> 0
| x, 1 -> 0
| x, y -> 1
Gyakran elfogadott a zárójelek kihagyása is, ha a tuple egy függvény visszatérési értéke.
// ✔️ OK
let update model msg =
match msg with
| 1 -> model + 1, []
| _ -> model, [ msg ]
Összefoglalva, előnyben részesítse a zárójeles tuple-példányokat, de ha tuple-öket használ mintaegyeztetéshez vagy visszatérési értékként, akkor rendben van elkerülni a zárójeleket.
Alkalmazáskifejezések formázása
Függvény vagy metódusalkalmazás formázásakor az argumentumok ugyanabban a sorban jelennek meg, amikor a sorszélesség lehetővé teszi:
// ✔️ OK
someFunction1 x.IngredientName x.Quantity
Hagyja ki a zárójeleket, kivéve, ha az argumentumok megkövetelik őket:
// ✔️ OK
someFunction1 x.IngredientName
// ❌ Not preferred - parentheses should be omitted unless required
someFunction1 (x.IngredientName)
// ✔️ OK - parentheses are required
someFunction1 (convertVolumeToLiter x)
Ne hagyja ki a szóközöket, ha több, curriált argumentummal rendelkező argumentumot invokált:
// ✔️ 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)
Az alapértelmezett formázási konvenciókban a rendszer szóközt ad hozzá, amikor kisbetűs függvényeket alkalmaz a csatolt vagy zárójeles argumentumokra (még akkor is, ha egyetlen argumentumot használ):
// ✔️ 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)
Az alapértelmezett formázási konvenciókban nem adnak hozzá szóközt, amikor nagybetűs módszereket alkalmaznak a tuplázott argumentumokra. Ennek az az oka, hogy ezeket gyakran használják folyékony programozással:
// ✔️ 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)
Ugyanezek a formázási konvenciók érvényesek a mintaegyezésre. F# stílusú értékek konzisztens formázása:
// ✔️ OK - Consistent formatting for expressions and patterns
let result = Some(value)
match result with
| Some(x) -> x
| None -> 0
Előfordulhat, hogy az argumentumokat át kell adnia egy új sorban lévő függvénynek az olvashatóság függvényében, vagy mert az argumentumok listája vagy az argumentumnevek túl hosszúak. Ebben az esetben egy szinttel beljebb húzni:
// ✔️ 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)
Ha a függvény egyetlen többsoros összefűző argumentumot használ, helyezze az egyes argumentumokat egy új sorba:
// ✔️ 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)
Ha az argumentumkifejezések rövidek, különítse el az argumentumokat szóközökkel, és tartsa egy sorban.
// ✔️ OK
let person = new Person(a1, a2)
// ✔️ OK
let myRegexMatch = Regex.Match(input, regex)
// ✔️ OK
let untypedRes = checker.ParseFile(file, source, opts)
Ha az argumentumkifejezések hosszúak, használjon új sorokat, és egy szinttel húzza be a szöveget, ne a bal zárójelhez igazodva.
// ✔️ 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)
Ugyanezek a szabályok akkor is érvényesek, ha csak egyetlen többsoros argumentum létezik, beleértve a többsoros sztringeket is:
// ✔️ 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" ]
)
Folyamatkifejezések formázása
Ha több sort használ, a folyamatkezelőknek |> az általuk használt kifejezések alá kell lépniük.
// ✔️ 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
A fordított folyamat <| operátorai esetében a rövid kifejezéseket egy sorban kell tartani. Ha a sor hossza tördelést igényel, helyezze az argumentumokat az új sorokra, és konzisztensen igazítsa őket:
// ✔️ 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-kifejezések formázása
Ha a lambda kifejezést argumentumként használják egy többsoros kifejezésben, és más argumentumok követik, helyezze egy lambda kifejezés törzsét egy új sorra, egy szinttel behúzva:
// ✔️ OK
let printListWithOffset a list1 =
List.iter
(fun elem ->
printfn $"A very long line to format the value: %d{a + elem}")
list1
Ha a lambda argumentum egy függvényalkalmazás utolsó argumentuma, helyezze az összes argumentumot addig, amíg a nyíl ugyanarra a sorra nem kerül.
// ✔️ 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}")
A lambda-kifejezéseket hasonló módon kezelje.
// ✔️ OK
functionName arg1 arg2 arg3 (function
| Choice1of2 x -> 1
| Choice2of2 y -> 2)
Ha a lambda előtt rengeteg kezdeti vagy többsoros argumentum található, az összes argumentumot egy szinttel kell behúzni.
// ✔️ OK
functionName
arg1
arg2
arg3
(fun arg4 ->
bodyExpr)
// ✔️ OK
functionName
arg1
arg2
arg3
(function
| Choice1of2 x -> 1
| Choice2of2 y -> 2)
Ha egy lambda-kifejezés törzse több sor hosszú, érdemes megfontolni, hogy egy helyi hatókörű függvényre bontsa át.
Ha a folyamatok lambdakifejezéseket tartalmaznak, az egyes lambdakifejezések általában az utolsó argumentumok a folyamat minden szakaszában:
// ✔️ 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}")
Ha a lambda argumentumai nem férnek el egyetlen sorban, vagy maguk is többsorosak, helyezze őket a következő sorba, egy szinttel behúzva.
// ✔️ 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 ()
Lusta kifejezések formázása
Egysoros lusta kifejezések írásakor minden egy sorban marad:
// ✔️ OK
let x = lazy (computeValue())
// ✔️ OK
let y = lazy (a + b)
Többsoros lusta kifejezések esetén helyezze a nyitó zárójelet a kulcsszóval megegyező sorra lazy , a kifejezés törzse egy szinttel behúzva, a záró zárójel pedig a megnyitáshoz igazodik:
// ✔️ 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
)
Ez ugyanazt a mintát követi, mint más többsoros argumentumokkal rendelkező függvényalkalmazások. A nyitó zárójel a(z) lazy elemmel marad, és a kifejezés egy szinttel van behúzva.
Aritmetikai és bináris kifejezések formázása
A bináris aritmetikai kifejezések körül mindig használjon fehér szóközt:
// ✔️ OK
let subtractThenAdd x = x - 1 + 3
Ha nem sikerül körülvenni egy bináris - operátort, bizonyos formázási lehetőségekkel kombinálva a függvényt unárisként -értelmezheti.
A nem kötelező - operátorokat mindig azonnal követnie kell az általuk tagadott értéknek:
// ✔️ OK
let negate x = -x
// ❌ Not OK
let negateBad x = - x
Ha az operátor után - szóköz karaktert ad hozzá, az zavart okozhat mások számára.
Bináris operátorok elkülönítése szóközök szerint. Az infix kifejezések rendben vannak ugyanazon az oszlopon való sorbaálláshoz:
// ✔️ OK
let function1 () =
acc +
(someFunction
x.IngredientName x.Quantity)
// ✔️ OK
let function1 arg1 arg2 arg3 arg4 =
arg1 + arg2 +
arg3 + arg4
Ez a szabály a típusok és állandó széljegyzetek mértékegységére is vonatkozik:
// ✔️ 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 következő operátorok az F# standard kódtárban vannak definiálva, és egyenértékűek definiálása helyett használhatók. Az operátorok használata ajánlott, mert a kód olvashatóbbá és jobban megfelel az idiomatikus stílusnak. Az alábbi lista az ajánlott F#-operátorokat foglalja össze.
// ✔️ 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
Tartomány operátorkifejezéseinek formázása
Csak akkor adjon hozzá szóközöket, .. ha bármely kifejezés nem atomi.
Az egész számok és az egyszavas azonosítók atominak minősülnek.
// ✔️ 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 ]
Ezek a szabályok a szeletelésre is vonatkoznak:
// ✔️ OK
arr[0..10]
list[..^1]
Feltételes kifejezések formázása
A feltételes kifejezések behúzása az őket alkotó kifejezések méretétől és összetettségétől függ. Írja őket egy sorba, amikor:
-
cond,e1ése2rövidek. -
e1ése2önmagukban nem kifejezések.
// ✔️ OK
if cond then e1 else e2
Ha a másik kifejezés hiányzik, javasoljuk, hogy soha ne írjon a teljes kifejezést egy sorba. Ennek célja, hogy megkülönböztesse az imperatív kódot a funkcionálistól.
// ✔️ OK
if a then
()
// ❌ Not OK, code formatters will reformat to the above by default
if a then ()
Ha bármelyik kifejezés többsoros, minden feltételes ágnak többsorosnak kell lennie.
// ✔️ OK
if cond then
let e1 = something()
e1
else
e2
// ❌ Not OK
if cond then
let e1 = something()
e1
else e2
Több feltételes elifelse feltétel ugyanazon a hatókörön belül van behúzva, mint if amikor az egysoros if/then/else kifejezések szabályait követik.
// ✔️ OK
if cond1 then e1
elif cond2 then e2
elif cond3 then e3
else e4
Ha bármelyik feltétel vagy kifejezés többsoros, a teljes if/then/else kifejezés többsoros:
// ✔️ 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
Ha egy feltétel többsoros vagy meghaladja az egysoros alapértelmezett tűréshatárt, a feltételkifejezésnek egy behúzást és egy új sort kell használnia.
A if és then kulcsszavaknak egymáshoz kell igazodniuk a hosszú feltételkifejezés beágyazásakor.
// ✔️ 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
Azonban jobb stílus a hosszú feltételek újrabontása egy let kötésre vagy különálló függvényre:
// ✔️ OK
let performAction =
complexExpression a b && env.IsDevelopment()
|| someFunctionToCall
aVeryLongParameterNameOne
aVeryLongParameterNameTwo
aVeryLongParameterNameThree
if performAction then
e1
else
e2
Unió típus kifejezések formázása
A diszkriminált uniós esetek alkalmazása ugyanazokat a szabályokat követi, mint a függvény- és metódusalkalmazások. Mivel a név nagy kezdőbetűvel írt, a kódformázók eltávolítanak egy szóközt a kettős előtt.
// ✔️ OK
let opt = Some("A", 1)
// OK, but code formatters will remove the space
let opt = Some ("A", 1)
A függvényalkalmazásokhoz hasonlóan a több sorra osztott szerkezeteknek is behúzást kell használniuk:
// ✔️ OK
let tree1 =
BinaryNode(
BinaryNode (BinaryValue 1, BinaryValue 2),
BinaryNode (BinaryValue 3, BinaryValue 4)
)
Lista és tömbkifejezések formázása
Írás x :: l szóközökkel az :: operátor körül (:: egy infix operátor, ezért szóközökkel körülvéve).
Az egy sorban deklarált listáknak és tömböknek szóközzel kell rendelkezniük a nyitó zárójel és a záró zárójel előtt:
// ✔️ OK
let xs = [ 1; 2; 3 ]
// ✔️ OK
let ys = [| 1; 2; 3; |]
Mindig használjon legalább egy szóközt két különböző zárójelhez hasonló operátor között. Például hagyjon szóközt az [ és a { között.
// ✔️ 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 }]
Ugyanez az útmutató vonatkozik a listákra vagy a tupolok tömbjeire is.
A több sorra osztott listák és tömbök a rekordokhoz hasonló szabályt követnek:
// ✔️ 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 |] |]
A rekordokhoz hasonlóan a nyitó és záró zárójelek saját sorban való használata megkönnyíti a kód mozgatását és a függvényekbe történő átirányítást.
// ✔️ 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 |]
|]
Ha egy lista- vagy tömbkifejezés a kötés jobb oldalán van, javasolt a következő stílust használni: Stroustrup
// ✔️ OK
let pascalsTriangle = [|
[| 1 |]
[| 1; 1 |]
[| 1; 2; 1 |]
[| 1; 3; 3; 1 |]
[| 1; 4; 6; 4; 1 |]
[| 1; 5; 10; 10; 5; 1 |]
[| 1; 6; 15; 20; 15; 6; 1 |]
[| 1; 7; 21; 35; 35; 21; 7; 1 |]
[| 1; 8; 28; 56; 70; 56; 28; 8; 1 |]
|]
Ha azonban egy lista- vagy tömbkifejezés nem a kötés jobb oldala, például ha egy másik listában vagy tömbben van, ha a belső kifejezésnek több sorra kell kiterjednie, a zárójeleknek a saját vonalaikra kell lépniük:
// ✔️ 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
] ]
Ugyanez a szabály vonatkozik a tömbök/listák rekordtípusaira:
// ✔️ 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
} ]
Tömbök és listák programozott létrehozásakor részesítsük előnyben a ->-t a do ... yield-tel szemben, ha egy érték mindig létrejön.
// ✔️ 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 ]
Az F# régebbi verzióiban meg kellett adni yield olyan helyzetekben, amikor az adatok feltételesen hozhatók létre, vagy egymást követő kifejezések kiértékelésére van szükség. Inkább kihagyja ezeket a yield kulcsszavakat, hacsak nem kell lefordítania egy régebbi F#-nyelvi verzióval:
// ✔️ 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"
]
Bizonyos esetekben do...yield segíthet az olvashatóságban. Ezeket az eseteket, bár szubjektív, figyelembe kell venni.
Rekordkifejezések formázása
A rövid rekordok egy sorban írhatók:
// ✔️ OK
let point = { X = 1.0; Y = 0.0 }
A hosszabb rekordoknak új sorokat kell használniuk a címkékhez:
// ✔️ OK
let rainbow =
{ Boss = "Jeffrey"
Lackeys = ["Zippy"; "George"; "Bungle"] }
Többsoros zárójel formázási stílusai
A több sorra kiterjedő rekordok esetében három gyakran használt formázási stílus létezik: Cramped, Alignedés Stroustrup. Az Cramped az F#-kód alapértelmezett stílusa, mert olyan stílusokat kedvel, amelyek lehetővé teszik a fordító számára, hogy könnyen elemezze a kódot. Mind Aligned a Stroustrup stílusok lehetővé teszik a tagok egyszerűbb átrendezését, ami a kód könnyebb újrabontását eredményezheti, azzal a hátránysal, hogy bizonyos helyzetekben kissé részletesebb kódra lehet szükség.
Cramped: Az előzményszabvány és az alapértelmezett F# rekordformátum. A nyitó zárójelek ugyanazon a sorban haladnak, mint az első tag, a záró zárójel ugyanazon a sorban, mint az utolsó tag.let rainbow = { Boss1 = "Jeffrey" Boss2 = "Jeffrey" Boss3 = "Jeffrey" Lackeys = [ "Zippy"; "George"; "Bungle" ] }Aligned: A kapcsos zárójelek mindegyike saját sorba kerül, ugyanarra az oszlopra igazítva.let rainbow = { Boss1 = "Jeffrey" Boss2 = "Jeffrey" Boss3 = "Jeffrey" Lackeys = ["Zippy"; "George"; "Bungle"] }Stroustrup: A nyitó zárójel ugyanazon a vonalon halad, mint a kötés, a záró zárójel saját vonalat kap.let rainbow = { Boss1 = "Jeffrey" Boss2 = "Jeffrey" Boss3 = "Jeffrey" Lackeys = [ "Zippy"; "George"; "Bungle" ] }
A lista- és tömbelemekre ugyanazok a formázási stílusszabályok vonatkoznak.
A rekordkifejezések másolásának és frissítésének formázása
A másolási és frissítési rekordkifejezések továbbra is rekordnak számítanak, ezért hasonló irányelvek érvényesek.
A rövid kifejezések egy sorban elférnek:
// ✔️ OK
let point2 = { point with X = 1; Y = 2 }
A hosszabb kifejezéseknek új sorokat kell használniuk, és a fent említett konvenciók egyikén alapuló formátumot kell használniuk:
// ✔️ 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 = ""
}
}
Megjegyzés: Ha a másolási és frissítési kifejezésekhez Stroustrup stílust használ, a tagokat be kell húznia a másolt rekordnévhez képest:
// ✔️ 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"
}
Formázási minták felismerése
Használjon | minden egyezési záradékhoz behúzás nélkül. Ha a kifejezés rövid, akkor érdemes lehet egyetlen sort használni, ha az egyes alkifejezések is egyszerűek.
// ✔️ 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"
A mintamegfeleltetés formázásának konzisztensnek kell lennie a kifejezésformázással. Ne adjon hozzá szóközt a mintaargumentumok nyitó zárójele előtt:
// ✔️ 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
Használjon azonban szóközöket a különböző, curriált argumentumok között a mintákban, ugyanúgy, mint a kifejezésekben:
// ✔️ 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
Ha a mintaillesztési nyíl jobb oldalán lévő kifejezés túl nagy, helyezze át a következő sorba, és húzza be egy szinttel az elejéről match/|.
// ✔️ OK
match lam with
| Var v -> 1
| Abs(x, body) ->
1 + sizeLambda body
| App(lam1, lam2) ->
sizeLambda lam1 + sizeLambda lam2
Hasonlóan a nagyobb if feltételekhez, ha egy egyező kifejezés többsoros, vagy meghaladja az egysoros alapértelmezett tűréshatárt, az egyező kifejezésnek egy behúzást és egy új sort kell használnia.
A match hosszú egyezés kifejezés beágyazásakor a kulcsszónak with igazodnia kell.
// ✔️ OK, but better to refactor, see below
match
complexExpression a b && env.IsDevelopment()
|| someFunctionToCall
aVeryLongParameterNameOne
aVeryLongParameterNameTwo
aVeryLongParameterNameThree
with
| X y -> y
| _ -> 0
A hosszú egyezés kifejezéseit azonban jobb stílus újra átalakítani egy let kötés vagy egy különálló függvény használatával.
// ✔️ OK
let performAction =
complexExpression a b && env.IsDevelopment()
|| someFunctionToCall
aVeryLongParameterNameOne
aVeryLongParameterNameTwo
aVeryLongParameterNameThree
match performAction with
| X y -> y
| _ -> 0
Kerülni kell a mintázatilleszkedés nyílainak igazítását.
// ✔️ 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
A kulcsszó function használatával bevezetett mintaegyeztetésnek egy szintet kell behúznia az előző sor elejéről:
// ✔️ OK
lambdaList
|> List.map (function
| Abs(x, body) -> 1 + sizeLambda 0 body
| App(lam1, lam2) -> sizeLambda (sizeLambda 0 lam1) lam2
| Var v -> 1)
Általában kerülendő, hogy a function-t használják a let vagy a let rec által meghatározott függvényekben, egy match helyett. Használat esetén a mintaszabályoknak a következő kulcsszóhoz functionkell igazodniuk:
// ✔️ 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 kifejezések formázása
A kivételtípushoz tartozó mintamegfeleltetéseket a kivételtípussal megegyező szinten withkell behúzni.
// ✔️ 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"
Adjon hozzá egy-egy | záradékot, kivéve, ha csak egyetlen záradék van:
// ✔️ 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
Elnevezett argumentumok formázása
A névvel ellátott argumentumoknak szóközöknek kell körülvennie a következőt =:
// ✔️ 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)
Ha a mintaegyeztetésekhez diszkriminált uniókat használunk, a névvel ellátott minták hasonló formátumban jelennek meg, például.
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
Mutációs kifejezések formázása
A mutációs location <- expr kifejezések általában egy sorban vannak formázva.
Ha többsoros formázásra van szükség, helyezze a jobb oldali kifejezést egy új sorra.
// ✔️ 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
Objektumkifejezések formázása
Az objektumkifejezések tagjainak igazodniuk kell úgy, hogy a member elemet egy szinttel beljebb húzzák.
// ✔️ 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) }
Használhatja a Stroustrup stílust is:
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)
}
Az üres típusdefiníciók egy sorban formázhatók:
type AnEmptyType = class end
A választott oldalszélességtől = class end függetlenül mindig ugyanabban a sorban kell lennie.
Index-/szeletkifejezések formázása
Az indexkifejezések nem tartalmazhatnak szóközöket a nyitó és záró zárójelek körül.
// ✔️ OK
let v = expr[idx]
let y = myList[0..1]
// ❌ Not OK
let v = expr[ idx ]
let y = myList[ 0 .. 1 ]
Ez a régebbi expr.[idx] szintaxisra is vonatkozik.
// ✔️ OK
let v = expr.[idx]
let y = myList.[0..1]
// ❌ Not OK
let v = expr.[ idx ]
let y = myList.[ 0 .. 1 ]
Idézett kifejezések formázása
A határolójeleket (<@, , @>, <@@) @@>külön sorokba kell helyezni, ha az idézett kifejezés többsoros kifejezés.
// ✔️ OK
<@
let f x = x + 10
f 20
@>
// ❌ Not OK
<@ let f x = x + 10
f 20
@>
Az egysoros kifejezésekben a határolójeleket ugyanazon a vonalon kell elhelyezni, mint maga a kifejezés.
// ✔️ OK
<@ 1 + 1 @>
// ❌ Not OK
<@
1 + 1
@>
Láncolt kifejezések formázása
Ha a láncolt kifejezések (összefonódva a .-vel) hosszúak, minden függvényhívást külön sorba tegyünk.
Az első hivatkozás után a lánc további hivatkozásait húzzuk be egy szinttel.
// ✔️ OK
Host
.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(fun webBuilder -> webBuilder.UseStartup<Startup>())
// ✔️ OK
Cli
.Wrap("git")
.WithArguments(arguments)
.WithWorkingDirectory(__SOURCE_DIRECTORY__)
.ExecuteBufferedAsync()
.Task
Az elsődleges hivatkozás több hivatkozásból is állhat, ha ezek egyszerű azonosítók. Például egy teljesen minősített névtér hozzáadása.
// ✔️ OK
Microsoft.Extensions.Hosting.Host
.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(fun webBuilder -> webBuilder.UseStartup<Startup>())
Az ezt követő hivatkozásoknak egyszerű azonosítókat is tartalmazniuk kell.
// ✔️ 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))
Ha a függvényalkalmazásban lévő argumentumok nem férnek el a sor többi részén, minden argumentumot a következő sorba kell helyezni.
// ✔️ 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
A lambda argumentumoknak a függvényalkalmazáson belül a nyitóval (megegyező sorban kell kezdődnie.
// ✔️ OK
builder
.WithEnvironment()
.WithLogger(fun loggerConfiguration ->
// ...
())
// ❌ Not OK, formatting tools will reformat to the above
builder
.WithEnvironment()
.WithLogger(
fun loggerConfiguration ->
// ...
())
Formázási deklarációk
Ez a szakasz a különböző típusú formázási deklarációkat ismerteti.
Üres sorok hozzáadása deklarációk között
Különítse el a legfelső szintű függvényeket és osztálydefiníciókat egyetlen üres sortal. Példa:
// ✔️ 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
Ha egy szerkezethez XML-dokumentum megjegyzései vannak, adjon hozzá egy üres sort a megjegyzés elé.
// ✔️ OK
/// This is a function
let thisFunction() =
1 + 1
/// This is another function, note the blank line before this line
let thisFunction() =
1 + 1
Formázási let és tagdeklarációk
Formázás let és member deklarációk esetén a kötés jobb oldala általában vagy egy sorra, vagy (ha túl hosszú) egy szinten behúzott új sorra kerül.
A következő példák például megfelelőek:
// ✔️ 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
Ezek nem megfelelőek:
// ❌ Not OK, code formatters will reformat to the above by default
let a = """
foobar, long string
"""
let d = while f do
printfn "%A" x
A rekordtípus-példányok a szögletes zárójeleket is elhelyezhetik a saját vonalaikon:
// ✔️ OK
let bilbo =
{
Name = "Bilbo"
Age = 111
Region = "The Shire"
}
Előnyben részesítheti a Stroustrup stílust, ha a { kezdés a kötés nevével azonos sorban van.
// ✔️ OK
let bilbo = {
Name = "Bilbo"
Age = 111
Region = "The Shire"
}
Különítse el a tagokat egyetlen üres sortal és dokumentummal, és adjon hozzá egy dokumentációs megjegyzést:
// ✔️ OK
/// This is a thing
type ThisThing(value: int) =
/// Gets the value
member _.Value = value
/// Returns twice the value
member _.TwiceValue() = value*2
További üres sorok használhatók (takarékosan) a kapcsolódó függvények csoportjainak elkülönítéséhez. Üres sorokat hagyhat ki egy csomó kapcsolódó egysoros kódsor (például tesztimplementációk) között. Használjon üres sorokat a függvényekben, takarékosan, a logikai szakaszok jelzéséhez.
Formázási függvény és tagargumentumok
Függvények definiálásakor használjon üres szóközt az egyes argumentumok köré.
// ✔️ 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
Ha hosszú függvénydefinícióval rendelkezik, helyezze a paramétereket új sorokra, és húzza be őket a következő paraméter behúzási szintjéhez igazítva.
// ✔️ 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
Ez a tagokra, konstruktorokra és paraméterekre is vonatkozik, amelyek tuple-öket használnak.
// ✔️ 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
Ha a paraméterek curriáltak, helyezze a = karaktert a visszatérési típussal együtt egy új sorba:
// ✔️ 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
Így elkerülheti a túl hosszú sorokat (abban az esetben, ha a visszatérési típus hosszú névvel rendelkezik), és a paraméterek hozzáadásakor kevesebb sorsérülést okoz.
Formázási operátor deklarációi
Ha szeretné, használjon üres területet egy operátordefiníció körül:
// ✔️ OK
let ( !> ) x f = f x
// ✔️ OK
let (!>) x f = f x
Minden olyan egyéni operátor esetében, amely *-vel kezdődik, és egynél több karakterből áll, a fordító kétértelműségének elkerülése érdekében szóközt kell hozzáadnia a definíció elejéhez. Ezért azt javasoljuk, hogy egyszerűen vegye körül az összes operátor definícióját egyetlen szóköz karakterrel.
Rekorddeklarációk formázása
Rekorddeklarációk esetén alapértelmezés szerint négy szóközzel kell behúzni a { típusdefiníciót, ugyanabban a sorban elindítani a címkelistát, és a tagokat (ha vannak) a { jogkivonathoz igazítani:
// ✔️ OK
type PostalAddress =
{ Address: string
City: string
Zip: string }
Az is gyakori, hogy inkább szögletes zárójeleket helyeznek a saját vonalukra, és a címkéket további négy szóköz húzja be:
// ✔️ OK
type PostalAddress =
{
Address: string
City: string
Zip: string
}
A {-t a típusdefiníció első sora végére is elhelyezheti (Stroustrup stílus).
// ✔️ OK
type PostalAddress = {
Address: string
City: string
Zip: string
}
Ha további tagokra van szükség, ne használja with/end , ha lehetséges:
// ✔️ 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
A rekordok Stroustrup stílus szerinti formázása kivételt képez ez alól a stílusszabály alól. Ebben az esetben a fordítószabályok miatt a with kulcsszóra akkor van szükség, ha egy felületet szeretne implementálni, vagy további tagokat szeretne hozzáadni:
// ✔️ 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}"
Ha XML dokumentációt ad hozzá a rekordmezőkhöz, a Aligned vagy Stroustrup stílust kell előnyben részesíteni, és további szóközöket kell elhelyezni a tagok között.
// ❌ 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}"
A nyitó jogkivonat új sorra helyezése és a záró jogkivonat új sorra helyezése akkor előnyös, ha felületi implementációkat vagy tagokat deklarál a rekordon:
// ✔️ 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
Ugyanezek a szabályok vonatkoznak a névtelen rekordtípus-aliasokra is.
Diszkriminált unió deklarációk formázása
Diszkriminált uniók deklarálása esetén a típusdefiníciót négy szóközzel kell behúzni | :
// ✔️ 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
Ha egyetlen rövid egyesítés van, kihagyhatja a vezetőt |.
// ✔️ OK
type Address = Address of string
Hosszú vagy többsoros egyesítés esetén tartsa meg a | jelölőt, és minden egyesítési mezőt írjon új sorba, a sor végére pedig helyezze el az elválasztót *.
// ✔️ 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
Dokumentációs megjegyzések hozzáadásakor minden megjegyzés előtt /// használjon egy üres sort.
// ✔️ 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
Literális deklarációk formázása
Az attribútumot használó F#-literáloknak az Literal attribútumot a saját sorukra kell helyeznie, és PascalCase-elnevezést kell használniuk:
// ✔️ OK
[<Literal>]
let Path = __SOURCE_DIRECTORY__ + "/" + __SOURCE_FILE__
[<Literal>]
let MyUrl = "www.mywebsitethatiamworkingwith.com"
Ne helyezze az attribútumot ugyanabba a sorba, mint az érték.
Formázási modul deklarációi
A helyi modul kódját be kell húzni a modulhoz képest, de a legfelső szintű modul kódját nem szabad behúzni. A névtérelemeket nem kell behúzni.
// ✔️ 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
Formázási "do" deklarációk
Típusdeklarációkban, moduldeklarációkban és számítási kifejezésekben néha szükséges do vagy do! alkalmazása mellékhatásos műveletekhez.
Amikor ezek több sorra terjednek, használjon behúzást és új sort, hogy a behúzás konzisztens legyen a let/let!-al. Íme egy példa az osztályban való használatra do :
// ✔️ OK
type Foo() =
let foo =
fooBarBaz
|> loremIpsumDolorSitAmet
|> theQuickBrownFoxJumpedOverTheLazyDog
do
fooBarBaz
|> loremIpsumDolorSitAmet
|> theQuickBrownFoxJumpedOverTheLazyDog
// ❌ Not OK - notice the "do" expression is indented one space less than the `let` expression
type Foo() =
let foo =
fooBarBaz
|> loremIpsumDolorSitAmet
|> theQuickBrownFoxJumpedOverTheLazyDog
do fooBarBaz
|> loremIpsumDolorSitAmet
|> theQuickBrownFoxJumpedOverTheLazyDog
Íme egy példa do! két behúzási szóköz használatával (mivel do! négy behúzási szóköz használatakor véletlenül nincs különbség a megközelítések között):
// ✔️ 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
}
Számítási kifejezések formázása
A számítási kifejezések egyéni műveleteinek létrehozásakor a CamelCase elnevezés használata javasolt:
// ✔️ 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
}
A modellezett tartománynak végső soron az elnevezési konvenciót kell vezetnie. Ha egy másik konvenció használata az elterjedt, akkor azt kell alkalmazni.
Ha egy kifejezés visszatérési értéke számítási kifejezés, inkább a számítási kifejezés kulcsszónevét helyezze a saját sorára:
// ✔️ OK
let foo () =
async {
let! value = getValue()
do! somethingElse()
return! anotherOperation value
}
A számítási kifejezést a kötés nevével megegyező sorba is helyezheti:
// ✔️ OK
let foo () = async {
let! value = getValue()
do! somethingElse()
return! anotherOperation value
}
Bármelyiket is szeretné, törekedjen arra, hogy konzisztens maradjon a kódbázisban. A formázók lehetőséget adhatnak arra, hogy ezt a preferenciát a következetesség érdekében adja meg.
Formázási típusok és szövegjegyzetek
Ez a szakasz a formázási típusokat és a szövegjegyzeteket ismerteti. Az aláírásfájlok formázása a .fsi kiterjesztéssel ide tartozik.
Típusok esetén előnyben részesítse az általános (Foo<T>) előtagszintaxisát néhány konkrét kivétellel
Az F# lehetővé teszi az általános típusok írásának postfix stílusát (például int list) és az előtagstílust (például list<int>).
A postfix stílus csak egyetlen típusargumentummal használható.
Mindig a .NET stílust részesíti előnyben, kivéve hat konkrét típust:
- F#-listák esetén használja a postfix formátumot:
int listahelyett, hogylist<int>. - Az F# beállításoknál a
int optionpostfix formátum használata ajánlott aoption<int>helyett. - F# értékopciókhoz használja a postfix formát:
int voptionahelyett, hogyvoption<int>-t használná. - F#-tömbök esetén használja a postfix formát:
int arrayahelyett, hogyarray<int>vagyint[]lenne. - Hivatkozáscellák esetén használja
int refref<int>ahelyett vagyRef<int>. - F#-sorozatok esetén használja a következő postfix űrlapot:
int seqseq<int>helyett.
Más típusok esetén használja az előtag formáját.
Formázási függvénytípusok
Egy függvény aláírásának meghatározásakor használjon fehér szóközt a -> szimbólum körül:
// ✔️ OK
type MyFun = int -> int -> string
// ❌ Not OK
type MyFunBad = int->int->string
Formázási érték és argumentumtípus-széljegyzetek
Ha típusjegyzetekkel definiál értékeket vagy argumentumokat, használjon üres szóközt a : szimbólum után, de előtte ne:
// ✔️ 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
Többsoros típusú széljegyzetek formázása
Ha egy szöveg széljegyzete hosszú vagy többsoros, helyezze őket a következő sorba, egy szinttel behúzva.
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
Beágyazott névtelen rekordtípusok esetén a stílust is használhatja Stroustrup :
let f
(x: {|
x: int
y: AReallyLongTypeThatIsMuchLongerThan40Characters
|})
=
x
Formázás: visszatérési típusú annotációk
A függvényben vagy tagban a visszatérési típus annotációkban használjon üres szóközt a : szimbólum előtt és utá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
Formázási típusok az aláírásokban
Ha teljes függvénytípusokat ír aláírásokban, néha szükség van az argumentumok több sorra való felosztására. A visszatérési típus mindig be van húzva.
A tupled függvénynél az argumentumokat a sorok végén elhelyezett * választja el egymástól.
Vegyünk például egy függvényt a következő implementációval:
let SampleTupledFunction(arg1, arg2, arg3, arg4) = ...
A megfelelő aláírásfájlban (.fsi kiterjesztésben) a függvény a következőképpen formázható, ha többsoros formázásra van szükség:
// ✔️ OK
val SampleTupledFunction:
arg1: string *
arg2: string *
arg3: int *
arg4: int ->
int list
Hasonlóképpen fontolja meg a curried függvényt is:
let SampleCurriedFunction arg1 arg2 arg3 arg4 = ...
A megfelelő aláírási fájlban a -> sorokat az egyes sorok végén helyezik el:
// ✔️ OK
val SampleCurriedFunction:
arg1: string ->
arg2: string ->
arg3: int ->
arg4: int ->
int list
Hasonlóképpen fontolja meg egy olyan függvényt, amely a curried és a tupled argumentumok kombinációját használja:
// Typical call syntax:
let SampleMixedFunction
(arg1, arg2)
(arg3, arg4, arg5)
(arg6, arg7)
(arg8, arg9, arg10) = ..
A megfelelő aláírási fájlban a tuple előtt lévő típusok be vannak húzva
// ✔️ 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
Ugyanezek a szabályok vonatkoznak a típusjelekben található tagokra is.
type SampleTypeName =
member ResolveDependencies:
arg1: string *
arg2: string ->
string
Explicit általános típusú argumentumok és megkötések formázása
Az alábbi irányelvek a függvénydefiníciókra, tagdefiníciókra, típusdefiníciókra és függvényalkalmazásokra vonatkoznak.
Ha nem túl hosszú, tartsa meg az általános típusargumentumokat és korlátozásokat egy sorban:
// ✔️ OK
let f<'T1, 'T2 when 'T1: equality and 'T2: comparison> param =
// function body
Ha az általános típusargumentumok/kényszerek és függvényparaméterek nem felelnek meg, de a típusparaméterek/kényszerek önmagukban igen, helyezze a paramétereket az új sorokra:
// ✔️ OK
let f<'T1, 'T2 when 'T1: equality and 'T2: comparison>
param
=
// function body
Ha a típusparaméterek vagy kényszerek túl hosszúak, bontsa meg és igazítsa őket az alább látható módon. A típusparaméterek listájának megőrzése a függvényével megegyező sorban, a hosszától függetlenül. Korlátozások esetén helyezze when az első sorba, és tartsa az egyes korlátozásokat egyetlen sorban, függetlenül annak hosszától. Helyezze > az utolsó sor végére. Húzza be a kényszereket egy szinttel.
// ✔️ 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
Ha a típusparaméterek/megkötések fel vannak bontva, de nincsenek normál függvényparaméterek, helyezze az = új sorra, függetlenül attól, hogy:
// ✔️ 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
A függvényalkalmazásokra ugyanazok a szabályok vonatkoznak:
// ✔️ 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
Formázás öröklése
Az alaposztály-konstruktor argumentumai megjelennek a záradék argumentumlistájában inherit .
Helyezze a inherit záradékot egy új sorra, egy szinttel behúzva.
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)
Ha a konstruktor hosszú vagy többsoros, helyezze őket a következő sorba, egy szinttel behúzva.
Formázza ezt a többsoros konstruktort a többsoros függvényalkalmazások szabályai szerint.
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
""")
Az elsődleges konstruktor formázása
Az alapértelmezett formázási konvenciókban nincs hely a típusnév és az elsődleges konstruktor zárójelei között.
// ✔️ 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
Több konstruktor
Ha a inherit záradék egy rekord része, tegye ugyanarra a sorra, ha rövid.
És helyezze a következő sorba, egy szinttel beljebb, ha az hosszú vagy többsoros.
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 }
Formázási attribútumok
Az attribútumok egy szerkezet fölé kerülnek:
// ✔️ OK
[<SomeAttribute>]
type MyClass() = ...
// ✔️ OK
[<RequireQualifiedAccess>]
module M =
let f x = x
// ✔️ OK
[<Struct>]
type MyRecord =
{ Label1: int
Label2: string }
Utána kell menniük bármelyik XML-dokumentációnak.
// ✔️ OK
/// Module with some things in it.
[<RequireQualifiedAccess>]
module M =
let f x = x
Paraméterek formázási attribútumai
Az attribútumok paraméterekre is elhelyezhetők. Ebben az esetben helyezze a paraméter és a név elé a következő sort:
// ✔️ OK - defines a class that takes an optional value as input defaulting to false.
type C() =
member _.M([<Optional; DefaultParameterValue(false)>] doSomething: bool)
Több attribútum formázása
Ha több attribútumot alkalmaz egy olyan szerkezetre, amely nem paraméter, helyezze az egyes attribútumokat egy külön sorba:
// ✔️ OK
[<Struct>]
[<IsByRefLike>]
type MyRecord =
{ Label1: int
Label2: string }
Ha paraméterre van alkalmazva, helyezze az attribútumokat ugyanarra a sorra, és különítse el őket egy ; elválasztóval.
Köszönetnyilvánítás
Ezek az irányelvek Anh-Dung Phan F#-formázási konvenciókra vonatkozó átfogó útmutatóján alapulnak.