Is there an equivalent to creating a C# implicit operator in F#?
In C# I can add implicit operators to a class as follows:
public class MyClass
{
private int data;
public static implicit operator MyClass(int i)
{
return new MyClass { data = i };
}
public static implicit operator MyClass(string s)
{
int result;
if (int.TryParse(s, out result))
{
return new MyClass { data = result };
}
else
{
return new MyClass { data = 999 };
}
}
public override string ToString()
{
return data.ToString();
}
}
Then I can pass any function that is expecting a MyClass object a string or an int. eg
public static string Get(MyClass c)
{
return c.ToString();
}开发者_开发问答
static void Main(string[] args)
{
string s1 = Get(21);
string s2 = Get("hello");
string s3 = Get("23");
}
Is there a way of doing this in F#?
As others have pointed out, there is no way to do implicit conversion in F#. However, you could always create your own operator to make it a bit easier to explicitly convert things (and to reuse any op_Implicit definitions that existing classes have defined):
let inline (!>) (x:^a) : ^b = ((^a or ^b) : (static member op_Implicit : ^a -> ^b) x)
Then you can use it like this:
type A() = class end
type B() = static member op_Implicit(a:A) = B()
let myfn (b : B) = "result"
(* apply the implicit conversion to an A using our operator, then call the function *)
myfn (!> A())
Implicit conversion is rather problematic with respect to type safety and type inference, so the answer is: No, it actually would be a problematic feature.
No, there is not.
As of F#6, there is now support for op_Implicit as long as the resolution is type-directed and clear e.g.
let x : System.Nullable<int> = 10 // valid in F#6
let doStuff (x:System.Nullable<int>) = ()
doStuff 10 // valid in F#6
type Foo() =
static member X (x:System.Nullable<int>) = ()
Foo.X 10 // valid in F#6
Note that the first two example will give a warning (FS3391) which can be turned off.
On a related note, it is possible to add implicit or explicit static members so C# can use them.
type Country =
| NotSpecified
| England
| Wales
| Scotland
| NorthernIreland
with static member op_Implicit(c:Country) =
match c with | NotSpecified -> 0
| England -> 1
| Wales -> 2
| Scotland -> 3
| NorthernIreland -> 4
This allows a c# user to use (int) Wales
for example
You can call the operator like this:
let casted = TargetClass.op_Implicit sourceObject
Thanks to @isaac-abraham and @kvb. I condensed their answers and built my typesafe ids, using implicit constructors that the op asked for:
type UserId =
{ id: string } // underlying, could be int or so as well
static member op_Implicit(id:string) = { id = id }
override r.ToString() = r.id
type ProductId =
{ id: string }
static member op_Implicit(id:string) = { id = id }
override r.ToString() = r.id
let id1 : UserId = "p1"
let id2 : UserId = "p1"
let id3 : UserId = "p2"
id1 = id2 // true
id1 = id3 // false
let pid1 : ProductId = "p1"
id1 = pid1 // type error, not comparable -> excellent, thats what this is for
Using records gives structural equality and even comparison works:
id3 > id1 // true
id3 < id1 // false
精彩评论