--
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.
Ouch!
Why are traced and outlined both applicable to the same thing, even though one’s type is
outlined : LineStyle -> Shape -> Form
and the other’s type is
traced : LineStyle -> Path -> Form
That’s odd. The library documentation does not convey at all that Shape and Path are the same type. On the contrary, the programmer thinks that there is some type safety here, that the different concepts shape and path are separated and one cannot pass the wrong one to a function. Except, that type safety is an illusion.
I’ve created a GitHub issue, since I think this is a genuine bug (either in the documentation or in the implementation).
It’s an implementation deficiency that requires a language feature to solve. Right now Elm only has type alias, which is a transparent name for another type. Haskell (for example) also has way to give an opaque name to a type using newtype. It uses constructors like we have in union types, to show when you’re talking about the “new type” or the type it wraps.
Basically we want Shape and Path to be distinct so we should use something like newtype instead of type alias.
Now that think about it, maybe you can get behaviour like newtype by using a record alias like: type alias Shape = { unwrapShape : List (Float, Float) } and type alias Path = { unwrapPath : List (Float, Float) } (note the different field names).
I haven’t used records enough to know whether your suggestion would work. But in any case, I don’t think the issue requires a new language feature (or an unorthodox use of an existing language feature like records) to solve. The obvious thing to do is:
type Shape = Shape (List (Float,Float))
type Path = Path (List (Float,Float))
Does this incur some small inefficiency? Yes (because it is not implemented like Haskell’s newtype). But I’m sure there are other places in the standard library where the same small inefficiency is accepted for the sake of abstraction safety (so, where a type with a single constructor is used instead of a type alias).
--
I feel so silly for not having thought of a normal union type with one constructor :) Yes, that’s completely correct, and I remember the first time I learnt about newtype I learnt it was just an optimisation for a union type with one constructor. I remember thinking: If it’s that simple, why not automate it?
Re: “If it’s that simple, why not automate it?”
Well, in Haskell it isn’t that simple. Because there, we have a semantic difference between
data C = C Int
and
newtype C = C Int
Lazy evaluation makes it so (different). But in a strict language, yes, the two would be semantically equivalent, so it would make sense to not have both (and if one worries a lot about efficiency, to automatically optimize the special case “union type with one constructor” to not have a runtime representation for that constructor).