3

Why can't I call the base implementation of f here:

type Base = 
    abstract f : int -> int -> int
    default this.f (x : int) (y : int) : int = x + y

type Derived = 
    inherit Base
    override this.f (x : int) (y : int) : int = base.f -x -y

The call to base.f elicits this compiler error:

error FS0419: 'base' values may only be used to make direct calls to the base implementations of overridden members

If I change f to take a single argument then it compiles. Presumably this is something to do with curried parameters vs tupled parameters, but the above code looks fine to me.

jon hanson
  • 8,722
  • 2
  • 37
  • 61
  • See also: http://cs.hubfs.net/forums/thread/13909.aspx (no conclusion there). To me that looks like a compiler bug. – wmeyer May 01 '11 at 11:53

3 Answers3

5

I believe that the issue is that base can't be captured by a closure - the call has to be made directly. However, overriding a curried function automatically creates a closure since only the first argument is applied immediately. Therefore, even though it looks like you are indeed using the base value to make a direct call to the base implementation of the overridden member, you're actually using the base value within a closure, which is illegal.

Unfortunately, I don't think there's any great way to work around this issue. Generally, you should avoid curried members when possible, but here's one alternative:

type Base = 
    abstract f : int -> (int -> int)
    default this.f (x : int) = fun y -> x + y

type Derived = 
    inherit Base
    override this.f x = 
       let fn = base.f -x
       fun y -> fn -y
kvb
  • 54,864
  • 2
  • 91
  • 133
  • 1
    It will take me some time to digest your answer, but since you're not suggesting it's a compiler bug, then it seems to me that the logical conclusion is that since what I'm attempting is essentially idiomatic, that the language is broken in this respect. – jon hanson May 01 '11 at 22:20
  • 2
    @jon - Arguably it's a compiler bug to have such a misleading error message. Furthermore, there's no fundamental limitation preventing base members from being callable from within closures, though it's not a trivial problem to solve. However, I'd argue that your code is __not__ essentially idiomatic. It's idiomatic to use curried let-bound functions in modules, but it's not idiomatic to use curried members, exactly because of issues like this. – kvb May 01 '11 at 23:40
  • Personally I won't do OO programming in such a beautiful functional language like F#. If you really want do such things as inheritance, base class etc then do it in C# because in F# it will look weired to an OO neuron . F# is functional first and lets use it for that only – Ankur May 02 '11 at 04:06
  • 1
    @Ankur: F# is touted as a multi-paradigm language that purports to support both OO as well as FP styles of programming. – jon hanson May 02 '11 at 09:07
  • 1
    Curried members in general is one of those spots where an impedance mismatch occurs between OOP pragmatics and FP pragmatics. (It crops up a lot when mixing F# and C#.) When mixing OOP and FP, you have to design to *both* paradigms rather than *either*. Irritating, but it does become more automatic with use. – TechNeilogy May 03 '11 at 14:45
0

@kvb is right in his analysis but if you really want to override a curried method you can. The syntax is pretty verbose though:

type Base = 
    abstract f : int -> (int -> int)
    default this.f (x : int) = fun (y : int) -> x + y

type Derived = 
    inherit Base
    override this.f (x : int) =
        let baseCall = base.f -x
        fun (y : int) -> baseCall -y
Nathan Phillips
  • 11,899
  • 1
  • 31
  • 24
0

Your assumption about the curried parameters is correct. The below code compiles and run fine:

type Base () = 
    abstract f : int * int -> int
    default this.f (x : int,y : int) : int = x + y

   type Derived ()  = 
    inherit Base()
    override this.f (x : int,y : int) : int = 
        base.f(-x,-y)

NOTE: I have used tupled parameters. This reason may be because in curried parameters it break downs the function in multiple functions (each function takes 1 parameter)

Ankur
  • 33,367
  • 2
  • 46
  • 72