Updated Proposal: Revisiting Implicits

This is not the surprising part and not what’s going on here. Try this instead:

object Test extends App {
  import ConvertibleOps.given
  // error - "Not found: type Convertible"
  type X = Convertible
}

Which is not different than how implicits work nowadays:

object external {
  trait Convertible[A,B] {
    def cast(a: A): B
  }

  object Convertible {
    implicit object IntToString extends Convertible[Int, String] {
      def cast(a: Int): String = s"<$a>"
    }
  }
}

object ConvertibleOps {
  import external.Convertible

  implicit class Ops[A](a: A) {
    def cast[B]()(implicit c: Convertible[A,B]): B = c.cast(a)
  }
}

object Test extends App {
  import ConvertibleOps
  
  // compiles
  val s1 = 1.cast[String]
  val s2: String = 2.cast

  // doesn't
  type X = Convertible
  val x = IntToString
}

Which is one of the reasons why implicits are confusing. They do not only get applied in the local scope upon import – a name resolution mechanism – but are being propagated between scopes, unlike names.