【问题标题】:Scala: Is there a way where type aliases can be treated as distinct from the type that they alias?Scala:有没有一种方法可以将类型别名与它们别名的类型区别对待?
【发布时间】:2018-10-03 17:54:33
【问题描述】:

给定以下示例:我想截断字符串以满足某些长度限制,例如与 SQL 类型的兼容性。

type varchar8 = String

implicit def str2Varchar8(str: String): varchar8 = str.take(8)

val a: varchar8 = "abcdefghi"

// wanted: "abcdefgh", actual result:
a: varchar8 = abcdefghi

编译器似乎没有区分这两种类型。

给定一个类型别名type A = String,我想要实现的是:

  1. 避免运行时分配(即包装类)
  2. 仅在从String 映射到类型别名A 时应用断言/转换的能力。即直接使用类型别名 A 作为输入时避免进一步的断言/转换

验证示例:

type NotNullA = A

def method(a: A) = if(a != null)
    _method(a: NotNullA) // explicit typing
  else
    ???

// "a" at runtime is a String but we consider it validated, instead relying on the type system
protected def _method(a: NotNullA) = ???
protected def _otherMethod(a: NotNullA) = ???

有没有一种方法可以将类型别名与它们别名的类型分开处理 - 从而使它们之间的隐式转换和类型检查成为可能?是否有其他一些编码/技术可以完成这项工作?

Side:我似乎记得两个 是分开的,并且类型和别名是不同的(与类型数量问题无关)。我之前的代码是这样的:

type FieldAType = Int

// and in a different class
def method(a: FieldAType) = ???

val b: FieldAType = 1
method(b) // worked

val c: Int = 1
method(c) // compiler error
method(c: FieldAType) // worked

但是,我无法重现此问题(可能是由于 Scala 版本较旧 - 目前使用的是 2.11.8)

【问题讨论】:

    标签: scala casting implicit-conversion typechecking


    【解决方案1】:

    我建议你看看softwaremill.scala-common.tagging library。

    • 无运行时开销

    • 保护类型的可靠方法

    只需添加导入并定义您的标记类型:

    import com.softwaremill.tagging._
    
    type EvenTag
    type EvenInt = Int @@ EvenTag
    
    object EvenInt {
      def fromInt(i: Int): Option[EvenInt] =
        if (i % 2 == 0) Some(i.taggedWith[EvenTag]) else None
    }
    
    def printEvenInt(evenInt: EvenInt): Unit = println(evenInt)
    
    EvenInt.fromInt(2).foreach(printEvenInt)
    
    val evenInt: EvenInt = 2 // Doesn't compile
    printEvenInt(2) // Doesn't compile
    

    我们如何破解它?

    val evenInt: EvenInt = 1.taggedWith[EvenTag]
    

    享受吧!

    【讨论】:

    • 我设想在受保护代码中使用的用例,并确保没有类型错误/错误。似乎它也可以支持多态性:“当 U 是 V (U <: v t x: u shapeless scalaz>
    【解决方案2】:

    据我所知,这是不可能的。别名就是这样,一个附加名称。纯粹是为了可读性。

    但是,您可以使用 value classes 执行此操作。它们是完全不同的类型,因此您可以在代码中以不同方式处理它们。但大多数情况下,编译器能够避免实际分配包装对象 - 链接页面有更多关于例外情况的信息。

    【讨论】:

    • 有很多例外和限制,但我喜欢简单 :) 类型安全、转换支持等可能会胜过潜在的对象分配成本。
    【解决方案3】:

    有一个功能可能会在 Scala 3 中实现:opaque types。

    它们确实解决了您描述的问题:能够根据名称而不是其真正的底层类型来区分类型别名和普通类型。

    看看the official proposal

    【讨论】:

      猜你喜欢
      • 2020-03-29
      • 1970-01-01
      • 2014-10-06
      • 2013-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多