You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/fsharp/style-guide/component-design-guidelines.md
+28-28Lines changed: 28 additions & 28 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# F# component design guidelines
2
2
3
-
This document is a set of component design guidelines for F# programming, based on [the F# Component Design Guidelines, v14, Microsoft Research](fsharp-design-guidelines-v14.pdf) and [another version](http://fsharp.org/specs/component-design-guidelines/) originally curated and maintained by the F# Software Foundation.
3
+
This document is a set of component design guidelines for F# programming, based on [the F# Component Design Guidelines, v14, Microsoft Research, and [another version](http://fsharp.org/specs/component-design-guidelines/) originally curated and maintained by the F# Software Foundation.
4
4
5
5
This document assumes you are familiar with F# programming. Many thanks to the F# community for their contributions and helpful feedback on various versions of this guide.
6
6
@@ -25,7 +25,7 @@ There are a few universal guidelines that apply to F# libraries, regardless of t
25
25
26
26
Regardless of the kind of F# coding you are doing, it is valuable to have a working knowledge of the [.NET Library Design Guidelines](../../standard/design-guidelines/index.md). Most other F# and .NET programmers will be familiar with these guidelines, and expect .NET code to conform to them.
27
27
28
-
The .NET Library Design Guidelines provide a lot of general guidance regarding naming, designing classes and interfaces, member design (properties, methods, events, etc.) and more, and are a useful first point of reference for a variety of design guidance.
28
+
The .NET Library Design Guidelines provide general guidance regarding naming, designing classes and interfaces, member design (properties, methods, events, etc.) and more, and are a useful first point of reference for a variety of design guidance.
29
29
30
30
### Add XML documentation comments to your code
31
31
@@ -77,25 +77,25 @@ In addition to the previous table, be aware of the following:
77
77
78
78
#### Avoid abbreviations
79
79
80
-
The .NET guidelines discourage the use of abbreviations (for example, “use `OnButtonClick` rather than `OnBtnClick`”). Very common abbreviations, such as `Async` for “Asynchronous”, are tolerated. This guideline is sometimes ignored for functional programming; for example, `List.iter` uses an abbreviation for “iterate”. For this reason, using abbreviations tends to be tolerated to a greater degree in F#-to-F# programming, but should still generally be avoided in public component design.
80
+
The .NET guidelines discourage the use of abbreviations (for example, “use `OnButtonClick` rather than `OnBtnClick`”). Common abbreviations, such as `Async` for “Asynchronous”, are tolerated. This guideline is sometimes ignored for functional programming; for example, `List.iter` uses an abbreviation for “iterate”. For this reason, using abbreviations tends to be tolerated to a greater degree in F#-to-F# programming, but should still generally be avoided in public component design.
81
81
82
82
#### Avoid casing name collisions
83
83
84
-
The .NET guidelines say that casing alone cannot be used to disambiguate name collisions, since some client languages (e.g. Visual Basic) are case-insensitive.
84
+
The .NET guidelines say that casing alone cannot be used to disambiguate name collisions, since some client languages (for example, Visual Basic) are case-insensitive.
85
85
86
86
#### Use acronyms where appropriate
87
87
88
88
Acronyms such as XML are not abbreviations and are widely used in .NET libraries in uncapitalized form (Xml). Only well-known, widely recognized acronyms should be used.
89
89
90
90
#### Use PascalCase for generic parameter names
91
91
92
-
Do use PascalCase for generic parameter names in public APIs, including for F#-facing libraries. In particular, use names like `T`, `U`, `T1`, `T2` for arbitrary generic parameters, and when specific names make sense, then for F#-facing libraries use names like `Key`, `Value`, `Arg` (but not e.g.`TKey`).
92
+
Do use PascalCase for generic parameter names in public APIs, including for F#-facing libraries. In particular, use names like `T`, `U`, `T1`, `T2` for arbitrary generic parameters, and when specific names make sense, then for F#-facing libraries use names like `Key`, `Value`, `Arg` (but not for example,`TKey`).
93
93
94
94
#### Use either PascalCase or camelCase for public functions and values in F# modules
95
95
96
-
camelCase is generally used for public functions that are designed to be used unqualified (e.g. invalidArg), and for the “standard collection functions” (e.g. List.map). In both these cases, the function names act much like keywords in the language.
96
+
camelCase is used for public functions that are designed to be used unqualified (for example, `invalidArg`), and for the “standard collection functions” (for example, List.map). In both these cases, the function names act much like keywords in the language.
97
97
98
-
### Object, Type and Module Design
98
+
### Object, Type, and Module Design
99
99
100
100
#### Use namespaces or modules to contain your types and modules
101
101
@@ -134,9 +134,9 @@ The differences between using modules and namespaces to organize code at the top
134
134
* Namespaces can span multiple files
135
135
* Namespaces cannot contain F# functions unless they are within an inner module
136
136
* The code for any given module must be contained within a single file
137
-
* Top-level modules can contain F# functions without the neeed for an inner module
137
+
* Top-level modules can contain F# functions without the need for an inner module
138
138
139
-
The choice between a toplevel namespace or module affects the compiled form of the code, and thus will affect the view from other .NET languages should your API eventually be consumed outside of F# code.
139
+
The choice between a top-level namespace or module affects the compiled form of the code, and thus will affect the view from other .NET languages should your API eventually be consumed outside of F# code.
140
140
141
141
#### Use methods and properties for operations intrinsic to object types
142
142
@@ -227,7 +227,7 @@ Overuse of `[<AutoOpen>]` leads to polluted namespaces, and the attribute should
227
227
228
228
#### Consider defining operator members on classes where using well-known operators is appropriate
229
229
230
-
Sometimes classes are used to model mathematical constructus such as Vectors. When the domain being modeled has well-known operators, defining them as members intrinsic to the class is helpful.
230
+
Sometimes classes are used to model mathematical constructs such as Vectors. When the domain being modeled has well-known operators, defining them as members intrinsic to the class is helpful.
231
231
232
232
```fsharp
233
233
type Vector(x:float) =
@@ -247,7 +247,7 @@ This guidance corresponds to general .NET guidance for these types. However, it
247
247
248
248
#### Use method overloading for member functions, if doing so provides a simpler API
249
249
250
-
Method overloading is a powerful tool for simplifying an API which may need to do multiple related things.
250
+
Method overloading is a powerful tool for simplifying an API that may need to do multiple related things.
251
251
252
252
```fsharp
253
253
type Logger() =
@@ -264,7 +264,7 @@ Avoid revealing concrete representations of objects. For example, the concrete r
264
264
265
265
#### Avoid the use of implementation inheritance for extensibility
266
266
267
-
In F#, implementation inheritance is rarely used. Furthermore, inheritance heirarchies are often complex and difficult to change when new requirements arrive. Inheritance implementation still exists in F# for compatility and rare cases where it is the best solution to a problem, but alternative techniques should be sought in your F# programs when designing for polymorphism.
267
+
In F#, implementation inheritance is rarely used. Furthermore, inheritance hierarchies are often complex and difficult to change when new requirements arrive. Inheritance implementation still exists in F# for compatibility and rare cases where it is the best solution to a problem, but alternative techniques should be sought in your F# programs when designing for polymorphism.
268
268
269
269
### Function and Member Signatures
270
270
@@ -280,7 +280,7 @@ For return types containing many components, or where the components are related
280
280
281
281
#### Use `Async<T>` for async programming at F# API boundaries
282
282
283
-
If there is a corresponding synchronous operation named `Operation` that returns a `T`, then the async operation should be named `AsyncOperation` if it returns `Async<T>` or `OperationAsync` if it returns `Task<T>`. For commonly-used .NET types that expose Begin/End methods, consider using `Async.FromBeginEnd` to write extension methods as a façade to provide the F# async programming model to those .NET APIs.
283
+
If there is a corresponding synchronous operation named `Operation` that returns a `T`, then the async operation should be named `AsyncOperation` if it returns `Async<T>` or `OperationAsync` if it returns `Task<T>`. For commonlyused .NET types that expose Begin/End methods, consider using `Async.FromBeginEnd` to write extension methods as a façade to provide the F# async programming model to those .NET APIs.
284
284
285
285
```fsharp
286
286
type SomeType =
@@ -312,11 +312,11 @@ The .NET Library Design Guidelines give excellent advice on the use of exception
312
312
313
313
* Do not throw `System.Exception` when it will escape to user code. This includes avoiding the use of `failwith`, `failwithf`, which are handy functions for use in scripting and for code under development, but should be removed from F# library code in favor of throwing a more specific exception type.
314
314
315
-
* Use `nullArg`, `invalidArg` and `invalidOp` as the mechanism to throw `ArgumentNullException`, `ArgumentException` and `InvalidOperationException` when appropriate.
315
+
* Use `nullArg`, , and `invalidOp` as the mechanism to throw `ArgumentNullException`, , and `InvalidOperationException` when appropriate.
316
316
317
317
#### Consider using option values for return types when failure is not an exceptional scenario
318
318
319
-
The .NET approach to exceptions is that they should be “exceptional”; that is, they should occur relatively infrequently. However, some operations (for example, searching a table) may fail frequently. F# option values are an excellent way to represent the return types of these operations. These operations conventionally start with the name prefix “try”.
319
+
The .NET approach to exceptions is that they should be “exceptional”; that is, they should occur relatively infrequently. However, some operations (for example, searching a table) may fail frequently. F# option values are an excellent way to represent the return types of these operations. These operations conventionally start with the name prefix “try”:
320
320
321
321
```fsharp
322
322
// bad: throws exception if no element meets criteria
@@ -353,7 +353,7 @@ type System.Collections.Generic.IDictionary<'Key,'Value> with
353
353
354
354
#### Use discriminated unions instead of class hierarchies for tree-structured data
355
355
356
-
Tree-like structures are recursively-defined. This is awkward with inheritance, but quite elegant with Discriminated Unions.
356
+
Tree-like structures are recursivelydefined. This is awkward with inheritance, but elegant with Discriminated Unions.
This is a suitable function for a public API in a mathematical library.
413
413
414
-
#### Avoid using member constraints to simulate typeclasses and duck typing
414
+
#### Avoid using member constraints to simulate type classes and duck typing
415
415
416
-
It is possible to simulate “duck typing” using F# member constraints. However, members that make use of this should not in general be used in F#-to-F# library designs. This is because library designs based on unfamiliar or non-standard implicit constraints tend to cause user code to become inflexible and strongly tied to one particular framework pattern.
416
+
It is possible to simulate “duck typing” using F# member constraints. However, members that make use of this should not in general be used in F#-to-F# library designs. This is because library designs based on unfamiliar or non-standard implicit constraints tend to cause user code to become inflexible and tied to one particular framework pattern.
417
417
418
418
### Operator Definitions
419
419
420
420
#### Avoid defining custom symbolic operators
421
421
422
-
Custom operators are essential in some situations and are highly useful notational devices within a large body of implementation code. For new users of a library, named functions are often easier to use. In addition custom symbolic operators can be hard to document, and users find it more difficult to lookup help on operators, due to existing limitations in IDE and search engines.
422
+
Custom operators are essential in some situations and are highly useful notational devices within a large body of implementation code. For new users of a library, named functions are often easier to use. In addition custom symbolic operators can be hard to document, and users find it more difficult to look up help on operators, due to existing limitations in IDE and search engines.
423
423
424
-
As a result, it is generally best to publish your functionality as named functions and members, and additionally expose operators for this functionality only if the notational benefits outweight the documentation and cognitive cost of having them.
424
+
As a result, it is best to publish your functionality as named functions and members, and additionally expose operators for this functionality only if the notational benefits outweigh the documentation and cognitive cost of having them.
425
425
426
426
### Units of Measure
427
427
428
428
#### Use units of measure for added type safety in F# code
429
429
430
-
This type information is erased when viewed by other .NET languages, so be aware that .NET components, tools and reflection will just see types-sans-units (e.g.`float` rather than `float<kg>`) after this information has been erased.
430
+
This type information is erased when viewed by other .NET languages, so be aware that .NET components, tools and reflection will just see types-sans-units (for example,`float` rather than `float<kg>`) after this information has been erased.
431
431
432
432
### Type Abbreviations
433
433
@@ -437,9 +437,9 @@ Be aware that .NET components, tools and reflection will just see the types-bein
437
437
438
438
#### Avoid type abbreviations for public types whose members and properties should be intrinsically different to those available on the type being abbreviated
439
439
440
-
In this case, the type being abbreviated reveals too much about the representation of the actual type being defined. Instead, consider wrapping the abbreviation in a class type or a single-case discriminated union (or, when performance is absolutely essential, consider using a struct type to wrap the abbreviation).
440
+
In this case, the type being abbreviated reveals too much about the representation of the actual type being defined. Instead, consider wrapping the abbreviation in a class type or a single-case discriminated union (or, when performance is essential, consider using a struct type to wrap the abbreviation).
441
441
442
-
For example, it is tempting to define a multi-map as a special case of an F# map, e.g.
442
+
For example, it is tempting to define a multi-map as a special case of an F# map, for example:
443
443
444
444
```fsharp
445
445
type MultiMap<'Key,'Value> = Map<'Key,'Value list>
@@ -449,7 +449,7 @@ However, the logical dot-notation operations on this type are not the same as th
449
449
450
450
## Guidelines for Libraries for Use from other .NET Languages
451
451
452
-
When designing libraries for use from other .NET languages, it is very important to adhere to the [.NET Library Design Guidelines](../../standard/design-guidelines/index.md). In this document we label these libraries vanilla .NET libraries, as opposed to F#-facing libraries which use F# constructs without restriction and are mostly intended for use by F# applications. Designing vanilla .NET libraries means providing familiar and idiomatic APIs consistent with the rest of the .NET Framework by minimizing the use of F#-specific constructs in the public API. We propose the rules in the following sections.
452
+
When designing libraries for use from other .NET languages, it is important to adhere to the [.NET Library Design Guidelines](../../standard/design-guidelines/index.md). In this document, we label these libraries vanilla .NET libraries, as opposed to F#-facing libraries that use F# constructs without restriction and are mostly intended for use by F# applications. Designing vanilla .NET libraries means providing familiar and idiomatic APIs consistent with the rest of the .NET Framework by minimizing the use of F#-specific constructs in the public API. We propose the rules in the following sections.
453
453
454
454
### Namespace and Type Design (for libraries for use from other .NET Languages)
455
455
@@ -465,13 +465,13 @@ type PolarCoordinate = ...
465
465
member this.Theta = ...
466
466
```
467
467
468
-
#### Use namespaces, types and members as the primary organizational structure for your components
468
+
#### Use namespaces, types, and members as the primary organizational structure for your components
469
469
470
470
All files containing public functionality should begin with a `namespace` declaration, and the only public-facing entities in namespaces should be types. Do not use F# modules.
471
471
472
-
Use non-public modules to hold implementation code, utility types and utility functions.
472
+
Use non-public modules to hold implementation code, utility types, and utility functions.
473
473
474
-
Static types should be preferred over modules, as they allow for future evolution of the API to use overloading and other .NET API design concepts which may not be used within F# modules.
474
+
Static types should be preferred over modules, as they allow for future evolution of the API to use overloading and other .NET API design concepts that may not be used within F# modules.
475
475
476
476
For example, in place of the following public API:
477
477
@@ -850,6 +850,6 @@ The fixes we have made to prepare this type for use as part of a vanilla .NET li
850
850
851
851
* We used a return type of `seq<RadialPoint>` instead of `RadialPoint list` by changing a list construction using `[ ... ]` to a sequence construction using `IEnumerable<RadialPoint>`.
852
852
853
-
* We used the .NET delegate type System.Func instead of an F# function type.
853
+
* We used the .NET delegate type `System.Func` instead of an F# function type.
0 commit comments