First, I appreciate that this proposal is being made in the spirit of Elm: 
seeking to simplify the language, with reference to existing usage (red 
sentence in the first post), and trying to solve an existing problem 
(serialization of union types). It's clear that Maxime wants to learn and 
improve the language. Thank you.

So let's look at how union types are used, what jobs they are hired to do, 
if you will. You've noticed in core that union types with one tag are used 
to hide things. Sometimes that's as an opaque type: there is one tag that's 
not exposed, and is pattern-matched easily, and the data the tag holds can 
change even in a patch release (example here 
<https://github.com/mgold/elm-animation/blob/master/Animation.elm#L89-L105>). 
Core also contains a rare pattern of, say *type Task x a = Task* where the 
*Task* value defined is not actually used anywhere because it's actually a 
native implementation. Serializing arbitrary union types will have to 
account for this case.

A more realistic use of union types is as a finite set of labels, even if 
these labels don't carry any information along with them. For example:

type Direction = Up | Down | Left | Right

>From this we derive pattern matches with static 
(compile-time) exhaustiveness checking, which is a huge improvement in 
reliability and refactorability over "stringly typed" conventions (instead 
of strongly typed, get it?) that you'll see in JavaScript and the like. 
Replacing the Direction type with a record including a string removes this 
huge improvement.

Adding data to these tags allows for the classic use case of data that only 
makes sense in certain contexts. I remember working with a C++ graphics 
library where certain fields of structs were only defined if an enum was 
set to a particular value. But this isn't really a separate use case from 
labels without data, since you can freely mix within a type. The RemoteData 
type 
<http://package.elm-lang.org/packages/krisajenkins/remotedata/4.2.1/RemoteData#RemoteData>does
 
this to great effect.

More theoretically, union types and records are not the same; they are 
actually duals. Union types are referred to as "sum types" and records (and 
tuples) are "product types". Here's the reasoning: consider two types, *a* 
and *b*. Let's denote the number of values of type *a* as |a|, and 
similarly |b|. If we construct the sum type *Result a b* then there are |a| 
values for the Err case and |b| values for the Ok case. So |Result a b| = 
|a| + |b|, hence sum types. For the pair (a, b) we can pick any *a* and any 
*b* so |(a,b)| = |a|*|b|, hence produce types.

If you want to produce (create) a value of sum type, such as *Result a b*, 
you can pick either case to work with. But when you consume (inspect) a 
value of that type, you must be prepared to handle all cases. For product 
types it is reversed: to produce a value of type (a,b) you must have both 
an *a* and a *b*, but when you consume such a value, you can pick out the 
one you want.

So, while I appreciate the gesture, I don't think this will work.

-- 
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+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to