Перевести/закодировать данные Haskell Obj = forall a. (Показать a) => Obj a` in Scala

Я не смог придумать, как закодировать Obj в Scala:

{-# LANGUAGE ExistentialQuantification #-}

data Obj = forall a. (Show a) => Obj a

instance Show Obj where show (Obj a) = "Obj " ++ show a

main = print $ show [Obj "hello", Obj 3, Obj True]

при запуске выше выдает следующий вывод:

[Obj "hello",Obj 3,Obj True]

В Scala, однако, это не похоже на компиляцию:

forSome { type T; implicit val ev: Show[T] }

и не делает этого:

forSome { type T : Show[T] }

Возможно ли это на уровне системного уровня или мне нужно "захватить" экземпляр класса типа, используя что-то вроде этого:

class Obj[T](val x: T)(implicit val: Show[T])  // ...or similar

Любое понимание будет оценено!

Ответ 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.

Ответ 2

Я "упаковал" Олега отвечаю на эту общую и (по-видимому) многоразовую структуру:

import scala.language.{ higherKinds, implicitConversions }

trait AnyWithTC[TC[_]] { type T; val x: T; implicit val ev: TC[T] }

// don't like the 'implicit' here; suggestions welcome
implicit def AnyWithTC[T, TC[_]](x: T)(implicit ev: TC[T]) = {
  type T0 = T; val x0 = x; val ev0 = ev
  new AnyWithTC[TC] { type T = T0; val x = x0; val ev = ev0 }
}

тогда data Obj = forall a. (Show a) => Obj a можно реализовать следующим образом:

type Obj = AnyWithTC[Show]
implicit val objShow = Show.shows[Obj] { x => "Obj " + x.show.shows(x.x)   }
val xs: List[Obj] = List(1, true, "hello")
println(xs.shows) // prints [Obj 1,Obj true, Obj hello]