7

F# 3.0 adds stricter checks for calls to base and protected members. I have something like the following abstract class in C# that has protected static helper methods to be used by derived classes.

public abstract class Processor {
    public abstract void Process();
    protected static void Helper(object arg) { }
}

In F#, one of those helper methods is passed as a first-class function:

type DerivedProcessor() =
  inherit Processor()

  let init f =
    f ()

  override x.Process() =
    init Processor.Helper

It compiles without complaint in 2.0, but in 3.0 produces an error:

A protected member is called or 'base' is being used. This is only allowed in the direct implementation of members since they could escape their object scope.

OK, it's easy enough to comply, just wrap the call in another static member

static member private HelperWrapper(arg) = Processor.Helper(arg)

and pass that intead. But why?

C# has no problem with this same pattern.

public class HappyToCompile : Processor {
    private void Init(Action<object> f) {
        f(null);
    }

    public override void Process() {
        Init(Helper);
    }
}

The questions:

  1. Why were the stricter checks added?
  2. (and related) What terrible problem does such a trivial workaround address?
  3. Is there a better design that this is supposed to encourage?
Daniel
  • 47,404
  • 11
  • 101
  • 179

1 Answers1

5

Using my psychic language design skills, I'd guess that F# 2.0 is generating unverifiable code. See this post on Eric Lippert's blog for an explanation of a related issue in C# (which has been fixed since C# 4, IIRC).

In short, when you create an F# function you are really creating a new class deriving from FSharpFunc<_,_>, and calling your protected method from within that class isn't valid since it's not in the inheritance chain.

Of course, rather than disallowing these calls, the compiler could just do what you're doing (create a private method and use that), but perhaps the benefits were thought not to outweigh the cost of implementing that improvement.

kvb
  • 54,864
  • 2
  • 91
  • 133
  • You answered the first question, and Eric's blog answers the second one. It's easy to gloss over the differences between `Func<_>` and `FSharpFunc<_>`. How could this be refactored to eliminate `protected`? – Daniel Aug 24 '12 at 16:20
  • 1
    Well, why is it protected in the first place? If you want it to be accessible only to subclasses then there's not really a lot that you can do. Perhaps you could expose `Helper` as a protected property of type `Action` rather than as a method, if you expect it to be passed around as a delegate anyway. – kvb Aug 24 '12 at 16:34
  • 3
    This would be a good addition to [_Breaking Changes_](http://msdn.microsoft.com/en-us/library/hh416791). Another [recent question](http://stackoverflow.com/q/11978234/162396) might be a good candidate too. – Daniel Aug 24 '12 at 16:48
  • _"Why is it protected in the first place?"_ I'm not telling if you don't know! :) Of course, because it should only be available to subtypes. Your delegate suggestion is good, and I would use it if it didn't break existing code. Looks like I'm stuck with the local static member. – Daniel Aug 24 '12 at 16:51
  • 1
    I noticed that Tomas [answered](http://stackoverflow.com/a/2390591/82959) a related question of yours a while ago and some of the same ground was covered. – kvb Aug 24 '12 at 17:29
  • I found that question after asking this one. Sorry for the overlap (that was over two years ago!). This question is motivated by the recent _change in behavior_ in 3.0, so I think it's different enough to be warranted. If nothing else, it can serve as a starting point for others who stumble upon this change. – Daniel Aug 24 '12 at 18:33
  • Oh, absolutely. I didn't mean to imply that that question obviated the need for this one - just that there was some related info there. – kvb Aug 24 '12 at 19:27