Ответ 1
Вы получили это почти правильно:
import scalaz._
import scalaz.Scalaz._
trait Obj {
type T // existential type
val x: T
implicit val show: Show[T]
}
implicit val objSow: Show[Obj] = Show.shows[Obj] { (x: Obj) =>
x.show.shows(x.x)
}
object Obj {
/* "constructor" */
def apply[U](_x: U)(implicit _show: Show[U]): Obj = new Obj {
type T = U
val x = _x
val show = _show
}
}
val test: List[Obj] = List(Obj(1), Obj(true), Obj("foo"))
/*
scala> test.shows
res0: String = [1,true,"foo"]
*/
P.S Я бы хотел использовать T
и show
в apply
; не U
и _show
. Если кто-то знает, как избежать затенения, я буду признателен!
В качестве альтернативы вы можете использовать forSome
:
import scala.language.existentials
trait ObjE {
val pair: Tuple2[T, Show[T]] forSome { type T }
}
/* And to define Show instance we have to help compiler unify `T` in pair components. */
def showDepPair[T] = Show.shows[Tuple2[T, Show[T]]] { x => x._2.shows(x._1) }
implicit val showObjE = Show.shows[ObjE] { x => showDepPair.shows(x.pair) }
Здесь мы должны использовать Tuple2
(или другой вспомогательный тип) для захвата show
. Мне больше нравится предыдущий вариант. Для меня проще обернуть ум вокруг члена типа.
Также в Scala "Don Giovanni" forSome
синтаксис будет устранен в пользу val pair: ({ type λ[T] = Tuple2[T, Show[T]] })#λ[_] }
, который работает уже слишком, Я надеюсь, что будет поддержка синтаксиса для типа lambdas. kind-projector не помогает в этой ситуации (повторное использование T
). Возможно, что-то вроде Typelevel scalac
: val pair: ([T] => Tuple2[T, Show[T])[_])
.
Другим основополагающим изменением будет:
Одно фундаментальное понятие – члены типа – может дать точное значение для дженериков, экзистенциальных типов, подстановочных знаков и типов более высокого уровня.
Таким образом, обе формы будут эквивалентны с точки зрения компилятора (в предыдущем мы распаковываем кортеж). Я не уверен на 100%, каковы различия в настоящее время, если они есть.
P.S. The Troubles with Types помогли мне понять системные причуды типа scala.