I never understood why Rust couldn't figure this shit out. Scala did.
> If a crate doesn’t implement serde’s traits for its types then those types can’t be used with serde as downstream crates cannot implement serde’s traits for another crate’s types.
You are allowed to do this in Scala.
> Worse yet, if someone publishes an alternative to serde (say, nextserde) then all crates which have added support for serde also need to add support for nextserde. Adding support for every new serialization library in existence is unrealistic and a lot of work for crate authors.
You can easily autoderive a new typeclass instance. With Scala 3, that would be:
trait Hash[A]:
extension (a: A) def hash: Int
trait PrettyPrint[A]:
extension (a: A) def pretty: String
// If you have Hash for A, you automatically get PrettyPrint for A
given autoDerive[A](using h: Hash[A]): PrettyPrint[A] with
extension (a: A) def pretty: String = s"<#${a.hash.toHexString}>"
> Here we have two overlapping trait impls which specify different values for the associated type Assoc. trait Trait[A]:
type Assoc
object A:
given instance: Trait[Unit] with
type Assoc = Long
def makeAssoc: instance.Assoc = 0L
object B:
given instance: Trait[Unit] with
type Assoc = String
def dropAssoc(a: instance.Assoc): Unit =
val s: String = a
println(s.length)
@main def entry(): Unit =
B.dropAssoc(A.makeAssoc) // Found: Playground.A.instance.Assoc Required: Playground.B.instance².Assoc²
Scala catches this too.
Perhaps I'm insufficiently caffeinated, but isn't the author describing the expression problem? That basically nails what type classes are for (in Scala and elsewhere), no?