【问题标题】:Scala: how to create a type parametrized by a value, instead of a typeScala:如何创建由值参数化的类型,而不是类型
【发布时间】:2011-08-23 01:39:22
【问题描述】:

是否可以在 Scala 中创建由值参数化的类型?我不想定义一个部分类型List[A] 并使用List[Int] 对其进行参数化,而是定义一个部分类型(在伪代码中)Element[Symbol] 并使用Element['td] 对其进行参数化。在本例中,该类型表示一个 XML 元素:Element['td] 通知 Scala 类型检查器我们有一个 <td> 元素,您可以想象可以有专门期望或返回 <td> 元素的 API。

(子类化,如class Td extends Element,不是一个非常令人满意的解决方案,因为它不会阻止可能由独立开发人员编写的两段代码为<td>声明不同的子类,然后将考虑由 Scala 类型检查器确定为不同的类型。)

【问题讨论】:

  • 我认为这不可能。
  • 可能使用了很多内部类和路径依赖类型?

标签: scala


【解决方案1】:

如果你真的想通过一个值来参数化一个类型,你需要一种依赖类型的编程语言,比如 Agda。

【讨论】:

  • Scala 是一种依赖类型的编程语言。即使它不是单例类型也能让你足够接近:因为值可以提升为类型,我们可以根据值的相应单例类型将依赖于值的类型编码为类型。
  • @Miles Sabin:虽然这在技术上可能是正确的,但我不明白您将如何创建一个长度为 Int 参数的列表类型。有什么想法吗?
  • Ints 的单例类型存在于 Scala 类型检查器内部,但目前没有任何语法(有关补丁和讨论,请参阅 existentialtype.net/2008/07/21/literally-dependent-types)。但是对于任何引用类型,这都是完全可能的......请参阅下面的答案。
  • 在类型系统中拥有这些类型并不意味着您可以确保,例如,列表的长度方法确实会通过类型系统返回长度。只要这些微不足道的事情是不可能的,调用 Scala 依赖类型就有点用词不当恕我直言。
  • 引用您链接到的文章:“不是成熟的依赖类型”
【解决方案2】:

只是一些可以玩的想法...对象是值并且它们具有类型。

trait XmlElement
object Td extends XmlElement

然后,您可以拥有一个参数化类并在该特定类型上使用它。

class Element[T <: XmlElement] { ... }
val elementOnTd = new Element[Td.type]
// elementOnTd can only be used with Td.

如果您只想支持固定数量的元素,那么您可以创建一个密封的特征,并且您的库将仅适用于这些对象(尽管这似乎非常有限)

sealed trait XmlElement
object Td extends XmlElement
object Tr extends XmlElement
// can't have anything other than `Td` and `Tr` !

你可以玩的另一件事是对象的内部类没有相同的类型。因此,您也可以按照object Td { class Element { ... } } 和Td.Element 的类型与Tr.Element 的类型不同。

【讨论】:

    【解决方案3】:

    听起来你真正想要的不是通过一个值来参数化一个类型(就像你在 C++ 中那样),而是迫使不同的开发人员就一个通用的实现达成一致。

    我认为该语言不会为您解决这个问题。即使您强制只有一种方法来拥有 &lt;td&gt; 类型,是否仍然可以自行决定 &lt;td&gt; 类型到底是什么?

    如果你真的想通过一个值来参数化一个类型,你可能不得不这样做:

    object TD {
    }
    

    即让它成为一个类型,现在你可以写Element[TD]。虽然这仍然存在其他人可以在另一个包中写入object TD { } 并且会有两个的问题。

    您可以尝试使用类似的东西来模拟完全依赖类型

    object A { }
    object B { }
    object C { }
    ...
    
    object TyString[Car,Cdr] { }
    

    因此“TD”将表示为

    TyString[T,TyString[D,()]]
    

    但你可能不想去那里;)

    (我听说 Scala 将实现一种称为“单例文字”的东西,但那曾经发生过吗?你应该能够编写 "td".type,但 Scala 2.9.1 不接受这种语法)。

    【讨论】:

    • 关于您的上一条评论,以下对我有用:val x = "string"; null: x.type。但是如果x 被声明为var 而不是val,编译器会给出一个错误,即x 不是一个稳定的标识符。
    • 非常有趣。我可以看到一种我们可以使用单例类型的方式。但我很困惑:如果我可以写val s = 's; val x: s.type = s 为什么我不能写val s = 's; val x: s.type = 's(这会导致类型不匹配;找到:符号,必填:s.type)?
    【解决方案4】:

    单例类型将满足您的要求,

    scala> class Element[+T]
    defined class Element
    
    scala> val td = 'td
    td: Symbol = 'td
    
    scala> val p = 'p
    p: Symbol = 'p
    
    scala> def acceptAll[T](e : Element[T]) = e
    acceptAll: [T](e: Element[T])Element[T]
    
    scala> def acceptTd(e : Element[td.type]) = e
    acceptTd: (e: Element[td.type])Element[td.type]
    
    scala> acceptAll(new Element[p.type])       // OK
    res3: Element[Symbol] = Element@628b54f4
    
    scala> acceptAll(new Element[td.type])      // OK
    res4: Element[Symbol] = Element@6e7eee36
    
    scala> acceptTd(new Element[td.type])       // OK
    res2: Element[td.type] = Element@547fa706
    
    scala> acceptTd(new Element[p.type])        // Doesn't compile
    <console>:12: error: type mismatch;
     found   : Element[p.type]
     required: Element[td.type]
                  acceptTd(new Element[p.type])
                           ^
    

    但是,通过创建(密封的)类型族来表示元素名称,它们并没有为您提供任何您无法拥有的东西。请注意,这里使用 Scala Symbols 并没有什么特别之处:引用类型的任何稳定标识符都会产生唯一的单例类型,可以以相同的方式使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-05
      • 2011-02-19
      • 2010-10-06
      相关资源
      最近更新 更多