13

I'm trying to get a grip on F#, and in the process I am converting some C# code. I'm having some trouble with defining properties in an interface and implementing them in a type.

Consider the following code:

module File1

type IMyInterface =
    abstract member MyProp : bool with get, set
    abstract member MyMethod : unit -> unit

type MyType() = 
    interface IMyInterface with
        member val MyProp = true with get, set
        member self.MyMethod() = if MyProp then () else ()

The documentation for F# properties appears to state that my implementation of MyProp in MyType is correct, however, the compiler complains that "The value or constructor 'MyProp' is not defined". Any ideas?

Eyvind
  • 5,221
  • 5
  • 40
  • 59

3 Answers3

20

To access the property within an (explicit) interface you need to cast to the self reference to the interface type:

type MyType() = 
    interface IMyInterface with
        member val MyProp = true with get, set
        member self.MyMethod() = if (self :> IMyInterface).MyProp then () else ()

You get the same error in C# if you implement the interface explicitly, and a cast is also required to access the member:

interface IMyInterface
{
    bool MyProp { get; set; }
    void MyMethod();
}

class MyType : IMyInterface
{
    bool IMyInterface.MyProp { get; set; }

    void IMyInterface.MyMethod()
    {
        if (((IMyInterface)this).MyProp) { }
    }
}
Phillip Trelford
  • 6,513
  • 25
  • 40
  • Aha! I was not aware that this was actually an explicit interface implementation. Is there a way to implement it implicitly? – Eyvind Aug 05 '15 at 08:15
  • 3
    As far as I know in F# all interfaces must be explicitly implemented. Scott explains it nicely here http://fsharpforfunandprofit.com/posts/interfaces/ – Kevin Aug 05 '15 at 08:17
  • 1
    No problem, and yes in F# interfaces can only be implemented explicitly – Phillip Trelford Aug 05 '15 at 08:23
13

If you are only accessing the interface, then you don't need to define members in the type itself. Phil's answer is good if you want minimalism, but another approach I like is to use "let-bound" values rather than members -- for more complex code, type inference is better and they are generally easier to deal with than members.

type MyType() = 
    let mutable myProp = true 
    let myMethod() = if myProp then () else ()

    interface IMyInterface with
        member __.MyProp with get() = myProp and set v = myProp <- v
        member __.MyMethod() = myMethod()

The code is slightly cleaner than member versions as well, imo, because the self tag in type MyType() as self is only needed to access members -- let-bound values can be accessed directly from the interface.

Grundoon
  • 2,734
  • 1
  • 18
  • 21
  • 1
    The way I like to think of it is: (a) I do all the real work with let bound values and functions (equivalent to private methods, say) and then (b) I expose some of that functionality through one or more interfaces. The interfaces are then just proxies to the "real" code. – Grundoon Aug 05 '15 at 08:46
5

here is a working version:

type IMyInterface =
    abstract member MyProp : bool with get, set
    abstract member MyMethod : unit -> unit

type MyType() = 
    member val MyProp = true with get, set
    member self.MyMethod() = if self.MyProp then () else ()
    interface IMyInterface with
        member self.MyProp with get () = self.MyProp and set v = self.MyProp <- v
        member self.MyMethod() = self.MyMethod()

please note that I included the explicit members as you surely would have missed them ;)

Random Dev
  • 51,810
  • 9
  • 92
  • 119
  • I know that this works, but I want auto properties, not explicit backing fields... – Eyvind Aug 05 '15 at 07:46
  • 1
    `MyProp` is a auto property - it's just the interface implementation that overrides this - maybe there is a shorter way but honestly I try not to use mutable values anyway so I don't know for sure ;) – Random Dev Aug 05 '15 at 07:47
  • Yeah, I know using mutable properties is not exactly idiomatic F#, but in my real scenario here, the interface I'm implementing is defined in C#, and not something that I can change. Still, your solution looks like more of a mess (no offense intended) than it should be; hopefully someone will chime in with some more succinct code :) – Eyvind Aug 05 '15 at 07:50
  • 2
    @Eyvind In my experience, F# is extremely elegant when you use it functionally, but can get 'messy' when used in an object-oriented way. This motivates you to go for nice functional design instead :) – Mark Seemann Aug 05 '15 at 07:54
  • 1
    it's only *messy* because you don't get the implicit interfaces so you either have to do certain things twice (as here) or you have to do `(myType :> IMyInterface).MyProp` all the time - anyway as Mark commented you will not be happy if you try to 1:1 copy the stuff you know from C# – Random Dev Aug 05 '15 at 08:00
  • @Carsten I am absolutely NOT trying to copy the stuff I know from C# (why would I use F# then?), but I AM trying to make interaction between F# and C# code work, which requires implementing C# interfaces. It is kind of disappointing if I have to go through such hoops in order to do that (after all, isn't smooth interop with C#/other .NET code a design goal for F#?). But alas, if this is the only route, then I'll travel down it :) – Eyvind Aug 05 '15 at 08:11
  • well Philip showed you how you can do without - and btw: smooth interop is IMO not the no1 design goal (but you usually have it if you are careful) - F# is very explicit in it's use of types and this is a good thing – Random Dev Aug 05 '15 at 08:17
  • @Carsten. Thanks; it is an interesting discussion for an F# noob like me. While I agree that being strict about types is good, cluttering your program with stuff in order to appease the compiler is not =) When it comes to explicit vs implicit, I tend to lean towards preferring the latter in most cases (type inference perhaps being the most prominent example) – Eyvind Aug 05 '15 at 08:28
  • 1
    @Eyvind I think there is a difference between type inference and implicit casting. F# will infer types for you "as if" you had annotated them explicitly, but it won't implicitly upcast a class to an interface or base class (with certain exceptions). It also won't implicitly cast an int to a float, or even an int64 to an int32! This is what Carsten is talking about I think. There's quite a lot of stuff that you can do in C# that F# will complain about and force you to be explicit in your intention. And after a while you even grow to *like* all the complaining! – Grundoon Aug 05 '15 at 09:42