From an answer by kvb about how to call implicit conversion operators:
let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)
I've known F# for a while but I just don't know how to parse the implementation here. What is (^a or ^b)? And the stuff after that? Please go over what each part represents grammatically.
^a or ^b means literally "^a or ^b".
The colon : means "has" or "contained in", depending on how you look at it.
So the whole expression (^a or ^b) : (static member op_Implicit : ^a -> ^b) means "static member named "op_Implicit" that has type ^a -> ^b and is defined on either type ^a or type ^b". This whole expression ultimately evaluates to a function of type ^a -> ^b.
Then, the x placed to the right of that expression means "function application", just like in the usual F# syntax.
So the whole thing taken together would mean "on type ^a or type ^b, find a static member named "op_Implicit" that has type ^a -> ^b, and apply that member to argument x".
For a bit more discussion of statically resolved constraints, see this answer or this MSDN article.
Related
The following F# code
let f<'T when 'T: (member Id:int)> (t:'T) = t.Id
is not accepted with the following error:
Error FS0670 This code is not sufficiently generic. The type variable
^T when ^T : (member get_Id : ^T -> int) could not be generalized
because it would escape its scope.
What's wrong?
How to fix it?
EDIT
#Fyodor: tricky! I did some tests and found more strangeness:
let inline f1<^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )
let inline f2<'T when 'T: (member Id:int)> (t:'T) = ( 'T: (member Id:int) t )
let inline f3<'T when 'T: (member Id:int)> (t:'T) = ( ^T: (member Id:int) t )
let inline f4 t = ( ^T: (member Id:int) t )
f1 gives error in <^T
Error FS0010 Unexpected infix operator in pattern
f2 gives errors in ( 'T
Error FS0583 Unmatched '('
Error FS0010 Unexpected quote symbol in binding
f3 and f4 are accepted
You have three mistakes:
First, the function needs to be inline. .NET CLR doesn't currently support member constraints (i.e. "this can be any type as long as it has this member"), which means that such function cannot be compiled to IL, so the F# compiler has to fake it and substitute these functions at compile time. To signal the compiler that you know this and agree, you have to add the inline keyword right after let. Inline functions will be completely erased at compile time and will not show up as CLR methods in compiled code.
Second, the generic parameter name needs to be prefixed with ^ instead of '. This is actually optional, since the compiler seems to be automatically replacing ' with ^ (as evident from your error message), but just for consistency. The generic parameters prefixed with ^ are called "statically resolved type parameters", which refers to the fact that they get resolved (and erased) at compile time, as described above.
Third, the syntax for referencing such members in the function body is not actually the same as the syntax for referencing regular members. You can't use the dot notation. Instead you have to use this weird syntax that mirrors the parameter declaration.
Applying all three fixes, this would be your new code:
let inline f<^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )
Note that, since the member constraint is now in the function body, it does not have to be repeated on the left of =, so you can write just this:
let inline f t = ( ^T: (member Id:int) t )
Here's a bit more info on this.
To add a brief comment regarding the edit that you added - the problem with your definition of f1 is simply that the parser requires a space between the angle brackets and the hat in: <^:
let inline f1< ^T when ^T: (member Id:int)> (t:^T) = ( ^T: (member Id:int) t )
Otherwise, the syntax <^ gets parsed as an operator, rather than generic argument list, which is what you need here. All other information is in Fyodor's answer!
What does "^" mean when it's in front of a type?
Example:
int : ^T -> int
string : ^T -> string
this indicates an Statically Resolved Type Parameter
from MSDN:
A statically resolved type parameter is a type parameter that is
replaced with an actual type at compile time instead of at run time.
They are preceded by a caret (^) symbol.
so it's very similar to 'T but you can use it to give member constraints and the compiler will resolve them at compile-time (obviously) - usually you are just using inline and the type-inference will work it out for you - but there are some quite advanced tricks (for example FsControl) out there using this (not often used) feature
example
let inline add a b = a + b
val inline add :
a: ^a -> b: ^b -> ^c
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
will add such a constraint to indicate that this will work with all numeric types (it will add an member constraint to an static operator (+))
very simple examples:
let myfun x = x
Here in the intellisense it says "x: 'a -> 'a". In the FSI it says "x: 'a -> 'a"
let inline myfun x = x
Here in the intellisense it says "x: 'a -> 'a". In the FSI it says "x: 'a -> 'a" <<<< why not ^a?
let inline myfun (x: 'b) = x
Here in the intellisense it says "x: 'b -> 'b". In the FSI it says "x: 'b -> 'b"
let inline myfun (x: ^b) = x
Here in the intellisense it says "x: 'b -> 'b". In the FSI it says "x: ^b -> ^b" <<<< different
Since the intellisense never shows ^b, should I look for ^b as an indicator of "statically resolved" in FSI?
Does inline guarantee "statically resolved"?
Inline does allow but does not force statically resolved types, that's why in case 2. it remains the same as in case 1.
I think in most cases type inference is smart enough to guess if the type should really be statically resolved, even if you don't specify the ^.
For example if you change your function body to sqrt x in case 3. you'll get
> let inline myfun (x: 'b) = sqrt x;;
val inline myfun : ^b -> ^a when ^b : (static member Sqrt : ^b -> ^a)
I personally always try not to specify types explicitly at first try, then I check if I'm happy with the inference, if I'm not then I try adding inline, but not the hat types.
Why intellisense shows sometimes something different? that's probably a small bug.
There are two snippets of F# I would like to understand, but don't know what to google. First:
let ``1+2`` () = ....
I am guessing this just means "turn the expression into an identifier"? But what is that feature called if I want to refer to it?
Second, what does the character ^ mean when it occurs in a type? I have found several mentions of it, but the explanation always just says "the type is this" rather than "it differs from a type without a 1^1 in that ...". For example:
let inline blah x y = x+y;;
val inline blah :
^a -> ^b -> ^c
when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
Many thanks in advance.
I'd probably call that a "quoted identifier" http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec.html#_Toc270597387
"Statically resolved type parameter" http://msdn.microsoft.com/en-us/library/dd548046%28VS.100%29.aspx
The backquote syntax is indeed just a way to 'quote' arbitrary characters into identifiers, I am not sure if it has a name. It is typically used for e.g.
let ``This Identifier Contains Spaces`` = 42
or
foo.``member``(42) // 'member' is an F# keyword, but maybe it was the name of some
// method from C# code you're using, so here's a way to call it
The carat indicates a statically resolved type parameter:
http://msdn.microsoft.com/en-us/library/dd548046.aspx
used for ad-hoc overloading/genericity.
I am trying to explore the type of operators such as :: in F# interactive.
But I get these kinds of messages:
Unexpected symbol '::' in expression. Expected ')' or other token.
even if I surround it with (::).
I do it like this:
> let inline showmecons a b = a :: b;;
val inline showmecons : 'a -> 'a list -> 'a list
or
> let inline showmepow a b = a ** b;;
val inline showmepow :
^a -> ^b -> ^a when ^a : (static member Pow : ^a * ^b -> ^a)
You'll see the type of usual operators if you surround them with parentheses:
> (+);;
val it : (int -> int -> int) = <fun:it#4-5>
Unfortunatelly, this restricts the type of the operator to one specific type - F# Interactive doesn't print the polymorphic definition (with constraints). You can use the workaround suggested by Stephen (and define a new inline function) to see that.
The reason why it doesn't work for :: is that :: is actually a special syntactic construct (defined directly in the F# specification).
This is pretty old, but I'm learning F# and also wanted to figure this out.
Looking at the F# specification on page 32, we see that symbolic keywords also have a compiled name in F#. The equivalent compiled name for :: is op_ColonColon, which actually accepts tuples:
> op_ColonColon;;
val it : arg0:'a * arg1:'a list -> 'a list = <fun:clo#22-5>`
Using :: to define an inline function will give us a curried cons function, which is misleading, I believe.