Фантомные экзистенциальные типы в Scala - PullRequest
2 голосов
/ 23 февраля 2020

Я пытаюсь написать класс значений, который охватывает Scala коллекции Map и предоставляет альтернативу get. Я пытаюсь использовать фантомный тип в классе значений и помечать Key тем же типом, используя метод member. Если результат member равен Some(k), то пользователь должен иметь возможность вызвать get(k) и получить V вместо Option[V].

import scala.collection.{Map => M}

class Key[PH, K] private (val k: K) extends AnyVal

object Key {
  def apply[PH, K](k: K): Key[PH, K] = new Key(k)
}

class Map[PH, K, V] private (val m: M[K, V]) extends AnyVal {
  def member(k: K): Option[Key[PH, K]] = m.get(k).map(_ => Key(k))

  def get(key: Key[PH, K]): V = m.get(key.k).get
}

object Map {
  def apply[PH, K, V](m: M[K, V]): Map[PH, K, V] = new Map(m)
}

def withMap[K, V, T](m: M[K, V])(cont: (Map[PH, K, V] forSome { type PH }) => T): T = cont(Map(m))

withMap(M("a" -> "a")){ m =>
  m.member("a") match {
    case Some(v) => println(m.get(v))
    case None => println(":(")
  }
}

Но в настоящее время он не может скомпилироваться с следующая ошибка:

found   : Key[PH(in value $anonfun),String] where type +PH(in value $anonfun)
required: Key[PH(in value cont),String]
    case Some(v) => println(m.get(v))

Как мне убедить scalac, что PH одинаковы?

Ответы [ 2 ]

2 голосов
/ 24 февраля 2020

Уничтожить экзистенциальное:

withMap(M("a" -> "a")) { case m =>
  m.member("a") match {
    case Some(v) => println(m.get(v))
    case None => println(":(")
  }
}

Это сокращает

withMap(M("a" -> "a")) { case m: Map[ph, String, String] => // name the phantom type ph, name the map m =>
  m.member("a") match {
    case Some(v) => println(m.get(v))
    case None => println(":(")
  }
}

Деструктуризация позволяет дать m несуществующий тип в терминах вновь введенной переменной типа. Это означает, что каждое вхождение m теперь может иметь один и тот же тип.

1 голос
/ 25 февраля 2020

Призрачные типы бессмысленны. Вы должны сказать, что вы имеете в виду: каждый Key принадлежит определенному Map:

import scala.collection.immutable.Map // it is not safe to use Maps in general!

class KeyedMap[K, V](val m: Map[K, V]) extends AnyVal {
  import KeyedMap._
  def member(k: K): Option[Key[K, V, m.type]] = m.get(k).map { _ => new Key[K, V, m.type](k) }
  def fromKey(k: Key[K, V, m.type]): V = m(k.k)
}
object KeyedMap {
  //                              vvvvvvvvvvvvvv requires this parameter to be <something>.type
  class Key[K, +V, M <: Map[K, V] with Singleton] private[KeyedMap](val k: K) extends AnyVal
}

object Test {
  def main(args: String*): Unit = {
    val m = new KeyedMap(Map("a" -> "b"))
    m.member("a") match {
      case Some(v) => println(m.fromKey(v))
      case None => println(":(")
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...