Members
Additional elements in type definition (class, record, union)
(Event)
Method
Property
Indexed property
Operator overload
Static and instance members
Static member: static member member-name ....
Instance member:
Concrete member:
member self-identifier.member-name ...Abstract member:
abstract member member-name: type-signatureVirtual member = requires 2 declarations
Abstract member
Default implementation:
default self-identifier.member-name ...
Override virtual member:
override self-identifier.member-name ...
β member-name in PascalCase (.NET convention)
β No protected member!
Self-identifier
In Cβ―, Java, TypeScript :
thisIn VB :
MeIn Fβ― : we can choose β
this,self,me, any valid identifier...
Declaration:
For the primary constructorβ: with
asβtype MyClass() as self = ...β οΈ Can be costly
For a member:
member me.Introduce() = printfn $"Hi, I'm {me.Name}"For a member not using it: with
_(since Fβ― 6) βmember _.Hi() = printfn "Hi!"
Call a member
Calling a static member
β Prefix with the type name: type-name.static-member-name
Calling an instance member inside the type
β Prefix with self-identifier: self-identifier.instance-member-name
Calling an instance member from outside the type
β Prefix with instance-name: instance-name.instance-member-name
Method
β Function attached directly to a type
2 forms of parameter declaration:
Curried parameters = FP style
Parameters in tuple = OOP style
Better interop with Cβ―
Only mode allowed for constructors
Support named, optional, arrayed parameters
Support overloads
β with required in β but not in β‘ because of indentation
β end can end the block started with with (not recommended)
β this.Price βΆ and me.Price β·
β Access to instance via self-identifier defined by member
Named arguments
Calls a tuplified method by specifying parameter names:
Useful to:
Clarify a usage for the reader or compiler (in case of overloads)
Choose the order of arguments
specify only certain arguments, the others being optional
β Arguments after a named argument must be named too.
Optional parameters
Allows you to call a tuplified method (including constructors) without specifying all the parameters.
Optional parameter:
Declared with
?in front of its name β?arg1: intIn the body of the method, wrapped in an
Optionβarg1: int optionYou can use
defaultArgto specify the default valueBut the default value does not appear in the signature!
When the method is called, the argument can be specified either:
Directly in its type β
Method(arg1 = 1)Wrapped in an
Optionif named with prefix?βMethod(?arg1 = Some 1)
β Other syntax for interop .NET: [<Optional; DefaultParameterValue(...)>] arg
Example:
β Notice the shadowing of parameters by variables of the same name
let parity (* bool *) = defaultArg parity (* bool option *) Full
.NET optional parameters
There is another possibility to declare optional parameters, based on attributes. It's less handy but required for .NET interop or for using other attributes on parameters.
[<Optional; DefaultParameterValue(...)>] arg
The Optional and DefaultParameterValue attributes are available in open System.Runtime.InteropServices.
Example: tracing the call to a function by retrieving its name from the CallerMemberName attribute in System.Runtime.CompilerServices (*)
(*) Documentation π : Caller information - F# | Microsoft Docs
Using |> pipe operator
|> pipe operatorYou can use |> with a method with:
1 parameter
2 parameters, the last of which is .NET optional
π‘ If we want a 3rd parameter, we have to write a sub-lambda, like currying the function ourselves:
Parameter array
Allows specifying a variable number of parameters of the same type
β Via System.ParamArray attribute on the last method argument
π‘ Equivalent of Cβ― public static T Max<T>(params T[] items)
Call Cβ― method TryXxx()
β How to call in Fβ― a Cβ― method bool TryXxx(args, out T outputArg)?
(Example: int.TryParse, IDictionnary::TryGetValue)
π Use Fβ― equivalent of
out outputArgbut use mutation π΅β Do not specify
outputArgargumentChange return type to tuple
bool * ToutputArgbecomes the 2nd element of this tuple
Call method Xxx(tuple)
β How do you call a method whose 1st parameter is itself a tuple?!
Let's try:
π‘ Explanation: TryGetValue(0,0) = method call in tuplified mode
β Specifies 2 parameters, 0 and 0.
β 0 is an int whereas we expect an int * int tuple!
Solutions
π Backward pipe, but also confusing
friendsLocation.TryGetValue <| (0,0)
π Double parentheses, but confusing syntax
friendsLocation.TryGetValue((0,0))
β Use a function rather than a method
friendsLocation |> Map.tryFind (0,0)
Method vs Function
Partial application
β yes
β yes
β no
Named arguments
β no
β no
β yes
Optional parameters
β no
β no
β yes
Params array
β no
β no
β yes
Overload
β no
β no
β yes 1οΈβ£
Notes
1οΈβ£ If possible, prefer optional parameters to overloads.
2οΈβ£ Declaration order:
Methods generally don't need to follow the top-down compilation rule.
But it's required in the case of generic members β See https://stackoverflow.com/q/66358718/8634147
We recommend declaring members from top to bottom, to ensure consistency with the rest of the code.
Method vs Function (2)
Naming
camelCase
PascalCase
PascalCase
Support for inline
β yes
β yes
β yes
Recursive
β
if rec
β yes
β yes
Inference of x in
f x β β
yes
K.M x β β
yes
x.M() β β no
Can be passed as argument
β
yes : g f
β
yes : g T.M
β no : g x.M 1οΈβ£
1οΈβ£ Alternatives:
β Fβ― 8: shorthand members β g _.M()
β Wrap in lambda β g (fun x -> x.M())
Static methods vs companion module
Companion module is more idiomatic β default choice.
Static methods are interesting in some use cases:
Usage easier due to optional parameters 1οΈβ£
Usage more readable due to named arguments 3οΈβ£
Usage terser to instanciate record with several fields:
Depending on your use of Fantomas and its configuration, multi-line record expression can be verbose
A factory method call is usually formatted in a single line, hence terser. 2οΈβ£
When field labels are necessary for code clarity, we can use named arguments.
Record expressions can be ambiguous: we are not sure of which type it is.
A factory method can help resolve ambiguity: we force to use it qualified, hence the type is explicit.
Properties
β Syntactic sugar hiding a getter and/or a setter β Allows the property to be used as if it were a field
There are 2 base ways to declare a property:
Getter
member this.Property = expression using this
β The expression is evaluated on each call.
Example:
member _.Property = expression involving side-effect
β This kind of property is generally not recommended. Prefer a method.
Automatic property
Automatic because a backing field is generated by the compiler.
Read-only
member val Property = value
public Type Property { get; }
Read/write
member val Property = value with get, set
public Type Property { get; set; }
β The property returns the same value on each call, mutation with the setter aside.
Example:
βοΈ PersonName is immutable and, as a structπ, has structural equality. It's the OO alternative to records.
Other cases
In other cases, the syntax is verbose: (details). π When possible, prefer methods as they are more explicit.
Properties and pattern matching
β οΈ Properties cannot be deconstructed
β Can only participate in pattern matching in when part
Indexed properties
Allows access by index, as if the class were an array: instance[index]
β Useful for an ordered collection, to hide the implementation
Set up by declaring the member Item
π‘ Property read-only (write-only) β declare only the getter (setter)
β Notice the setter parameters are curried
Example :
Slice
Same as indexed property, but with multiple indexes
Declaration: GetSlice(?start, ?end) method (regular or extension)
Usage: .. operator
Operator overload
Operator overloaded possible at 2 levels:
In a module, as a function
let [inline] (operator-symbols) parameter-list = ...π See session on functions
β Limited: only 1 definition possible
In a type, as a member
static member (operator-symbols) (parameter-list) =Same rules as for function form
π Multiple overloads possible (N types Γ P overloads)
Example:
Last updated
Was this helpful?