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 "base
cannot 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?
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
}