Scala неявный выбор использования

Мне было интересно, прозрачны ли конверсии implicit, и действительно ли лучше использовать implicits больше, um, явно. Например, предположим, что у меня есть метод, который принимает Date как параметр, и у меня есть неявное преобразование, которое превращает a String в Date:

implicit def str2date(s: String) : Date = new SimpleDateFormat("yyyyMMdd").parse(s)

private def foo(d: Date)

Тогда, очевидно, я могу назвать это прозрачным преобразованием implicit:

foo("20090910")

Было бы лучше сделать тот факт, что я преобразовываю строку в дату более явным?

class DateString(val s: String) { 
  def toDate : Date = new SimpleDateFormat("yyyyMMdd").parse(s) 
}

implicit def str2datestr(s: String) : DateString = new DateString(s)

Итак, использование выглядит больше:

foo("20090910".toDate)

Преимущество этого заключается в том, что более понятно, что происходит. Я несколько раз поймал прозрачные преобразования implicit, о которых я должен знать (Option to Iterable кто-нибудь?) и это использование все еще позволяет нам использовать преимущества implicit s.

Ответы

Ответ 1

Я считаю, что более "явный" способ делать неявные преобразования намного лучше с точки зрения удобочитаемости, чем полностью прозрачный, по крайней мере в этом примере.

По-моему, использование implicit полностью прозрачно от типа A для типа B является ОК, когда вы всегда можете просмотреть объект типа A как способный использоваться всякий раз и объект типа B необходимо. Например, неявное преобразование a String в RandomAccessSeq[Char] всегда имеет смысл - a String всегда, концептуально, можно рассматривать как последовательность символов (в C строка - это всего лишь последовательность символов, для пример). Вызов x.foreach(println) имеет смысл для всех String s.

С другой стороны, более явные преобразования следует использовать, когда объект типа A иногда может использоваться как объект типа B. В вашем примере вызов foo("bar") не имеет смысла и вызывает ошибку. Поскольку Scala не имеет проверенных исключений, вызов foo(s.toDate) ясно указывает на то, что может возникнуть исключение (s может быть недействительной датой). Кроме того, foo("bar".toDate) явно выглядит неправильно, в то время как вам нужно проконсультироваться с документацией, чтобы узнать, почему foo("bar") может быть неправильным. Примером этого в стандартной библиотеке Scala является преобразование из String в Int s через метод toInt обертки RichString (String можно рассматривать как Int s, но не все время).

Ответ 2

Когда вы делаете неявное преобразование из X в Y (например, преобразование из String в Date выше), вы, по сути, говорите, что если бы вы имели полный контроль над написанием X в первую очередь, вы бы сделали реализацию X или быть подклассом Y.

Если для X есть смысл реализовать Y, добавьте преобразование. Если это не так, то, возможно, это не подходит. Например, для String имеет смысл реализовать RandomAccessSeq [ Char], но, возможно, для String не имеет смысла реализовать Date (хотя String, реализующая StringDate, кажется прекрасным).

(Я немного опоздал, и у Флави есть отличный ответ, но я хотел добавить комментарий о том, как я думаю о implicits.)