Skip to content

Instantly share code, notes, and snippets.

@propensive
Last active June 8, 2016 09:58
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save propensive/8894fd2a58ac93bfa2c3 to your computer and use it in GitHub Desktop.
Save propensive/8894fd2a58ac93bfa2c3 to your computer and use it in GitHub Desktop.
// Define the following traits and companion object
// It's in Rapture Core (https://github.com/propensive/rapture-core) if you don't want to
trait LowPriorityDefaultsTo { implicit def fallback[T, S]: DefaultsTo[T, S] = null }
object DefaultsTo extends LowPriorityDefaultsTo { implicit def defaultDefaultsTo[T]: DefaultsTo[T, T] = null }
trait DefaultsTo[T, S]
// Then, assuming we want to specify a default for a type class like `Namer`,
case class Namer[T](name: String)
// where we have a couple of alternatives,
implicit val stringNamer = Namer[String]("string")
implicit val intNamer = Namer[Int]("int")
// we define the default one for a particular method like this:
def myMethod[T](implicit default: T DefaultsTo String, namer: Namer[T]) = namer.name
// Let's try it out in the REPL:
scala> myMethod
res0: String = string
scala> myMethod[Int]
res1: String = int
@sourcedelica
Copy link

Very nice. I just added this to a much-used utility we have and it worked like a charm. Thanks!

@propensive
Copy link
Author

Cool! :)

@chirlo
Copy link

chirlo commented May 8, 2015

It's even more expectacular if you change it to case class Namer[T](t:T) , then you get type safety on the default value!

@4lex1v
Copy link

4lex1v commented Oct 12, 2015

Jon, i wonder why did you place fallback to LowPriorityDefaultsTo, if i understand SLS correctly there's no ambiguity between these two implicits, since defaultDefaultsTo more specific then fallback, so if we omit the type parameter, the compiler would pick it instead of fallback, hence no ambiguity. So why placing it on a different layer?

To be sure this compiles on 2.11.7

trait DefaultsTo[Type, Default]
object DefaultsTo {
  implicit def fallback[T, D]: DefaultsTo[T, D] = null
  implicit def defaultDefaultsTo[T]: DefaultsTo[T, T] = null
}

trait Document
final class Table[DocType](val name: String)(implicit default: DocType DefaultsTo Document)

object Test {
  val table = new Table("settings")
  val table2 = new Table[String]("settings2")
}

@propensive
Copy link
Author

@4lex1v If that works, great! I tend to ignore the type-specificity rules and go straight with stacked traits, but that's a useful optimization!

@yanns
Copy link

yanns commented Oct 15, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment