5

I have a Java class and I need to write a similar one in F#. How can I define a private class inside public in f#?

public class KnownRuleGoals {
    void record() {
      knownParsingSet.add(new KnownParsing());
    }

    private class KnownParsing {
        Rule [] knownParsing;
        KnownParsing() {
         ...
        }
        void doWork() { ... }
    }
}
  • 1
    [F# does not support class nesting](http://stackoverflow.com/questions/8948332/why-doesnt-f-support-nested-classes). – vcsjones Jan 25 '16 at 21:56
  • 2
    In functional programming one thinks functions first, in OO one thinks objects first. You are thinking object when you should be thinking function. Think how do I create a public function record() that has a hidden function KnownParsing(). A function inside of another function is essentially private. – Guy Coder Jan 25 '16 at 22:25
  • 3
    Why all the down-votes? This sounds like a reasonable question. – Mathias Jan 26 '16 at 00:26
  • I don't think a question like this should be downvoted either. Have an upvote! – Grundoon Jan 26 '16 at 00:33
  • I haven't voted down (and don't plan to) but I do agree with @GuyCoder that this is not an idiomatic way of writing F#. What I would like to see is further clarification on intended use from the questioner so that we can suggest an alternative solution which is idiomatic. – TheInnerLight Jan 26 '16 at 00:35
  • I did ask the OP several questions. He asked the question then left. Not very good manners. If one Googles for an answer, the results are very relevant and clear. – Guy Coder Jan 26 '16 at 00:41
  • @TheInnerLight F# is not pure functional, it is `functional first`. `Functional` has a lot of performance implications and is not a silver bullet. And objects are often a good abstraction, especially when they live for the lifetime of a program. Fundamentalism (often religious) is bad in any domain, be it FP or OOP. – V.B. Jan 26 '16 at 00:43
  • @V.B. I didn't say it was. I said this approach wasn't idiomatic. There are plenty of sensible uses for OOP in F# but I suspect that this isn't one of them. In my experience performance is a too often invoked excuse for writing sloppy and inadequate code. Ironically, sloppy and inadequate code usually performs poorly. Not saying that's true of you, I really have no idea. That said, functional code offers additional advantages, such as being relatively straightforward to test so should be preferred when possible - hence functional first. – TheInnerLight Jan 26 '16 at 01:38
  • @TheInnerLight for F#, both FP and OOP are idiomatic. – V.B. Jan 26 '16 at 01:45

2 Answers2

7

It is perfectly possible in F#. F# is functional first, not pure functional - and this is one of its major strengths. F# is pragmatic and this construct below lets one achieve exactly the same result, even though the KnownParsing type is not "nested" in Java or C# sense.

type KnownRuleGoals() =
    let knownParsingSet : List<KnownParsing> = List()
    member this.Record() =
      knownParsingSet.Add(new KnownParsing())

and private KnownParsing() =
    let foo = "private fields"
    member this.DoWork() = failwith "TODO implementation"

Object expressions are useful for implementing interfaces or abstract classes, but the "parent" class (the one that creates an object expression) won't be able to access its internals. Or, more correctly, there is no way to create internals other than interface/abstract members in any way other than via ref cells outside the object expression itself. This has performance implications such as GC pressure for ref cells.

See this example in my open-source project about how object expressions and "nested via and" types work interchangeably for IEnumerator implementation. On this line I use private type as in this answer and it works well. In the whole project I use F# in mostly imperative way because this is the only way to get decent performance. And I must say I like F# most often over C# even for imperative code.

Community
  • 1
  • 1
V.B.
  • 6,236
  • 1
  • 33
  • 56
5

As people have said, F# does not currently support nested classes.

One workaround is as shown by V.B., with two separate classes, one of which is private.

However, if the inner class is simple (with one or two methods only) then there is an alternative approach, which is to use a closure as a poor-person's object.

In the code below, the KnownParsing function creates two functions and returns them as a pair. The knownParsingSet then contains pairs of functions, rather than instances of classes.

