The error in the title is vague but googling it gives presently two hits on SO and five altogether (suggesting it is a rare beast, so don't expect too many visits here ;). I expect this question to be sixth in that list :p.

Kvb's answer in this thread suggests that the error text is misleading and is raised if you call base from within a closure, but that doesn't always seem the case, as this works:

// in a closure, but no FS0419
let myObj = fun foo ->
    { new obj() with override this.ToString() = base.ToString() + foo}

But this fails (the simplest way I found to exemplify the issue):

// not in a closure, but raises FS0419
let myObj = 
    { new obj() with override this.ToString() = () |> base.ToString }

I was using object expressions instead of type declarations with inheritance, and I tried to create a new FsUnit constraint by building a custom NUnit constraint. Here's a simplified version that shows the issue I was seeing:

let EndsWithIgnoreWhitespaceContraint expectedResult = 
    { new EndsWithConstraint(expectedResult) with 
        override __.ApplyTo<'T> (actual: 'T) =
            let actual = box actual
            if actual :? string then 
                actual :?> string
                |> fun s -> if isNull s then String.Empty else s
                |> fun s -> s.Trim()

                // error FS0419: 'base' values may only be used to make direct
                // calls to the base implementations of overridden members
                |> base.ApplyTo 
            else
                exn "String expected .. bla bla..." |> raise        }

// make it available to FsUnit's syntax style (overriding existing endWith)
let endWith = EndsWithIgnoreWhitespaceContraint

// using it:
"  hello world  " |> should endWith "world"

Now, it is obviously not necessary to know FsUnit to see this behavior. But it took me a night and a day to realize I was duped, in fact I didn't see it until I was writing this question on SO.

Turns out that this works:

Instead of x |> base.SomeMethod write base.SomeMethod x.

I find this surprising. Not sure it is a bug or a feature. But since the |> operator is inlined (I tested it with a different operator) and it doesn't create a new function (like >> does), I don't see why this error is raised.

In fact, I don't see any semantic difference between f a and a |> f (apart from precedence rules and the like). So why the error? What rule am I breaking?


One final thought, kvb wrote "basecannot be called from a closure... curried members create a closure automatically", this suggests this would be wrong, but it compiles just fine:

let myObj foo bar = 
    { new obj() with override this.ToString() = base.ToString() + foo + bar}

Does anybody know of precisely what causes, or not causes, this error?

1

There are 1 best solutions below

6
On BEST ANSWER

First, you misunderstood the answer that "base cannot be used in a closure". It was meant to refer to a closure that captures the base itself - it is the capturing that prevents this from working, not the closure as such. In your { new obj } example, base is not captured by any closures. The whole object is captured, but base is only directly used within the ToString method.

To illustrate, try this:

let myObj = 
    { new obj() with override this.ToString() = (fun() -> base.ToString())()}

This code won't compile, because base is being captured by the closure fun() -> base.ToString().

Secondly, using an object method as a function does not work "directly" as one might expect, because .NET methods are represented differently from F# functions. Instead, when faced with something like let x = obj.M, the compiler will treat it as let x = fun a -> obj.M(a) - that is, wrap it in a closure.

To illustrate, try this:

let myObj = 
    { new obj() with 
      override this.ToString() = 
        let f = base.ToString  // Error here
        f()
    }

See where this is going? :-)

When you pipe into an object method, the compiler has to create that closure, and then pass it to the pipe operator. To illustrate, try this:

let myObj = 
    { new obj() with 
      override this.ToString() = 
        () |> base.ToString  // Same error
    }