我想要一个A|B类型作为子类型A|B|C.这可能在Scala中编码吗?如果有,怎么样?
我希望我可以在implicitly[¬¬[IF] <:< T]下面进行编译(原始代码在这里),但事实并非如此.有没有办法修复此代码以允许子类型?
object NUnion{
type ¬¬[A] = ¬[¬[A]]
type ¬[A] = A => Nothing
trait Disj[T] {
type or[S] = Disj[T with ¬[S]]
type apply = ¬[T]
}
// for convenience
type disj[T] = { type or[S] = Disj[¬[T]]#or[S] }
type T = disj[Int]#or[Float]#or[String]#apply
type IF = disj[Int]#or[Float]#apply
implicitly[¬¬[Int] <:< T] // works
// implicitly[¬¬[Double] <:< T] // doesn't work
// implicitly[¬¬[IF] <:< T] // doesn't work - but it …Run Code Online (Sandbox Code Playgroud) 我正在遵循这个问题的接受答案中提出的技术 如何定义"类型分离"(联合类型)?为了支持对方法的多类型参数进行类型检查.
隐含的"证据"
@implicitNotFound(msg="Only String, Array[Byte] and InputStream are supported")
sealed class Input[T]
object Input{
implicit object ByteArrayWitness extends Input[Array[Byte]]
implicit object StringWitness extends Input[String]
implicit object InputStreamWitness extends Input[InputStream]
}
Run Code Online (Sandbox Code Playgroud)
API方法
def foo[T: Input](param: T) =
param match {
case x: String => //...
case x: Array[Byte] => //...
case x: InputStream => //...
case _ => throw new UnsupportedOperationException(s"not implemented for type ${param.getClass}")
}
Run Code Online (Sandbox Code Playgroud)
问题
这个编译
foo("test")
foo(Array[Byte](123.toByte))
Run Code Online (Sandbox Code Playgroud)
但这不是(因为它不具体InputStream)
foo(new ByteArrayInputStream("abc".getBytes("UTF-8")))
Run Code Online (Sandbox Code Playgroud)
我必须将其转换为确切的超类型以使其工作(此编译)
foo(new ByteArrayInputStream("abc".getBytes("UTF-8")).asInstanceOf[InputStream])
Run Code Online (Sandbox Code Playgroud)
有没有办法改变 …
是否可以根据Scala中的另一个清单定义清单?
我几乎不相信这是不可能的,因为Scala Manifest信息不是动态使用的.
这是问题所在.我有一个函数可以返回多种类型的对象(String,Int,List [Int],List [List [String]]等).为了支持这些多种类型,返回类型设置为Any,但是由于要键入擦除,列表,地图等支持的类型信息将丢失.为了恢复一些细节,我返回一个Manifest以及返回类型.
但是,返回的信息可以放在另一个列表或映射中,然后从另一个函数返回.我想更新清单,以包括类型现在是清单定义的先前类型的List或Map这一事实.
这是一些示例代码
def returnWithManifest[T: Manifest](x: T) = (x, manifest[T])
// May return String, Int, List[Int], List[List[String]], ...
def contrivedExample(t: String): (Any, Manifest[_]) = t match {
case "String" => returnWithManifest("test")
case "Int" => returnWithManifest(1)
case "Boolean" => returnWithManifest(true)
case "List[Int]" => returnWithManifest(List(1,2,3))
case "List[List[String]]" =>
returnWithManifest(List(List("a","b"),List("c","d")))
case _ => returnWithManifest(None)
}
scala> val v1 = contrivedExample("List[Int]")
v1: (Any, Manifest[_]) = (List(1, 2, 3),scala.collection.immutable.List[Int])
scala> val x = v1._1
x: Any = List(1, 2, …Run Code Online (Sandbox Code Playgroud) 接下来就如何在Scala中定义联合类型形成了这一组优秀的答案.我一直在使用Miles Sabin对Union类型的定义,但仍有一个问题.
如果直到运行时才知道类型,你如何使用这些?例如:
trait inv[-A] {}
type Or[A,B] = {
type check[X] = (inv[A] with inv[B]) <:< inv[X]
}
case class Foo[A : (Int Or String)#check](a: A)
Foo(1) // Foo[Int] = Foo(1)
Foo("hi") // Foo[String] = Foo(hi)
Foo(2.0) // Error!
Run Code Online (Sandbox Code Playgroud)
此示例有效,因为参数A在编译时已知,并且调用Foo(1)实际上正在调用Foo[Int](1).但是,如果A在运行时之前不知道参数,您会怎么做?也许你正在削减包含Foos 的数据的文件,在这种情况下,在Foo读取数据之前不知道类型参数.A在这种情况下,没有简单的方法来设置参数.
我能够提出的最佳解决方案是:
模式匹配您已阅读的数据,然后Foo根据该类型创建不同的数据.在我的情况下,这是不可行的,因为我的case-class实际上包含了几十个union类型,因此有数百种类型的组合来模式匹配.
转换您刚读过的类型(String or Int),因此您只能传递一种类型,在Foo使用它时传递Type Class约束.然后返回Foo[_].这使得Foo用户有责任计算出每个字段的类型(因为它们看起来都是Any类型),但至少在实际使用字段之前它必须知道类型,在这种情况下,模式匹配似乎更容易处理.
第二个解决方案如下所示:
def parseLine: Any // Parses …Run Code Online (Sandbox Code Playgroud) 我有一个带有 2 个类型参数的案例类
case class Relation[T <: Model, RT] (model: T)
Run Code Online (Sandbox Code Playgroud)
类型 T 显然是一种类属性“模型”。类型 RT 可以与 T 相同,也可以是 List[T](取决于我们创建 OneToOne 或 OneToMany 的关系类型)。那么我如何限制 RT,它不允许传递除 T 或 List[T] 之外的其他内容。
PS 我正在阅读有关covariance和contravariance 的内容,但不太了解。它适用于我的情况吗?如果是,请您举例说明。如果没有 - 那么请展示其他工具来达到它。我什至无法理解 T 和 List[T] 是如何相互关联的?T <: List[T] 或 List[T] <: T?
提前致谢
我正在尝试在Miles Sabin的博客文章中定义的Scala的联合类型:
http://www.chuusai.com/2011/06/09/scala-union-types-curry-howard/
并在中讨论过
对于那里定义的简单情况,它们工作正常,但我要做的是使用它们在Play Framework中创建一个只接受某些值(String,Boolean,Int,Undefined)的通用JSON解析器,然后成功传递它们.
这是我的代码:
type UpdateType = Option[String] |?| Option[Int] |?| Option[Boolean] |?| Option[List[Int]]
def withValue[T : (UpdateType)#?](request: Request[JsValue])(block: (String, T) => Future[SimpleResult]) = {
val field = request.body \ ("field")
val value = request.body \ ("value")
(field, value) match {
case (x: JsString, y: JsString) => block(x.value.toString, Some(y.value.toString))
case (x: JsString, y: JsNumber) => block(x.value.toString, Some(y.value.intValue))
case (x: JsString, y: JsBoolean) => block(x.value.toString, Some(y.value.booleanValue))
case (x: JsString, y: JsUndefined) => block(x.value.toString, None)
case _ => …Run Code Online (Sandbox Code Playgroud) 在 Scala 中是否有可能有一个联合类型的集合。有几个方法联合类型讨论这里最精彩的答案感觉最原生的,我有这样的事情:
sealed trait StringOrNumber[T]
object StringOrNumber {
implicit object IntWitness extends StringOrNumber[Int]
implicit object StringWitness extends StringOrNumber[String]
}
Run Code Online (Sandbox Code Playgroud)
但是当我尝试制作包含两者的地图时
val m: Map[String, Any] = Map("str" -> "hellp", "int" -> 32)
Run Code Online (Sandbox Code Playgroud)
scala 编译器将其视为 [String,Any] 的映射 有没有办法告诉 Scala 编译器这是一个映射 [String, StringOrNumber]
编辑:
我不认为使用上述方法可以创建字符串或联合的集合。我认为它需要是联合类型的另一种方法,因为上述类似于重载方法而不是类型系统中的真正联合类型
functional-programming scala discriminated-union scala-collections