facebook / flow

Adds static typing to JavaScript to improve developer productivity and code quality.
https://flow.org/
MIT License
22.08k stars 1.85k forks source link

Documentation claims enums are on the way #859

Closed StoneCypher closed 7 years ago

StoneCypher commented 9 years ago

I'm not sure whether this is a mistake, but freenode samg_ requested a filed issue.

The future plans page suggests that enumerations are on their way.

IRC suggests that these should just be a named union of string literals, eg

type suit = 'heart' | 'club' | 'diamond' | 'spade' | 'joker';

Is there another thing actually called enumerations on its way (eg maybe on symbols?) Also, could this page be improved to note the union approach?

Thanks much

StoneCypher commented 9 years ago

(now writing a PR)

samwgoldman commented 8 years ago

Looking forward to the PR. Since you're on top of it, let's close this.

RaeesBhatti commented 8 years ago

Any updates on this?

StoneCypher commented 8 years ago

sorry, i was hoping to ask for clarification on whether there's something called enums underway. i don't understand why this got closed.

StoneCypher commented 8 years ago

i think my comment about writing a pr was just that i was going to put a workaround note in the docs, but this was months back and i've long since forgotten what my intent was

samwgoldman commented 8 years ago

Sure. Can you provide an example of what you're referring to when you say enum? Jus to make sure we're talking about the same thing.

samwgoldman commented 8 years ago

bueller?

StoneCypher commented 8 years ago

I mean, that's sort of what this ticket is about, is to find out what the Flow authors meant when they wrote enums into their documentation.

tl;dr: i don't know the answer to what i'm referring to, because what i was doing was asking you what you were referring to. the long horrible thing below is what i would like, but it's probably not what you had in mind. trap door: reified set of strings defined under a label as an admissible set of (literals? i 'unno.)

god this gets super boring and ranty and very very poorly explained. i apologize in advance

if you are bored by things taking more than six hours, bail now.


this is a reference to that last bullet point:

A bunch of type system features like bounded polymorphism, enums, function purity analysis and lots more

And I mean, we're talking four months ago, and that might as well be two laps around the Wheel of Dharma, for all my memory is concerned, but the note left:

type suit = 'heart' | 'club' | 'diamond' | 'spade' | 'joker';

uh

to me it looks like what i'm doing there is shorthand for what lisp / prolog / erlang / etc call atoms, what js calls symbols, what c/c++ sort of calls enums but without the actual number part, et cetera.

so i guess if i'm using the cards example with a joker there, i must have been in the erlang atom mindset, because you only set those apart as not part of the sequence if you're in a language where filtered generators are convenient, so i guess i had been thinking of these as a collection of reified strings.

maybe? i dunno.

anyway, that'd be proper useful. that's the norm in the bcpl family (though over there you have to staple it to integers; hooray) and not-uncommon in the lisp and prolog families. especially now that js has pattern matching or destructuring assignment, depending on what phase of the moon it is, just tossing these things around like labels ends up being super useful. if you don't erlang or prolog, the most natural explanation is probably c++ SFINAE, if substitution occurred on pattern matching in the signature. (deep breath.)

i mean, i realize this sort of sounds dumb and rambling. and that's only about 80% my storytelling skills' fault. but hey, an example can both make this terribly, terribly longer and more boring, while making me think i'm being clearer and achieving the exact opposite.

so consider basically any moderately complicated example of multimethods in a language that doesn't have multimethods, eg every language you'd ever actually use in the real world. everyone's favorite case is, of course, double dispatch.

erlang dipshits don't generally have to double dispatch, because there's a much easier cheat for us. we have arbitrary ordering over everything (except numerics, because fuck anything actually working right, grumble mutter broken sort mutter grumble,) which includes instances of functions and instances of signatures.

so we just cheat by pattern matching it out on euler's triangle! wait, come back, don't go, that actually means something, come on buddy, i have snacks

euler's triangle: if you get foo(a,b) where a>b, just call foo(b,a) and return the value. don't implement both sides; just implement one side and cache the result.

all we do is:

thing( { _, Left }, { _, Right } ) when Left > Right ->
thing( { type_name_as_atom, Left }, { type_name_as_atom, Right } ) -> impl