If there is only one method in the nested class, then this approach is fine because a one method class is basically just a function. For more than one method in the nested class, it gets quite ugly, which is why F# people solve the problem using other techniques instead. :)

type KnownRuleGoals() = 

    let knownParsingSet = System.Collections.Generic.HashSet()

    // use a function
    let KnownParsing() =
        let knownParsing = [||]
        let doWork() =
            ()  // do something
        let doWork2() =
            ()  // do something
        // return the pair of functions
        doWork,doWork2

    // the member/method comes after the closure because
    // definitions must come before usage.
    member this.record() = 
        knownParsingSet.Add(KnownParsing())
Grundoon
  • 2,734
  • 1
  • 18
  • 21
  • Additional allocations just to be pure - tuple + 2 Funcs at least, with no hope for inlining by Jit. Let me just understand why so many F# users are so against much more simple solutions in favor of purism? That question haunts me for 2+ years. – V.B. Jan 26 '16 at 00:57
  • The question was not about performance, just on how to implement nested classes. I certainly wouldn't claim that this is a optimal solution! I was just showing an alternative solution for the OP. – Grundoon Jan 26 '16 at 01:00
  • @V.B. For me it is that functional code is more likely to be correct when it is purely functions. If other pure functional languages like [ML](https://en.wikipedia.org/wiki/ML_(programming_language)) became more main stream and had better tools and support I would be using them instead. If [Coq](https://coq.inria.fr/) could be used as a programming language I would use that. If [Mercury](https://mercurylang.org/) were more mainstream and had better tools I would use that. I also rely on [Algebraic Data Types](https://en.wikipedia.org/wiki/Algebraic_data_type) and so despise if statements – Guy Coder Jan 26 '16 at 01:01
  • @GuyCoder that is at least an answer with a reasoning behind, thank you! My experience with F# (in my domain) has taught me that much stricter type inference (e.g. to C#, it has some) and lack of implicit conversions is enough in most cases. But there is another approach I try to adhere - use mutable state when it matters for performance and keep it private. Best of the two worlds. – V.B. Jan 26 '16 at 01:06
  • @V.B. I also forgot to mention [F*](https://www.fstar-lang.org/) which is F# with an added proof system. If this goes mainstream with Visual Studio, I will be seeing F# only in the back window. – Guy Coder Jan 26 '16 at 01:10
  • @GuyCoder we are offtop now for past several comments. But I envy you that performance is not one of your main requirements :) – V.B. Jan 26 '16 at 01:12
  • The question of performance of OO code vs. idiomatic FP code is a deep subject! On the side of FP, I would point out that F# functions can be inlined, and that immutability can arguably be more GC-friendly (more gen0, but less gen1). Also, if I was really concerned about CPU performance I would probably use arrays instead of lists, and structs rather than classes, but I probably wouldn't bother until after I had profiled first. Chances are, I/O is your bottleneck anyway :) – Grundoon Jan 26 '16 at 01:12
  • @Grundoon been there, [done that](https://github.com/Spreads/Spreads/blob/master/tests/Spreads.Tests/Benchmarks.fs). Performance impact of any functional data structures is just huge. For numeric computing, any functional is a plague. I have spend several months of my life just to forget Okasaki and embrace cache locality! – V.B. Jan 26 '16 at 01:17
  • Yes, deep learning and numeric computing are obviously going to be very CPU and memory intensive -- time to shell out to the Fortran code then :) – Grundoon Jan 26 '16 at 01:21
  • @Grundoon F# with occasional P/Invoke and R provider is already great. Just avoid lists and trees and use arrays where possible. – V.B. Jan 26 '16 at 01:28
  • @GuyCoder yes, I have seen that and tested that. It is buried in git history in [this](https://github.com/Spreads/Spreads/blob/master/tests/Spreads.Tests/Benchmarks.fs) (or maybe another branch not on GitHub). Anyway, orders of magnitude difference. Even HAMT which has now a decent [F# implementation](http://fsprojects.github.io/FSharpx.Collections/index.html) is "slow" compared to imperative solutions. I used to implement my own with popcount optimization - still cache locality rules. – V.B. Jan 26 '16 at 02:06