GrumpyRodriguez
GrumpyRodriguez

Reputation: 769

F# can't implement interface with object expression when method parameter is unit

I cannot find a way of implementing an interface with an object expression when it contains a method with no types that can refer to the type for which the implementation is being put into place. A method with signature unit -> string is a simple example. Example code below:

type AsString =
    abstract member asString: unit -> string
    
type AsString2<'T> =
    abstract member asString: 'T -> string

type SomeDu =
    | A of int
    | B of string
    | C of float
    interface AsString
        with member self.asString () =
                match self with
                | A a -> string a
                | B b -> b
                | C c -> string c
    interface AsString2<SomeDu>
        with member self.asString x =
                if not (x = self) then failwith "argument must be the member container"
                match self with
                | A a -> string a
                | B b -> b
                | C c -> string c

//this expression was expected to have type AsString but here has type SomeDU            
let implSomeDU = 
    {new AsString with
        member self.asString () =
            match self with
            | A a -> string a
            | B b -> b
            | C c -> string c}

If I attempt to refer to the implementing type as I've done with AsString2 then no problem with object expressions, but the implementation on the DU now has a requirement that I cannot express in the type system: the argument has to be the DU instance or the method has no value.

Is there any trick that'd let me implement AsString as an object expression for SomeDu?

Upvotes: 0

Views: 293

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243041

When implementing an interface using an object expression, the type of this is always going to be the interface and nothing else - unlike when implementing an interface in some other type (where this refers to the current value), in case of object expressions, the "current value" is just the interface implementation.

If you want to implement an interface in an existing type, do that by implementing it in the type itself.

If you want to get an implementation of an interface based on some other value, you can write a function that takes the value and implements the interface using object expression.

In your case, your function in the second case would look like this:

type AsString =
  abstract member asString: unit -> string

type SomeDu =
  | A of int
  | B of string
  | C of float

let implSomeDU someDu = 
  { new AsString with
      member self.asString () =
          match someDu with
          | A a -> string a
          | B b -> b
          | C c -> string c }

Upvotes: 3

Related Questions