Hiding stuff can be very useful. When you create a library, or a DSL, or perhaps a framework, you want to provide a good user-experience by hiding implementation details. At the same time, you don’t want your code to add a magnitude of complexity, to fulfil this requirement. Fsharp is perfect for that, and the following may help.
Note that I will only handle cases which are harder to find on the internet, for basic syntax, please follow provided links.
Signature files may be used to show or hide things at a file level. So if you have code file “mycode.fs”, you can have signature file “mycode.fsi”, which needs to come before the .fs file, in the file order of the project. So “mycode.fsi” specifies what can be seen in “mycode.fs”, by other files in the same project, and outside the project. What’s included in the .fsi will be accessible, what’s excluded will be hidden. You can automatically generate a signature file for the whole project, and use this as a start-point . I’ve seen issues with a generated signature file, so you still might need a bit of editing, but in most cases this is unnecessary.
The cases of a discriminated union can either be fully hidden or fully public.
Hiding in a record
A record may have record properties, but also static and instance members . A record may not inherit from another class (including an abstract class), but a record may implement an interface. So how do you show and hide all that?
First example is with all record properties public. Example code:
If a record implements an interface, you can hide it by excluding it from the signature file. You will get a warning that via reflection, one can still derive the interface. This is a design choice, if you hide to provide user experience, why care about someone digging with reflection?
Signature files are a nice way to hide and show large parts of your implementation. But sometimes you need it more fine grained.
If for example you have implemented an interface, or inherited from a baseclass (for classes), then you are required to include all interface or baseclass members in the signature file.
Sometimes that is more than you want to be public, because there can be technical reasons to choose for a baseclass/interface. For example, you want a set of types to implement a function member, while this function is only used internally. For user-experience, you don’t want to show that technical function to the outside world.
To hide such technical methods, you can use the “internal” access modifier on individual member functions, including “new “. You still need to mention all interface/inherited member functions in the signature.
The outside world will see type “Base”, but that doesn’t have to be bad. You can also hide “Base”, but whether this is a good idea depends on your design.
You want to show a baseclass like “Base”, when you also implement types “StoreSomethingElse”, “StoreAnothingThing”, and you need to provide a combinator operator like this: