В вашем супер-простом примере:
equal42("42")
equal42Explicit("42")
равно
equal42("42")(strToInt)
equal42Explicit(strToInt("42"))
, что в случае вашего определения не имеет значения.
НО, если оно сделал что-то еще, например
def parseCombined[S, T](s1: S, s2: S)
(combine: (S, S) => S)
(implicit parse: S => Option[T]): Option[T] =
parse(combine(s1, s2))
, тогда, когда вы примените преобразование, имеет значение:
implicit def lift[T]: T => Option[T] = t => Option(t)
implicit val parseInt: String => Option[Int] = s => scala.util.Try(s.toInt).toOption
implicit def parseToString[T]: T => Option[String] = t => Option(t.toString)
parseCombined[Option[Int], String]("1", "2") { (a, b) => a.zip(b).map { case (x, y) => x + y } } // Some("Some(3)")
parseCombined[String, Int]("1", "2") { _ + _ } // Some(12)
В первом случае аргументы были преобразованы перед передачей (а затем снова внутрь), тогда как в другом если они были преобразованы только внутри функции в определенном c месте.
Хотя этот случай несколько растянут, это показывает, что наличие контроля над выполнением преобразования может иметь значение для конечного результата.
При том, что такое использование неявных преобразований является антипаттерном, классы типов будут работать для него гораздо лучше. На самом деле методы расширения являются единственным не спорным использованием неявных преобразований, потому что даже шаблон ma gnet - единственный другой вариант использования, который может использоваться в производстве (см. Akka) - может рассматриваться как проблема.
Так рассматривайте этот пример из документации как демонстрацию механизма, а не как пример хорошей практики, которую следует использовать на производстве.