What is the difference between the generic signifier ' and the symbol ^ In F# method signatures
Asked Answered
C

3

5

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
Capuchin answered 28/6, 2012 at 17:59 Comment(3)
' are resolved at runtime (generics); ^ are resolved at compile-time. See Statically Resolved Type Parameters.Crossfade
ildjarn, why wouldn't you add this as an answer and not as a comment. It seems correct to me.Capuchin
Because simply linking to documentation without adding details makes a poor answer, and I don't feel like adding details at the moment. ;-]Crossfade
D
5

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.

Downhill answered 28/6, 2012 at 18:16 Comment(1)
Thanks for the clarification. I removed it to shorten the example and shouldn't have.Capuchin
S
6

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 chars 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.

Sickener answered 28/6, 2012 at 18:14 Comment(1)
Might be good to give a quick note on apostrophe. Don't need a ton of detail, but it is useful for people who don't know much about generics.Midweek
D
5

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.

Downhill answered 28/6, 2012 at 18:16 Comment(1)
Thanks for the clarification. I removed it to shorten the example and shouldn't have.Capuchin
V
0

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.

Vidovic answered 28/6, 2012 at 18:25 Comment(11)
I don't think that's right - see what happens if you try to execute let f (x:^t) = x and let f (x:'t) = x.Sickener
I'm confused about the effect of ^. This works the same either way: let add (x:^T) (y:^T) = x + y.Vidovic
Seems like the difference is ^ requires that some static constraint can be inferred...which wouldn't be the case for f (x:^t) = x.Vidovic
in those examples, the definition isn't generalized, so the type variable goes away in either case. See what happens when you make the functions explicitly generic (e.g. add<'t> vs. add< ^t>).Sickener
I see, but then why is this allowed? let inline add (x:'T) (y:'T) = x + yVidovic
I still am not getting it. Bot of these generate the exact same IL: let add1 (x:'t) (y:'t) = x + y let add2 (x:^t) (y:^t) = x + yCapuchin
@JoshuaBelden: My answer is wrong, but I'm not clear on the precise difference. In your example, kvb pointed out that neither function is generalized so the type args are essentially ignored.Vidovic
@Vidovic - regarding your last question to me - I think that the 'T is getting unified with a fresh statically resolved type variable (which the compiler conveniently names ^T) arising from the use of (+).Sickener
@kvb: (cue Twilight Zone theme) That would make the distinction esoteric and amorphous, in my view.Vidovic
@Vidovic - look at the resulting definition of add - it uses ^T, not 'T. Type inference can be a bit wacky when combined with constraints.Sickener
@kvb: The question is about a practical difference in syntax, behind-the-scenes translation by the compiler notwithstanding.Vidovic

© 2022 - 2024 — McMap. All rights reserved.