29

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#?

wethercotes
  • 646
  • 1
  • 7
  • 10

7 Answers7

30

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())
svick
  • 236,525
  • 50
  • 385
  • 514
kvb
  • 54,864
  • 2
  • 91
  • 133
  • This appears to be an invalid prefix operator name in F# 2.0. Are the rules for operator names defined somewhere? I don't see anything on the [MSDN](http://msdn.microsoft.com/en-us/library/dd233204.aspx) page that indicates this restriction. – Daniel Feb 17 '11 at 20:08
  • Omitting `~` from the name appears to work. Did the rules change? – Daniel Feb 17 '11 at 20:18
  • @Daniel - yes, I think the rules must have changed. Omitting the `~` won't quite work because it will make it an infix rather than prefix operator. However, replacing `~` with `!` should work. – kvb Feb 17 '11 at 21:58
  • Can anybody explain what this first line `let implicit (!>) ...` does? – robkuz Dec 17 '14 at 21:23
  • 1
    @robkuz it uses [Statically resolved type parameters](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/statically-resolved-type-parameters) to essentially say: The !> operator can be used between two types a and b where there exists a op_Implicit defined on a or b that can do the conversion, and to use it as the definition of the function – Dave Glassborow Nov 29 '18 at 10:49
  • great + helpful + outdated. – citykid Jun 10 '22 at 15:43
7

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.

Francesco
  • 3,200
  • 1
  • 34
  • 46
3

No, there is not.

Brian
  • 117,631
  • 17
  • 236
  • 300
3

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

Dave Glassborow
  • 3,253
  • 1
  • 30
  • 25
3

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.

Isaac Abraham
  • 3,422
  • 2
  • 23
  • 26
1

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
citykid
  • 9,916
  • 10
  • 55
  • 91
0

You can call the operator like this:

let casted = TargetClass.op_Implicit sourceObject
Mikael Dúi Bolinder
  • 2,080
  • 2
  • 19
  • 44