【问题标题】:using custom scala types in List在 List 中使用自定义 scala 类型
【发布时间】:2012-10-09 10:11:18
【问题描述】:

我希望创建一个表示数据大小(字节、KB...)的类型族。为此,我们的想法是构建一个基本类型,使其具有基于以下内容的实际尺寸:

  type SizeUnit = Int
  type B = SizeUnit
  type KB = SizeUnit
  type MB = SizeUnit
  type GB = SizeUnit
  type TB = SizeUnit
  type PB = SizeUnit
  type EB = SizeUnit
  type ZB = SizeUnit
  type YB = SizeUnit

有一个有序的列表:

val sizes = List(B, KB, MB, GB, TB, PB, EX, ZB, TB)

并且有一个转换方法,它接受一个目标类型,找到它们之间的索引差异,并乘以 1024 的差异幂。所以:

def convertTo(targetType: SizeUnit): SizeUnit ={
  def power(itr: Int): Int = {
    if (itr == 0) 1
    else 1024*power(itr-1)
  }

  val distance = sizes.indexOf(targetType) - sizes.indexOf(this)
  distance match {
    //same type - same value
    case 0 => targetType
    //positive distance means larget unit - smaller number
    case x>0 => targetType / power(distance)
    //negative distance means smaller unit - larger number and take care of negitivity 
    case x<0 => targetType * power(distance) * (-1)
  }  
}

在我检查方法的有效性之前,我遇到了一些问题(因为我是 Scala 新手):

  • 有没有办法创建一个包含类型而不是值的列表(或任何其他序列)?或者更确切地说 - 类型作为值?
  • 如果我理解正确,类型不会超出编译范围。这是否意味着在运行时,如果我将 GB 值传递给现有 KB,它就无法破译类型?

谢谢你, 埃胡德

【问题讨论】:

    标签: scala custom-type typelist


    【解决方案1】:

    所有这些类型都只是类型别名,而不是独立类型。

    scala> type SizeUnit = Int
    defined type alias SizeUnit
    
    scala> type B = SizeUnit
    defined type alias B
    
    scala> type KB = SizeUnit
    defined type alias KB
    
    scala> (3 : KB) == (3 : B)
    res0: Boolean = true
    

    类型别名只是同一类型的不同名称。所以即使你能写出来,你的清单也相当于写了:

    val sizes = List(Int, Int, Int, Int, Int, Int, Int, Int, Int)
    

    同样,您永远不能使用这些类型来编写一个需要以 MB 为单位接受数量的函数,因为所有这些类型都是相同的。

    要将 B、KB、MB 等分离为不同“种类”的整数,您需要它们是 Int 的子类型,而不是 Int 的类型别名。但是Int 是最终类型,所以无论如何您都不能对其进行子类型化。

    一个更好的方法是让Int 代表一个原始数字,而不是实现一个代表Int 的类型一个单元。您可以采取多种方法,但我会这样做:

    abstract class SizeUnit
    
    case object B extends SizeUnit
    case object KB extends SizeUnit
    case object MB extends SizeUnit
    
    
    case class Storage(num : Int, unit : SizeUnit)
    

    现在 3 MB 是 Storage(3, MB),而 17 字节是 Storage(17, B)。您可以在任意整数和Storage 数量之间进行很好的静态强制分离,并且只要您有Storage 数量,您就始终将单位作为数据对象(无需能够静态推断它)。您可以将对象 BKBMB 等放在一个列表中,然后对它们进行任何操作。

    或者,您可以让单元对象本身包含一些关于它们的顺序或它们之间的比率的信息,而不是将这些信息存储在外部列表中。

    您甚至可以使用此方案通过隐式转换来做一些古怪的事情。突然想到这样的事情:

    object SizeableInt {
        // since I want to give the conversion methods the same name as the
        // units, I need different names to refer to the units in the
        // implementation of those methods. If B, KB, etc were defined in
        // a different qualified namespace, this wouldn't be necessary.
        private val _B = B
        private val _KB = KB
        private val _MB = MB
    
        case class SizeableInt(x : Int) {
            def B : Storage = Storage(x, _B)
            def KB : Storage = Storage(x, _KB)
            def MB : Storage = Storage(x, _MB)
        }
    
        implicit def makeSizeableInt(x : Int) : SizeableInt = SizeableInt(x)
    }
    

    这样,一旦你导入了隐式,你就可以简单地写4 MB123456789 B之类的东西,而不是Storage(4, MB)Storage(123456789, B)

    【讨论】:

    • 这些是好主意,但它们并没有解决我的主要问题,即如何在不硬编码每个需要的功率的情况下进行转换,例如 def toB(storage: Storage) storage.unit match { case KB => storage.num * 1024....
    • @EhudKaldor 如果BKBMB等是对象,而不是类型,你可以对它们做任何事情.例如,您可以构造列表List(B, KB, MB, ...),并拥有一个函数convert(quantity : Storage, unit : SizeUnit) : Storage,它使用所涉及的两个SizeUnit 值的列表中的位置来计算它们之间的比率。
    【解决方案2】:

    在 Scala 中类型不是值。

    您可能应该查看singleton objects 的用例。

    【讨论】:

      猜你喜欢
      • 2021-06-22
      • 2012-04-08
      • 2013-03-06
      • 2020-10-02
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多