Hello
In the spirit of learning more about functional programming, I’ve watched a couple of talks on YouTube by Simon Peyton Jones, and the journey of Haskell!
Question
As Simon gets into type-classes at this point in one of his talks, I immediately though of a controversial statement from this community:
Elm doesn’t support type-classes!! When will it do so??
Needless to say, after rewatching a couple of times, and thinking about my intuitions on the subject, I wasn’t really convinced.
So, does Elm not support typeclasses?
Experiment:
I’ve done some experimentation to qualify my question a bit.
I was passively consuming video, but really felt like I could play with this idea a bit, so I went ahead and did just that.
Simon says that the underlying “System F” translation from Haskell has functions with the bounded polymorphism of type-classes simply take an extra argument with a record of functions in the given class. Here’s some type-signatures from Haskell:
sort :: Ord a => [a] -> [a]
serialise :: Show a => a -> String
member :: Eq a => a -> [a] -> Bool
ok, so in Elm:
module Orderable exposing (Orderable, isOrderable)
type alias Orderable kind value =
{ kind | orderBy : value -> value -> Basics.Order }
isOrderable : Fuzzer value -> Order kind value -> Test
isOrderable fuzzer orderable =
Test.describe "can this type of value be ordered?"
[ isSymmetric fuzzer orderable
]
isSymmetric : Fuzzer value -> Orderable kind value -> Test
isSymmetric fuzzer { orderBy } =
Test.fuzz2 fuzzer fuzzer "is the ordering symmetric?" <|
\first second ->
case orderBy first second of
EQ ->
Expect.equal (orderBy second first) EQ
LT ->
Expect.equal (orderBy second first) GT
GT ->
Expect.equal (orderBy second first) LT
So far, so good. Let’s say this exists as a library on elm-package - now, how would I implement this sort of interface on a binary-tree kind of thing?:
module Tree exposing (Tree, empty, insert)
import Orderable exposing (Orderable)
type Tree value
= Leaf
| Branch (Tree value) value (Tree value)
empty : Tree value
empty =
Leaf
insert : Orderable kind value -> value -> Tree value -> Tree value
insert order value tree =
case tree of
Leaf ->
Branch Leaf value Leaf
Branch left middle right ->
case order.orderBy value middle of
EQ ->
tree
LT ->
Branch (insert order value left) middle right
GT ->
Branch left middle (insert order value right)
Simon says type-classes are extensible. Well, the kind
-type-parameter makes Orderable
extensible in the sense that you can use the extensible-records feature of Elm to build a new type-alias around it and extend with new functions - a more elaborate example of this sort of tinkering is available here. In my experience, this is a misuse of extensible records, but it’s the nicest I could come up with here.
Finally:
- type-classes can exist in the package-ecosystem, as a niche.
- you explicitly import type-classes, and have to test implementations to make sure axioms are respected
- runtime implications aren’t obvious to me - does Haskell do anything fancy to make the “pass records of functions around” fast somehow? Can it cause slowdowns?
Maybe this is a horrible idea? if so why? I don’t think I’d hate programming on this sort of interface.