logoalt Hacker News

mboyesterday at 11:51 PM1 replyview on HN

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.

Replies

switchbaktoday at 12:09 AM

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?