Discussion:
[elm-discuss] Would Enums make a good addition to Elm?
Robin Heggelund Hansen
2017-08-02 14:00:02 UTC
Permalink
I think most people find that Union Types are a wonderfull thing. I would
argue that much of the beauty of Union Types comes from using them in a
case-of statement, having the compiler complain when there is a case one
haven't covered.

Strings and numbers are not so wonderfull, because they span essentially
unlimited values, so case-of statements become less valuable.

However, strings and numbers still need to be used in production apps for
several reasons:
1) Union Types is not supported in Json, so one needs to convert to/from
strings or numbers for communicating with other systems.
2) Union Types are not comparable, and so cannot be used as keys in Sets or
Dicts.

So a server might send me some json where the current user language is
represented by an Int (0 and 1, norwegian and english) and the current
game being played is a string ("chess", "checkers", "reversi"). It would be
great to have some compiler help for these values (say, you forgot the case
statement for "reversi"), AND it would be great to avoid writing
encoders/decoders for the union types and converting back and forth several
places.

Enums would help with this.

The way I imagine this to work is that an enum essentially represents a
restricted set of String or Number. Once the compiler sees that you're
working with an enum, it could help you out in case statements. And since
an enum is just a string, or just a number, they can be used in the same
places where one would normally use them, like as a value in a select.

One would need a way to check that a string/number matches the values of an
enum though. Something like `Enum.matches : MyStringEnum -> Maybe (Enum
MyStringEnum)`.

The one thing I can think of which makes this a bad addition to Elm, is
that it might be a bit confusing for beginners when to use enum and when to
use unions... maybe.

Anyway, what do people think of this?
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Robin Heggelund Hansen
2017-08-03 08:15:44 UTC
Permalink
Regarding (2), I was not aware of this. Cool =)

Having a deriving-esque way to generate encoders and decoders automagically
would be great, and would go a long way in solving the hassle. (2) would
also make my life easier.

The problem I see with both deriving-esque auto coders (DEAC, patent
pending) and comparable union types, is the difficulty of implementation.
DEAC's seem like an advanced language feature that will take a while to get
into the language. Does a union type require that all fields/members are
comparable as well? Or will it only work for member-less/valueless union
types? As a reader, how easy is it to tell if a union type is comparable,
and is it obvious when one is lesser than another? It's not straight
forward from a design perspective.

Enums have very clear encoding/decoding and comparability semantics, which
is why I think they would be a good addition.

Of course, it could be that DEAC's and comparable union types is the better
way (TM) and one should wait for that instead.
Post by Robin Heggelund Hansen
However, strings and numbers still need to be used in production apps for
Post by Robin Heggelund Hansen
1) Union Types is not supported in Json, so one needs to convert to/from
strings or numbers for communicating with other systems.
2) Union Types are not comparable, and so cannot be used as keys in Sets
or Dicts.
Worth noting that (1) could also be fixed by having a deriving-esque way
to generate encoders and decoders automatically, so long as it worked for
union types as well as records. (Granted, the serialized format is less
obvious with union types compared to records.)
Also worth noting that (2) is already slated to be fixed directly. It's a
lower priority than other things in the hopper right now, but it'd probably
still be higher priority than adding an enum feature. 😄
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Richard Feldman
2017-08-04 14:54:30 UTC
Permalink
Post by Robin Heggelund Hansen
The problem I see with both deriving-esque auto coders (DEAC, patent
pending) and comparable union types, is the difficulty of implementation.
DEAC's seem like an advanced language feature that will take a while to get
into the language.
Having talked to Evan about it, it seems like the design work is what's
hard. The implementation is actually not a ton of work, apparently.

That said, the design work *is* hard. 😉

Does a union type require that all fields/members are comparable as well?
I think that'd necessarily be true, yeah. But if union types and records
are comparable (assuming they hold only comparable types), I *think* you
end up with something like "functions aren't comparable, nor are
Kernel-implemented values such as Value, Task, Cmd, and Html, but
everything else is."
Post by Robin Heggelund Hansen
As a reader, how easy is it to tell if a union type is comparable
Probably not easy, would be my guess. It's unclear to me how big of a deal
that would be though.
Post by Robin Heggelund Hansen
and is it obvious when one is lesser than another?
Apparently the way Haskell does this is by using the order of the tags
(e.g. for type Maybe a = Just a | Nothing, we could say that Just "foo" >
Nothing because Just came before Nothing in the type declaration), which
seems fine to use since that ordering is currently arbitrary and
meaningless. Similarly, record fields could be compared alphabetically by
field name (since record fields definitionally have no ordering; if you
needed them to be compared in a certain order, you could use a union type).

Considering the main (only?) reason people want them to be comparable is so
they can be used in Sets and Dict keys, it doesn't seem hugely important
how they compare, so long as they can be for purposes of internal data
structure implementation details. I could be missing something though!

It's not straight forward from a design perspective.
I totally agree! 😄
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Mark Hamburg
2017-08-04 17:20:01 UTC
Permalink
This post might be inappropriate. Click to display it.
Richard Feldman
2017-08-04 15:04:06 UTC
Permalink
Post by Robin Heggelund Hansen
Having a deriving-esque way to generate encoders and decoders
automagically would be great, and would go a long way in solving the
hassle. (2) would also make my life easier.
The problem I see with both deriving-esque auto coders (DEAC, patent
pending) and comparable union types, is the difficulty of implementation.
DEAC's seem like an advanced language feature that will take a while to get
into the language.
Oh yeah - worth noting that this implementation already exists for records.
Ports do it. If you send a record out a port, or bring it in through a
port, under the hood what's happening is an encoder (or decoder,
respectively) gets generated automatically from the type.

