9

Is there any way to access let bound fields from a static member? The following gives the indicated error:

type Foo(x) =
    let x = x
    static member test() =
        let foo = Foo(System.DateTime.Now.Month)
        printfn "%A" foo.x //the field, constructor or member 'x' is not defined
        ()

Whereas private explicit fields do allow access from static members:

type Bar =
    val private x:int
    new(x) = { x=x }
    static member test() =
        let Bar = Bar(System.DateTime.Now.Month)
        printfn "%A" Bar.x
        ()

The documentation http://msdn.microsoft.com/en-us/library/dd469494.aspx states that "Explicit fields are not intended for routine use," yet accessing private instance fields from static members is certainly a routine scenario. Moreover, I don't believe you can set explicit fields within a primary constructor, which means if even one private instance field needs to be accessed from a static member, all of your fields must be moved over to explicit fields and you can no longer use a primary constructor -- it's all or nothing.

As real world example where you would actually want to access a private instance field from a static member, consider a big integer implementation: a BigInteger class would be immutable, so the internal representation of the big integer would kept as a private instance field (let's call it data). Now, suppose you felt an Add(other) instance method was inappropriate for an immutable data structure and you only wanted to implement a static Add(lhs,rhs) method: in this case, you would need to be able to access lhs.data and rhs.data.

Stephen Swensen
  • 22,107
  • 9
  • 81
  • 136

2 Answers2

7

I don't think you can do that... in fact, you can't access let-bound values from other instances either:

type Foo() =
  let x = 3
  member this.Test(f:Foo) =
    f.x // same error

In general, if you need to access such a value from outside of the instance it belongs to, you should probably either create a private property to get the value or use a private field instead.

UPDATE This is covered by section 8.6.2 of the spec. In particular:

Instance “let” bindings are lexically scoped (and thus implicitly private) to the object being defined.

Perhaps someone from the F# team will weigh in with a definitive answer as to why the language behaves this way. However, I can think of a couple of potential reasons:

  1. let-bound values may not even be present as fields (e.g. again from the spec, a let binding will be represented by a local to the constructor "if the value is not a syntactic function, is not mutable and is not used in any function or member")
  2. This seems consistent with the behavior of let bindings elsewhere in the language. See the examples of a roughly equivalent class and record definitions which I've included further down (because I can't seem to properly format code blocks within an ordered list...)
  3. This provides a finer-grained level of encapsulation than is possible in many other languages - bindings which are local to the object being defined. Often, other instances will not need access to these bindings, in which case it's nice not to expose them.
  4. If you want something which is accessible by other instances of your class (or from within static methods), there's an easy way to do that - create a private field or property, which has the benefit of explicitly expressing your intention that the value be accessible from outside of the instance that you are in.

As mentioned earlier, here are a roughly equivalent class definition and method to create a record:

type MyClass(i:int) =
  let j = i * i
  member this.IsSameAs(other:MyClass) = 
    false // can't access other.j here

type myRecord = { isSameAs : myRecord -> bool }
let makeMyRecord(i:int) =
  let j = i * i
  { isSameAs = (fun r -> false) } //obviously, no way to access r.j here

Since constructors in F# are conceptually similar to any other function which returns an instance of a type (e.g. they can be called without using new), calling MyClass 5 is conceptually similar to calling makeMyRecord 5. In the latter case, we clearly don't expect that there is any way to access the local let binding for j from another instance of the record. Therefore, it's consistent that in the former case we also don't have any access to the binding.

