@unboxed
type facing = | @as(true) Up | @as(false) Down
let f = v => {
switch v {
| Up => Console.log("up")
| Down => Console.log("down")
}
}
Which I really like because in my case I need to know if an object is Up or Down, a clear binary value.
But I never really need this for any boolean logic. So having English words for this is really beneficial.
Anyway, Up and Down are very generic.
In F# you can use an attribute RequireQualifiedAccess to mark your type.
With that you can’t use Up or Down without specifying its owning type.
Normally, the case identifiers can be used without qualifying them with the name of the union. If you want the name to always be qualified with the name of the union, you can apply the RequireQualifiedAccess attribute to the union type definition.
In F# this would be:
[<RequireQualifiedAccess>]
type Direction = | Up | Down
// usage
let x = Direction.Up
let y = Down // won't work, compiler blocks it
What is in ReScript the closest I can get to this behaviour?
There’s no exact equivalent of course, but you can get pretty far by putting the type in its own module and just never opening it:
module Facing = {
@unboxed
type t = | @as(true) Up | @as(false) Down
}
let f = v => {
switch v {
| Facing.Up => Console.log("up")
| Down => Console.log("down")
}
}
This works because putting it in its own module hides it to the outside world, unless you explicitly open it. So, the compiler won’t be able to infer that it’s Facing.t you’re after unless you also qualify it with the Facing module, so the compiler knows where to look for the variant constructors.
There are a few things you can do, the simplest being to just choose longer variant names:
@unboxed
type facing = | @as(true) FacingUp | @as(false) FacingDown
let f = v => {
switch v {
| FacingUp => Console.log("up")
| FacingDown => Console.log("down")
}
}
You can of course put it in a module and always prepend the module name as @zth explained!
If you want to add another layer of security, you could also declare it as private and create helper functions to create such variants:
module Facing: {
@unboxed
type t = private | @as(true) Up | @as(false) Down
let up: t
let down: t
} = {
@unboxed
type t = | @as(true) Up | @as(false) Down
let up = Up
let down = Down
}
This way, you can enforce that you have to use the module name to produce such values.
Even with a separate file that didn’t seem to work. Up was still always directly available.
This may be related to the fact that I have a namespace in my project.
I settled on having unique names, the module interface does indeed work, but seems too long for my taste. It is fine if you have it once, but grows ugly when you have three instances of it.
if you annotate the type of the variant, then you can use the variant without its module name, the compiler won’t enforce you to do it but you can still decide to manually prepend it and it won’t be formatted away!
Having unique names is likely the simplest solution. This is what I usually end up doing myself.