Doing it for union types as well would largely be a matter of designing a
nice serialization and deserialization format; the type information is just
as easy to access as it is for records. 😉
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Ambrose Laing
2017-08-04 16:06:53 UTC
Permalink
It seems like a minor detail that may come as a matter of course, but I'd
like to request that Bool's should also be comparable. If Bools are not
comparable but you embed a Bool inside a tuple or a List or a Record, that
makes the larger type also not comparable. Unless you decide to ignore the
Bool's but I think that is a less useful design choice IMO.

Another general issue is what happens if the programmer does not like the
ordering provided by default for a particular type? Ie. if we are in a
world where False < True, but I would prefer to use True < False, what do I
do. Can there be a special function name ("compare" would be the most
obvious choice) such that if I define my own version of that function for
any given type, then it is used rather than the native default? I think
that would be especially useful if you don't want to compare records
alphabetically by field name.

Eg. I want to sort records by name first and age second -- records of the
type { name: String, age: Int }. I don't want to have to force myself to
rename them creatively to get the sort I want eg: { appelation: String,
maturity: Int } or worse {aName: String, zAge: Int}.

In this instance the ability to override the sort order by defining a
compare function would be helpful.

Alternatively for records, one could use the fact that the listing order
otherwise carries no information, and have the sort order for
records be implied by the listing order. Just like unions in Haskell. So:

{name: String, age: Int}

would sort automatically by name first and age second. But it would remain
legal to write {age = 22, name="Bob"}. This is controversial because it
retains the fact that by definition the ordering of fields in the record is
irrelevant, and yet partially gives them meaning under special
circumstances. There might also be issues if
you define the record one way, and persist the data somehow, and then read
the data back in after compiling with a source code which changes the
listing order of
the fields. Nothing should change. Yet, this solution will cause changes
in sorting behavior.

All things considered, it might be better to allow the programmer to define
a compare function and have that used automatically for Sets and Dicts.
Kind of typeclass-ish, but it may not necessarily require typeclasses to
be exposed in the language. And let the listing order of the field names
in the source code have no significance.
Post by Robin Heggelund Hansen
Having a deriving-esque way to generate encoders and decoders
Post by Robin Heggelund Hansen
automagically would be great, and would go a long way in solving the
hassle. (2) would also make my life easier.
The problem I see with both deriving-esque auto coders (DEAC, patent
pending) and comparable union types, is the difficulty of implementation.
DEAC's seem like an advanced language feature that will take a while to get
into the language.
Oh yeah - worth noting that this implementation already exists for
records. Ports do it. If you send a record out a port, or bring it in
through a port, under the hood what's happening is an encoder (or decoder,
respectively) gets generated automatically from the type.
Doing it for union types as well would largely be a matter of designing a
nice serialization and deserialization format; the type information is just
as easy to access as it is for records. 😉
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Ambrose Laing
2017-08-04 16:48:26 UTC
Permalink
I just realized that the sortBy function already allows you to sort a list
by any sort criterion of your choice. Which removes some of my concerns in
the previous post.
Post by Ambrose Laing
It seems like a minor detail that may come as a matter of course, but I'd
like to request that Bool's should also be comparable. If Bools are not
comparable but you embed a Bool inside a tuple or a List or a Record, that
makes the larger type also not comparable. Unless you decide to ignore the
Bool's but I think that is a less useful design choice IMO.
Another general issue is what happens if the programmer does not like the
ordering provided by default for a particular type? Ie. if we are in a
world where False < True, but I would prefer to use True < False, what do I
do. Can there be a special function name ("compare" would be the most
obvious choice) such that if I define my own version of that function for
any given type, then it is used rather than the native default? I think
that would be especially useful if you don't want to compare records
alphabetically by field name.
Eg. I want to sort records by name first and age second -- records of the
type { name: String, age: Int }. I don't want to have to force myself to
rename them creatively to get the sort I want eg: { appelation: String,
maturity: Int } or worse {aName: String, zAge: Int}.
In this instance the ability to override the sort order by defining a
compare function would be helpful.
Alternatively for records, one could use the fact that the listing order
otherwise carries no information, and have the sort order for
{name: String, age: Int}
would sort automatically by name first and age second. But it would
remain legal to write {age = 22, name="Bob"}. This is controversial
because it retains the fact that by definition the ordering of fields in
the record is irrelevant, and yet partially gives them meaning under
special circumstances. There might also be issues if
you define the record one way, and persist the data somehow, and then read
the data back in after compiling with a source code which changes the
listing order of
the fields. Nothing should change. Yet, this solution will cause changes
in sorting behavior.
All things considered, it might be better to allow the programmer to
define a compare function and have that used automatically for Sets and
Dicts. Kind of typeclass-ish, but it may not necessarily require
typeclasses to be exposed in the language. And let the listing order of
the field names in the source code have no significance.
Post by Robin Heggelund Hansen
Having a deriving-esque way to generate encoders and decoders
Post by Robin Heggelund Hansen
automagically would be great, and would go a long way in solving the
hassle. (2) would also make my life easier.
The problem I see with both deriving-esque auto coders (DEAC, patent
pending) and comparable union types, is the difficulty of implementation.
DEAC's seem like an advanced language feature that will take a while to get
into the language.
Oh yeah - worth noting that this implementation already exists for
records. Ports do it. If you send a record out a port, or bring it in
through a port, under the hood what's happening is an encoder (or decoder,
respectively) gets generated automatically from the type.
Doing it for union types as well would largely be a matter of designing a
nice serialization and deserialization format; the type information is just
as easy to access as it is for records. 😉
--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...