【问题标题】:Scala how to let scala commit to the specified type boundsScala如何让scala提交到指定的类型边界
【发布时间】:2016-04-20 14:12:00
【问题描述】:

我创建了一个类型层次结构S_3 <: S_2 <: S_1 <: S_0 <: Selector,我想用它从数据结构中选择一个字段,同时保证用户不会要求一个不存在的字段。

例如如果_1, _2 and _3S_1, S_2 and S_3 类型的构造函数,那么我的意图如下:

 val x : (Int, String) = .. 
 x(_0) : TypeError 
 x(_1) : Int 
 x(_2) : String 
 x(_3) :  TypeError!

我的第一种方法是将类型限制如下:

  implicit class Bla2[+A,+B](val x : (A,B)) {
         def apply[N >: S_2 <: S_1](i : N) : Any = {
           i match {
              case 1 => x._1 
              case 2  => x._2 
          }
    }

其中关键信息是:N &gt;: S_2 &lt;: S_1。我希望 scala 会从提供的值x: N 推断出该类型是否允许。然而,这并没有发生,确切地说。

如果我编译以下内容(其中 _3 是 S_3 的构造函数):

 val x : (Int, String) = (1, "hello")
 x(_3) 

它会愉快地编译。

对于 _0 它工作正常。

我可以看到这样做的原因。因为 S_3 <: s_2 scala>

对于 S_0,它可以工作。因为 S_0 >: S_2 <: s_1 s_0>

我需要一种方法来强制 scala 不泛化类型并使其尽可能具体。我怎样才能做到这一点?

我知道可以通过使用类型编码的 Naturals 或其他结构更丰富的类型来实现,但我希望代码尽可能简单。我只需要一个订单。

完整代码如下(整个元组只是为了测试目的):

object Main extends App {
      sealed trait Selector {
           def unapply() : Int  
      }
      sealed class S_0 extends Selector {
           def unapply() : Int = 0
      }
      sealed class S_1 extends S_0 {
          override def unapply() : Int = 1 
      }
      sealed class S_2 extends S_1 {
          override def unapply() : Int = 2 
      }
      sealed class S_3 extends S_2 {
          override def unapply() : Int = 3 
      }

     def _0 = new S_0
     def _1 = new S_1
     def _2 = new S_2
     def _3 = new S_3


    implicit class Bla2[+A,+B](val x : (A,B)) {
         def apply[N >: S_2 <: S_1](i : N) : Any = {
           i match {
              case 1 => x._1 
              case 2  => x._2 
          }
    }
 }

 println("hello world")
 val b = (1,2)
 b[S_1](_1)
 b[S_2](_2)
 //  this won't compile 
 //   b[S_3](_3)
 //  but this will
 b(_3)
 }

编辑:问题在于类型的推断。如果类型被显式指定为:b[S_3](_3),它将不会编译。

【问题讨论】:

  • 也许,使用 shapeless 更容易Nat 类型:stackoverflow.com/questions/28287612/…
  • @dk14 确实有效,但我很好奇是否有办法强制类型推断更具体。如果手动指定类型b[S_3](_3),编译器会正确拒绝该语句。有时你不想仅仅为了一个特性而引入整个库。

标签: scala types


【解决方案1】:

您可以使用隐式 + 逆变来“更多/小于或等于”:

trait _4
trait _3 extends _4
trait _2 extends _3
trait _1 extends _2
trait _0 extends _1

trait >:>[A, -B] //more-than or equals

object Laws {
  implicit def mte[A] = new >:>[A,A]{}//00, 11, 22, 33...contravariance will also add 23, 24, 34 etc. 
}


import Laws._

//the type here is going to be bound (inclusive) between 3 and 2
def a[T <: _3](a: T)(implicit ev: T >:> _2) = a

结果:

scala> a(new _2{})
res29: _2 = $anon$1@13a37e2a

scala> a(new _3{})
res26: _3 = $anon$1@779dfe55

scala> a(new _4{})
<console>:27: error: inferred type arguments [_4] do not conform to method a's type parameter bounds [T <: _3]
              a(new _4{})
              ^
<console>:27: error: type mismatch;
 found   : _4
 required: T
              a(new _4{})
                ^
<console>:27: error: could not find implicit value for parameter ev: >:>[T,_2]
              a(new _4{})
               ^

scala> a(new _1{})
<console>:27: error: could not find implicit value for parameter ev: >:>[_1,_2]
              a(new _1{})
               ^

或者,您可以只使用 Shapeless:How to require typesafe constant-size array in scala?

更新

这是针对相反类型层次结构的方法:

trait _0
trait _1 extends _0
trait _2 extends _1
trait _3 extends _2
trait _4 extends _3
trait _5 extends _4

import annotation.implicitNotFound

@implicitNotFound("Can’t prove ${A} <= ${B}")
trait <=[A, -B] //less-than or equals

object Laws {

  @implicitNotFound("Can’t prove ${A} >= ${B}")
  type >=[A,B] = <:<[A,B]

  implicit def lte[A] = new <=[A,A]{}//00, 11, 22, 33...contravariance will also add 23, 24, 34 etc. 
}


import Laws._
def a[T](a: T)(implicit ev1: T >= _2, ev2: T <= _3) = a

【讨论】:

  • 这是一个巧妙的技巧。 MTE 几乎与 Leib 具有相同的类型,但由于子类型化,它将允许类型推断类似于大于或等于。谢谢!唯一遗憾的是,该类型实际上是封闭的。我们不能高于 _4 的数字,但为此我最好输入编码的 Naturals。
  • @EdgarKlerks 我们实际上可以 - 查看我的更新。很好,不是吗?
  • 顺便说一句,Leibniz-like [+A, -B](在 A 上有额外的协方差)也可以工作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-14
  • 2018-08-18
  • 1970-01-01
  • 2013-12-25
  • 2021-01-24
  • 2020-07-22
  • 2013-05-31
相关资源
最近更新 更多