Making ad-hoc polymorphism less ad hoc
swizec.com
swizec.com
I've been working on a language which attempts to put type classes into a dynamic language. The implementation is a little interesting, because as I've tinkered with it, I've discovered essentially two cases of overloaded functions:
* Ones where you can choose which implementation to use based on argument type. Similar to interfaces in Java, Go, etc, and multiple-dispatch languages. Also (I think) Rust only allows traits of this kind, but that might not be correct.
* Ones where you choose the implementation based on the return type. This is mostly (from what I can tell) unique to Haskell and similar languages. It's what lets you write `return :: a -> m a` and be able to write `foo = return 3` and have `foo` work in any monad.
Both of these approaches have their advantages and use cases. Haskell's system is nice because it allows for both, but because of how things are implemented, there's no introspection, so for example, a list "objects that implement Show" isn't feasible, because there's no way to inspect an object at runtime to determine its type. Or this is my understanding anyway.
In my language, I treat each case differently, and can generate appropriate generic functions by inferring from the declared type signature(s) in the type class which case they fall into. Subsequently, its possible to mimic both behaviors in a dynamic setting. The only limitation (which I might be able to avoid) is that using the second type requires an explicit cast; as in you can say `foo = pure 2; bar = foo : [Num]; baz = foo : Just Num`. Of course, introspection has a performance penalty as well.
Just thought some might find this interesting :)
One note: it is indeed possible to create a list of "objects that implement Show" in Haskell, but it's a bit clunky. Look into existential types.
Existential types are kind of the same, but they require the declaration of extra types to use them (for example, the `Obj` type used in the example on the Haskell wiki entry on existential types), which is clunky as you say. But at first glance, they do seem to provide a similar degree of power.
ghci> :i Num
class Num a where
(+) :: a -> a -> a
thus ‘memsq’ would look as follows: memsq :: (Eq a, Num a) => [a] -> a -> Bool
memsq xs x = square x `elem` xs(Wonder why I despise Markdown? Take a look at the paragraph immediately under the heading "Type Classes". I suppose this could be a formatting error, but I'm betting it's a Markdown misinterpretation.)
No more misinterpretation, and no more uncaught syntax error.
square :: Num a => a -> a
square :: Num a => a -> a instance Eq a, Eq b => Eq (a,b) where
(u,v) == (x,y) = (u == x) & (v == y)
(I'd check < in spite there are no examples in the article.)