As John Kelly pointed out to me in another thread, there's a trick called a
"phantom type" that's possible in Elm. It's where a union type has a type
variable that's not used on the right side. If you don't expose the
constructor, you can use the type to only allow values that have come
through other functions. Here's an example:
-- the Phantom Type
type Measurement a
= M Float
-- "dummy" types
type Length = Length
type Width = Width
type Area = Area
add : Measurement a -> Measurement a -> Measurement a
add (M a) (M b) =
M (a + b)
multiply : Measurement Length -> Measurement Width -> Measurement Area
multiply (M a) (M b) =
M (a * b)
I've been thinking about how to make these guarantees, there was a recent
ThoughtBots post on it, and I'm not sure anyone has seen this before.
Building out a whole system of measurement this way seems really tedious,
but for a smaller and more specific problem, it might be a valuable
technique to know about.
Note that this only works if it's impossible to create a polymorphic type,
e.g. *Measurement a*, because that will get unified with whatever type is
expected by the annotation. If every Measurement value is concrete, it
can't be passed in the wrong place.
As I've been thinking about this, I've seen multiple orthogonal pieces of
information that we sometimes sloppily roll up into the type:
- The *representation*, the storage of bits in memory, such as IEEE
floating point or 64-bit integer.
- The *dimension*, in the physics sense, such as length*mass.
- The *interpretation*. Is it length or width or height? Is length*mass
torque or momentum?
- The *units*, such as meter*kilograms.
When dealing with a discrete case -- the classic example is not mixing up
rows and columns in a grid/table -- we need the representation and the
interpretation (rows or columns), but units and dimensions seem not to
apply. Similarly, if we want to distinguish between strings (URLs, IDs,
escaped and unescaped HTML, and so on), we seem to want the same things.
It might be interesting to have a wrapped String library that would perform
the typical operations on strings with type-level identifiers, so when you
require an ID (aliased to WrapedString IDType) you can only get one that's
been through your validator. Or you could build URL-specific abstractions
like *Url.join : Url -> List String -> Url*.
So, just a neat idea not everyone is aware of, hopefully someone finds it
interesting or useful.
--
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 [email protected].
For more options, visit https://groups.google.com/d/optout.