简体   繁体   中英

Phantom existential types in Scala

I'm trying to write a value class that wraps Scala collection's Map and provides an alternative get . I'm trying to use a phantom type in the value class and tag the Key with the same type using the method member . If the result of member is Some(k) then the user should be able to call get(k) and get a V instead of an 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(":(")
  }
}

But currently it fails compiling with the following error:

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))

How can I convince scalac that the PH s are the same?

Destructure the existential:

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

This abbreviates

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(":(")
  }
}

The destructuring allows m to be given a non-existential type in terms of a newly introduced type variable. This means that every occurrence of m can now have the same type.

Phantom types are meaningless. You should say what you mean: every Key belongs to a certain 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(":(")
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM