【问题标题】:Scala try all typeclasses in scopeScala 尝试范围内的所有类型类
【发布时间】:2015-11-21 21:07:51
【问题描述】:

我在 scala 中有一个 typeclass 模式,我想要一个可以针对任何类型调用的方法,并根据范围内是否有适当的 typeclass 来返回一个选项。具体来说,我有:

trait Truthy[A] {
  def toBoolean(a:A):Boolean
  def unapply(a:Any):Option[Boolean]
}

object Truthy {
  implicit object IntTruthy extends Truthy[Int] {
    def toBoolean(i:Int) = i == 1
    def unapply(a:Any) = a match {
      case i:Int => Some(toBoolean(i))
      case _ => None
    }
  }
  implicit object BoolTruthy extends Truthy[Boolean] {
    def toBoolean(b:Boolean) = b
    def unapply(a:Any) = a match {
      case i:Boolean => Some(toBoolean(i))
      case _ => None
    }
  }
  implicit object StringTruthy extends Truthy[String] {
    def toBoolean(s:String) = Set("t", "true", "yes", "y").contains(s.toLowerCase)
    def unapply(a:Any) = a match {
      case i:String => Some(toBoolean(i))
      case _ => None
    }
  }

  def truthy[A](a:A)(implicit ev:Truthy[A]) = ev.toBoolean(a)
}

当我知道参数的类型时,truthy 方法很好,但是我正在使用来自缺少该类型信息的源的数据,所以我想要一个带有签名 Any => Option[Boolean] 的函数,它将尝试所有范围内的Truthy 实现,如果可以找到则返回结果。显然我可以写

def getTruth(a:Any) = a match {
  case IntTruthy(b) => Some(b)
  case BoolTruthy(b) => Some(b)
  case StringTruthy(b) => Some(b)
  case _ => None
} 

但这会破坏类型类“开放”的全部目的。

【问题讨论】:

    标签: scala types typeclass


    【解决方案1】:

    编译器确定有效隐式Truthy 的能力受到编译时可用信息的限制。这意味着,从您使用 Any 的那一刻起,它根本没有说明任何类型,您必须“手动”识别 A 类型,以便编译器可以找到您的 Truthy需要。 然后你的getTruth 看起来像:

    def getTruth(any: Any): Option[Boolean] = any match { 
         case i : Int => Some(truthy(i))
         case b : Boolean => Some(truthy(b))
          ...
         case _ => None
       } 
    

    【讨论】:

    • 你真的不需要在truthy[Int](i)中指定类型,这是推断出来的。
    • 对,一旦知道iInt,编译器就可以再次发挥他的魔力,将修复。谢谢!
    【解决方案2】:

    奇洛的回答是正确的。我要澄清一下,“尝试范围内的所有 Truthy 实现”的逻辑不存在。原则上,编译器不应该仅仅因为它在范围内就实例化每个可能的对象,除非它实际上被引用或以某种方式需要。因此,由于添加导入而在编译时“范围内”的内容在运行时根本不存在。这很容易测试:

    object Foo {
      println("initializing foo")
      def sayHello() = println("hello!")
    }
    
    object Test2 {
      def main(args: Array[String]): Unit = {
        val me = "Daniel"
        println(s"Hey, I'm $me")
        Foo.sayHello()
      }
    }
    

    打印:

    嘿,我是丹尼尔

    初始化 foo

    你好!

    如果您评论Foo.sayHello(),您将看到initializing foo 也会消失。该对象在运行时将不存在。

    在您的情况下,显式添加类型允许编译器静态定义将实例化和引用的隐式(如果有的话)。

    These java specs 也可能有助于澄清。

    【讨论】:

      猜你喜欢
      • 2017-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-30
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多