The following code does not compile:
module M
type private T = T
exception private E of T
Note that if the type is marked internal, the code compiles.
I inspected the generated C# code in SharpLib and there seems to be no difference between the private and internal modifiers.
So if the internal is allowed, private should be allowed too?
Repro steps
Provide the steps required to reproduce the problem:
Expected behavior
The above code should compile. It should be possible to match upon (& deconstruct) the exception E within the same module M that the exception E is defined in.
Actual behavior
Compiler error:
The type 'T' is less accessible than the value, member or type 'Data0' it is used in.
Known workarounds
Type T can be marked internal and the code compiles.
As discussed on slack, I believe the cause is because exception is special, in that it creates a DU with fields. Though I agree that it should be allowed, but I don't know what the best way, if any, is to fix this (the type is sealed, and making the fields and you're private, would mean you cannot use it in a match expression in a try... with).
Also note that there's a slight difference in using exception, or a classic exception class that inherits from exn. The latter cannot be used in match expressions, unless with :?.
Yes, this is current by design. It's roughly the same as the following, which also fails:
module M
type private T = T
type E = T of T
I think this could be discussed as a potential language suggestion here: https://github.com/fsharp/fslang-suggestions
Hi, @cartermp. Do you know what is the design decision behind not allowing private types here? I'm asking, because I don't want to be pulling the cart in a different direction just for the sake of it.
Allowing the above improves usability of the construct & encapsulation, but that might not outweigh whatever it is that I'm missing here.
@giedriusbruzas, I just checked how this gets compiled. If you take an exception type (which, as you know, is a special kind of Exception that you can map over), it gets compiled as follows:
exception private Foo of string
Becomes decompiled:
```c#
internal class Foo : Exception, IStructuralEquatable
{
internal string Data0@;
[CompilationMapping(SourceConstructFlags.Field, 0)]
internal string Data0
{
get
{
return Data0@;
}
}
internal Foo(string data0)
{
Data0@ = data0;
}
internal Foo()
// ...
If it's `public`, the fields also become public.
Now, if you us `of SomePrivateDuOrRecord`, which is also compiled as `internal`, the compiler itself checks whether the `private` is used correctly. Here we get into a problem: the compiler sees a type, internally marked as `private`, being used in another type's field accessor, which is typed `internal`. This is not allowed.
However, other types remedy for this fact, so I think it should be placed in the list of exceptions of the `exception` type: there's a bunch of outstanding issues and this belongs in the same area.
Note that, if the internals of the auto-generated members would not be `internal`, there would be plenty of scenarios where things would fail to fit together.
My original thoughts appear to have been incorrect, or at least ill-informed. I also think, @cartermp, that your example doesn't correspond to the original example, which has a private class (exception) use another private class (DU) for data. It's more akin to:
```f#
type private T = T
type private E = T of T
which is valid. In retrospect, that would make this a bug...
Sorry, I'm mistaken on the DU case as it is allowed so long as E (the DU) is also marked private and I forgot to do that.
@dsyme why is this behavior the way it is?
Hi, @cartermp. Should this be reopened? I don't want to be annoying, but also don't want to make an impression that I forgot about it. If it's a minor issue, it can just live here for a long time, I don't mind. But if it's closed, it's gone.
Thanks for the ping, will re-open. I think it's probably a minor issue since this appears to have been the behavior for quite a long time, if not since F# has existed.
Most helpful comment
Hi, @cartermp. Should this be reopened? I don't want to be annoying, but also don't want to make an impression that I forgot about it. If it's a minor issue, it can just live here for a long time, I don't mind. But if it's closed, it's gone.