Rescript-compiler: access the default case of a string enum

Created on 30 Jul 2020  路  6Comments  路  Source: rescript-lang/rescript-compiler

Thanks to the new JS representation of polymorphic variants, binding to JS string enums is extremely easy. Though, sometimes you don't control the input and it can be something else than the predefined variants, it would be nice to have a way to access the default case in such cases.

What I had in mind was an annotation as follows:

type status = [ `Offline  | `Online  | `Other of string [@bs.default ]]

You could then use it like that:

  match status with
  | `Offline -> doSomethingWhenOffline()
  | `Online -> doSomethingWhenOnline()
  | `Other other -> doSomethingInOtherCase(other)

Internally, the annotation could transform type status to a private polyvar type to prevent the creation of `Other for example if you don't want to change the representation of the polyvar but just its pattern-matching.

Right now the only type-safe escape hatch I can think of is this:

type status = private [ > `Offline  | `Online ]
external statusToString: status -> string = "%identity";

match status with
| `Offline -> doSomethingWhenOffline()
| `Online -> doSomethingWhenOnline()
|  other -> doSomethingInOtherCase(statusToString(other))

which is not as nice.

All 6 comments

I am not sure I really follow you , can you give more details what you want to achieve?
Why is the second one not as nice?

My goal is to easily bind to JS values that can be some string literals or any other string.
The most obvious use-case I'm familiar with is to bind to GraphQL enums that are often modeled with the ability to expand to new enum cases later on.
This would be represented in Flow as follows:
js type EVENT_TYPES = ('Offline' | 'Online' | '%future added value');

The second one is not as nice because users of your API can't access the value of the other case without using a function, which is not as nice as destructuring.

Since there is a workaround, this is of course a nice-to-have feature which is not as priority as use cases that don't have a zero-cost binding mechanisms yet, like tagged unions for example.

I think what you want is a subtyping between nullary poly variant and string.
So that you can do things like this: ( x :> string)
Note this would be another difference from native. I will raise a separate issue

Also when we have an open poly variant, we lose exhaustiveness checking. So for instance for graphql-ppx binding directly to a string literal for enums is not great because of this. (if all your switches have a catch-all, if a new enum is added you won't have a great user experience.). Not sure what a solution for that would be though (probably not 0-cost).

@tsnobip
it is real, the subtyping is landed on master, see this: https://github.com/rescript-lang/rescript-compiler/pull/5037

that's awesome, thanks a lot Hongbo :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bobzhang picture bobzhang  路  4Comments

paparga picture paparga  路  5Comments

wyze picture wyze  路  3Comments

bobzhang picture bobzhang  路  3Comments

bobzhang picture bobzhang  路  5Comments