Right now the only way to define an opaque type is to do so via an interface. eg.
module Length: {
type t
let feet: int => t
let meters: int => t
} = {
type t = Feet(int) | Meters(int)
let feet = n => Feet(n)
let meters = n => Meters(n)
}
What if we could take the lead of Flow’s opaque types and have the ability to mark the type as opaque to anyone outside of the module eg.
module Length = {
opaque type t = Feet(int) | Meters(int)
let feet = n => Feet(n)
let meters = n => Meters(n)
}
Length.t would be opaque everywhere apart from inside of the Length module itself.
Every now and then I’m struggling with the same question. I would like to use them more, but it’s not really convenient to work with.
I tried four different implementations, but there is still just the solution with an interface, right?
An interface file just to hide the implementation detail of a single type
I guess this should be considered together with a private-by-default PoC.
Which is mostly (but not entirely) the thing that makes .resi files obsolete.
I would argue making .resi obsolete is a good idea. On one side I can understand it: the interface files may look foreign to people coming from JS/TS, but they are an excellent feature!
When you explore a codebase, you may only take a look at 20-line .resi and immediately reason about the module without having to navigate across hundreds/thousands of implementation lines to find the public signatures. Multiply this by how coding agents look at things (the shorter and more precise the context, the better), and it makes .resi invaluable.
not at all, i just want to be able to do opaque types without having to resort to .resi files. actually we should just make types module private by default, and you just put pub in front of anything you want to be seen from the outside
Yeah I can get behind that.
That’s a heavy breaking change though.
My general idea was to have this (transitional?) behavior:
There is a .resi → nothing changes, the exports are defined by the .resi
Theres is no .resi and no pub keyword → nothing changes, everything is exported
Theres is no .resi and at least one pub keyword → enter private-by-default mode, only what is marked pub is exported
Theres is a mismatch between .resi and a pub value → probably (a new kind of) interface mismatch error.
Special case: entry-point module where you export nothing. We COULD require to have a .resi for this one since no pub usually means everything is exported. Which may be a good style anyway to promote .resi to a package description file.
I am probably missing some edge cases here but something like that would be nice.
essential the entry point for any self-contained software, say main.res, should only really have let main anyway, so makes no practical difference. i agree with how you’d transition it, in reality, no modules will have zero pubs anyway (unless they want to export everything - could add a warning to warn on “pub-by-default” because no pub keyword)