kvb
  • 54,864
  • 2
  • 91
  • 133
  • Is there any technical or logical reason why let bound fields should behave any differently in this regard to explicit private fields? – Stephen Swensen Jun 23 '10 at 04:50
  • 1
    @Stephen - see my update for speculation on why this approach may have been taken. – kvb Jun 23 '10 at 05:27
  • thanks. does the fact that they are lexically scoped limit the compiler's ability to discover them as fields in these scenarios (does "implicitly private" = hidden)? – Stephen Swensen Jun 23 '10 at 12:50
  • The key part in my mind is that even though the "let" syntax is being used, semantically I would expect field access (when they are compiled as fields) to be consistent across the board (excuse me if I'm abusing terms). Also, as to your second speculation, don't modules give you path qualified access to let bound values (e.g. List.fold)? – Stephen Swensen Jun 23 '10 at 13:02
  • 1
    @Stephen - as to your first question, note that the spec says that they are implicitly private to the _object_ being defined, so that's why they are hidden when accessing other objects of the same class. As to your second question, you are of course correct; I'll revise my answer to reflect that. – kvb Jun 23 '10 at 13:22
0

yet, accessing let bound fields from static members is certainly a routine scenario

What do you mean here? What is the corresponding C# scenario (with an example)?

Note that this is legal:

type Foo() =
    let x = 4
    member this.Blah = x + 1
    member private this.X = x
    static member Test(foo:Foo) =
        foo.X

That is, you can expose the let-bound value as a private member, which a static can read/use.

Brian
  • 117,631
  • 17
  • 236
  • 300
  • Well, what I really mean is that accessing the private instance fields/members/etc. of a class from the same class' static members is a common scenario allowed by most oo programming languages... even F# let's you do this when you use explicit "val" private instance fields. My remark is that I would expect primary constructor / let binding syntax to be just that: when let bound fields are compiled as fields, I would like them to be accessible by the same rules as ordinary private fields. – Stephen Swensen Jun 23 '10 at 17:01
  • Thanks, I was thinking of taking that approach but the main problem I have with it is that I believe X is property instead of a field (and I'd really rather use a field). – Stephen Swensen Jun 23 '10 at 17:18
  • @Stephen - any particular reason you don't want to use a private property? The CLR is likely to inline any trivial properties at runtime, so performance should generally not be an issue... – kvb Jun 23 '10 at 17:32
  • @kvb - That's the particular reason! Great piece of info to put my mind at ease. The other problem I have with this approach though is that then I'd have both x and X in scope some of the time. Also, it's hacky: I may have five let bound fields but only need to access one of them from a static member, in that case the sole purpose of creating a property wrapper for that single field is to get around the limitation (and if I need to access others in the future, I have to go back and add property wrappers). – Stephen Swensen Jun 23 '10 at 17:54
  • 1
    What you see as a 'limitation' is arguably 'improved encapsulation' when viewed from the flip side (what if I wanted to author a static member that intended to only consume the public API? you cannot have the compiler verify you're doing it right in C#). – Brian Jun 23 '10 at 17:59
  • @Brian - I suppose, but this 'improved encapsulation' is not really a feature anybody is asking for and in practice I believe is a hindrance. Also, if you were really going to implement this by design, wouldn't you want to create a new "objectprivate" access modifier which could be enjoyed by all instance fields and members? Moreover, F# generally uses public as the default access modifier so the uber restrictive "objectprivate" as default is odd (especially when the documentation states that primary constructors / let bindings are preferred). – Stephen Swensen Jun 23 '10 at 18:42
  • There's no need for 'objectprivate', since lexical scoping already accomplishes that. (Much of the notion of 'public'/'protected'/'private' is OO compensating for the weakness of not having (from the outset) closures and lexical scoping as a general encapsulation mechanism. Of course, it cuts both ways, see e.g. http://stackoverflow.com/questions/256625/when-to-use-closure/256651#256651 ) An advantage of the F# way is seen in http://blogs.msdn.com/b/lukeh/archive/2010/06/13/f-scaling-from-explorative-to-net-component-f-talk-teched-2010.aspx 'tab as extract method' which leverages 'let' thusly. – Brian Jun 23 '10 at 19:16
  • (F# tends to be "FP, with OO" whereas e.g. I think Scala is like "OO, with FP", and so maybe if you design from the other end you wind up in a place where your suggestions are more harmonious. I haven't thought deeply about it.) – Brian Jun 23 '10 at 19:18
  • @Brian - I appreciate the links and am totally on board with closures and lexical scoping as a superior encapsulation mechanism. I think this is a unique case though, we are designing a .NET class where F# otherwise provides full OO-style encapsulation, lexical scoping in this instance seems like an anomaly. I guess part of the problem is that primary constructors are using let bindings to do various things: create locals and instance fields, so it gets hairy. – Stephen Swensen Jun 23 '10 at 19:43
  • To be pedantic, F# doesn't "otherwise provide full OO-style encapsulation". There is no `protected` in F#, for instance. (But `protected` is bunk, anyway.) Anyway, again, from the other side of the coin, _not_ using lexical scoping as the primary way to encapsulate state would be an anomaly; and the whole concept of a 'field' is at best a corner-case interop thing, a rare and peculiar OO-ism. At the end of the day, there are two valid, but incompatible, perspectives. – Brian Jun 23 '10 at 21:41