I understand the tick to signify a generic parameter, as in:
Seq.append : seq<'T> -> seq<'T> -> seq<'T>
but what does the caret signify, as in:
Seq.average : seq<^T> -> ^T
I understand the tick to signify a generic parameter, as in:
Seq.append : seq<'T> -> seq<'T> -> seq<'T>
but what does the caret signify, as in:
Seq.average : seq<^T> -> ^T
The detailed signature is:
Seq.average : seq<^T> -> ^T (requires ^T with static member (+) and ^T with static member DivideByInt and ^T with static member Zero)
Unlike Seq.append
, Seq.average needs some more constraints on type of elements. Particularly:
_ DivideByInt (s1 + s2 + ... + sn) n where n <> 0
Seq.average {s1; s2;...; sn} = /
\_ ^T.Zero where n = 0
As you can see, both (+)
, DivideByInt
and Zero
are required in order that Seq.average<^T>
makes sense.
Useful information about generics could be found hereMSDN.
The caret indicates that the type parameter must be statically resolved, usually because there are particular constraints on the type that must be satisfied and which can't be expressed in normal .NET metadata. For instance, you can't call Seq.average "test"
even though "test"
is a seq<char>
, because char
s don't support the necessary arithmetic operations.
These statically resolved type variables only arise from inline
defintions, and when such a function is used, its body is inlined so that the compiler can insert the correct type-specific instructions.
The detailed signature is:
Seq.average : seq<^T> -> ^T (requires ^T with static member (+) and ^T with static member DivideByInt and ^T with static member Zero)
Unlike Seq.append
, Seq.average needs some more constraints on type of elements. Particularly:
_ DivideByInt (s1 + s2 + ... + sn) n where n <> 0
Seq.average {s1; s2;...; sn} = /
\_ ^T.Zero where n = 0
As you can see, both (+)
, DivideByInt
and Zero
are required in order that Seq.average<^T>
makes sense.
Useful information about generics could be found hereMSDN.
Although, as others have pointed out, by convention ^T
is used with inline
and 'T
is not, the two are interchangeable (sometimes?).
So, technically, the answer to your question is "there isn't a difference."
kvb pointed out: there is a difference. But it's not as clear-cut as the other answers indicate. In some cases, the two are interchangeable, e.g.,
let inline add (x:^T) (y:^T) = x + y
let inline add (x:'T) (y:'T) = x + y
or
let f (x:^T) = !x
let f (x:'T) = !x
The convention is clear, while the implementation is not.
let f (x:^t) = x
and let f (x:'t) = x
. –
Sickener ^
. This works the same either way: let add (x:^T) (y:^T) = x + y
. –
Vidovic ^
requires that some static constraint can be inferred...which wouldn't be the case for f (x:^t) = x
. –
Vidovic add<'t>
vs. add< ^t>
). –
Sickener let inline add (x:'T) (y:'T) = x + y
–
Vidovic 'T
is getting unified with a fresh statically resolved type variable (which the compiler conveniently names ^T
) arising from the use of (+)
. –
Sickener add
- it uses ^T
, not 'T
. Type inference can be a bit wacky when combined with constraints. –
Sickener © 2022 - 2024 — McMap. All rights reserved.
'
are resolved at runtime (generics);^
are resolved at compile-time. See Statically Resolved Type Parameters. – Crossfade