then provide what most languages would call overloadings of thing/2 on the various type_name_as_atom concretes

the only actually obvious concrete example of this i've ever seen is collision detection

routines for collision detection on two circles are super fast because there's a cheat in just taking the distance between the two centers and comparing it to the sum of the radii. there's also cheats for aligned box overlap, and less effective cheats for unaligned box overlap. there are good cheats for circle-with-square and circle-with-box. if you get into weird shapes, it gets complicated.

so it's actually useful to be able to call collide(thing, otherthing) and have it just sort of figure out which collision routine to call, and have it be the most efficient available one.

in erlang, we don't even bother. we just write out a sequence of matches and direct them. it's crude, but it's also super easy to write, maintain, extend, and debug, and you can keep it generic relatively easily with functions or modules.

so

i feel like y'all were referring to enums, going on the discussion from irc, as a "these strings and nothing else."

what (forgive me) mysql would call an ENUM(), because it's the only thing I can think of that actually does this with strings.

i haven't bothered to learn what es6 symbols are yet. there's a chance they may be a better choice for what i'm describing. there's also a chance that that's a terrible idea.

StoneCypher commented 8 years ago

if you're trapped into using string literals as values (which is common in js,) but if you know in advance that there is a finite admissible set, then having that enforceable within a type system would be hella useful, and the most natural name i can think of for that is enum.

y'all said enum first. i don't know what you had in mind. but when you said it, that's what i heard, and that'd be useful.

samwgoldman commented 8 years ago

Haha, all right. So I admit to just skimming the above, but my take-away is that we said something about enums at some point and we were super unclear about what that meant.

So I didn't write that doc, so I don't actually know what the author had in mind. However, I can try to provide some insight into what we do have.

We have string literal types, which restrict the inhabitant space to be exactly a single value, so let x: "foo" = "foo" is OK but let x: "foo" = "bar" is a type error. String literal types enable disjoint unions.

While the linked blog post discusses disjoint unions of object types, where each object is distinguished by a string literal "sentinel" value, we can also create disjoint unions of strings, e.g., type Suit = "diamonds" | "clubs" | "hearts" | "spades". An inhabitant of Suit can be one of those 4 strings and no other value.

Enums in other languages tend to have another property though, which is an ordering. Suits of cards is a handy example, because there is an order to them, which happens to be different from the lexicographic order. Our Suit representation doesn't imply any ordering, however. Indeed, the inhabitant "diamond" of the Suit type is not special—it's just a string.

Unions of strings do come up quite a bit though. For example, consider Object.keys. The expression Object.keys({ foo, bar }) has the type Array<"foo"|"bar">. The type "foo"|"bar" represents the type of each key from the object. So does string, or any for that matter.

Note that we also have number literal and even boolean literal types. That is, 2 is a type admitting a single inhabitant: the value 2. Similarly, true is a type, and so on.

So that's what we have now. If you wanted to specify an ordering to an enum-as-union-of-literal-types, you could do so, but only externally. Say with a function suitCompare(a: Suit, b: Suit): number where the sign of the return value determines the order. This is even arguably more flexible, as different card games might insist on different orderings for suits.

So, if I had to guess, the hinting at an "enum" was most likely describing something more like enumerated types in C, which introduce named constants with backing values. Flow doesn't have any syntax to introduce runtime bindings, constant or otherwise. What we have just describes values, but is still pretty useful.

All that said, it might help add context to this discussion to learn if there is any particular code you are trying to write that might make use of an enum feature, whatever form it might take, real, planned, or imaginary.

StoneCypher commented 8 years ago

I don't actually want C enums; I want lisp/prolog/erlang atoms. And, whereas those are also ordered (lexicographically,) that property isn't the part I care about; legitimate membership enforcement is.

I'm not entirely certain how their ordering would factor into a type system, though I may just be being naïve.

The poker example is an adequate example for why. Granted, poker is very simple; there're only nine (depending on how you count jokers) labels worth validating.

My perspective on atom enforcement is the simple one. I just want my type system to warn me when I've made typos, used wrong labels, wrong international spellings, &c.

calebmer commented 7 years ago

I’m going to close because we do support string enums. If you think there is a better way we can provide enums please open another issue and we can have a feature discussion! 😄