Just to be clarify for other readers of this thread: in order to achieve the kind of usability that is found in Haskell and Rust, you need both mechanisms, type classes/modular implicits and “deriving”, simultaneously (and indeed both Haskell and Rust implement both mechanisms).
The reason is that “modular implicits” will never define new functions for you. It will just make it easier to figure out which existing function to use at a given type. In order to automatically define new functions for user-defined types you need a “deriving”-type mechanism as well.
Concretely: suppose a user-defined type t
together with a function show_t : t -> string
. Suppose there is also a function show_list : ('a -> string) -> 'a list -> string
giving a way to “show” an 'a list
given a way to “show” an 'a
. Then in order to “show” a value x : t list list list
, you need to use show_list (show_list (show_list show_t)) x
.
-
Modular implicits will make it possible to simply write
show x
and the typechecker will figure out the precise combination ofshow_t
andshow_list
that should be used. -
“Deriving” will define the functions
show_t
andshow_list
for you.
If you only have modular implicits in your language, then you still need to define “show” functions for user-defined types (eg type t = A of int | B of bool
, or record types, or …), which is where a big part of the boilerplate is actually found.
Personally I think that a built-in “deriving” mechanism that better integrates with the compiler is more of a missing killer feature than “modular implicits”. But as mentioned above you need both mechanisms to achieve the usability of Haskell/Rust.
Cheers,
Nicolas