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-signature
Virtual 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 :
this
In VB :
Me
In 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
Call 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 for :
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 are necessarily 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: int
In the body of the method, wrapped in an
Option
βarg1: int option
You can use
defaultArg
to 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
Option
if 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
(*)
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 you to specify a variable number of parameters of the same type
β Via System.ParamArray
attribute on 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 outputArg
but use mutation π΅β Do not specify
outputArg
argumentChange return type to tuple
bool * T
outputArg
becomes 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:
π‘ Explanations: 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.
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 of 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
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]
β Interesting for an ordered collection, to hide the implementation
Set up by declaring 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?