Scala,类型类和“找不到隐含的价值”

时间:2013-12-08 04:25:22

标签: scala typeclass

我在下面的类型类中遇到了一些奇怪的问题:由于某种原因,隐式对象 ContentUploader 在调用DemoActor上传方法时无法解决。

import akka.actor.Actor
import java.io.File
import org.slf4j.LoggerFactory

class DemoActor extends Actor {

  import DemoActor.UploaderImpl._

  override def receive = {
    case (x: DemoActor.Content) =>
      DemoActor.upload(x)
  }

}

object DemoActor {

  val LOG = LoggerFactory.getLogger("DemoActor")

  sealed trait UploadData {
    val data: Array[File]
  }

  case class Content(data: Array[File]) extends UploadData

  case class UploadResult(url: String, contentType: String, size: Long)

  trait S3Uploader[T <: UploadData] {

    def uploadToS3(filez: Array[File]): Iterable[UploadResult]

  }

  object UploaderImpl {

    val LOG = LoggerFactory.getLogger("Uploader")

    private def contentType(name: String): String = {
      "application/octet-stream"
    }

    private def doUpload(filez: Array[File], bucketName: String) = {
      LOG.debug("Uploading: {} to {}", filez, bucketName)
      filez.flatMap {
        case f =>
          try {
            val key = f.getName
            val mime = contentType(f.getName)
            Some(UploadResult("http://" + bucketName + ".s3.amazonaws.com/" + key, mime, f.length()))
          } catch {
            case e =>
              LOG.error("Can not upload", e)
              None
          }
      }
    }

    implicit object ContentUploader extends S3Uploader[Content] {

      lazy val bucketName = "resources.aws.bucketname"

      lazy val awsSecret = "resources.aws.secret.key"

      lazy val awsAccess = "resources.aws.access.key"

      override def uploadToS3(filez: Array[File]) = doUpload(filez, bucketName)

    }

  }

  def upload[T <: UploadData](src: T)(implicit uploader: S3Uploader[T]) = uploader.uploadToS3(src.data)


}

我在这里错过了什么?

UPD

如果我在对象 DemoActor中为DemoActor移动的定义,比如

import akka.actor.Actor
import java.io.File
import org.slf4j.LoggerFactory

object DemoActor {

  val LOG = LoggerFactory.getLogger("DemoActor")

  sealed trait UploadData {
    val data: Array[File]
  }

  case class Content(data: Array[File]) extends UploadData

  case class UploadResult(url: String, contentType: String, size: Long)

  trait S3Uploader[UploadData] {

    def uploadToS3(filez: Array[File]): Iterable[UploadResult]

  }

  object UploaderImpl {

    val LOG = LoggerFactory.getLogger("Uploader")

    private def contentType(name: String): String = {
      "application/octet-stream"
    }

    private def doUpload(filez: Array[File], bucketName: String) = {
      LOG.debug("Uploading: {} to {}", filez, bucketName)
      filez.flatMap {
        case f =>
          try {
            val key = f.getName
            val mime = contentType(f.getName)
            Some(UploadResult("http://" + bucketName + ".s3.amazonaws.com/" + key, mime, f.length()))
          } catch {
            case e =>
              LOG.error("Can not upload", e)
              None
          }
      }
    }

    implicit object ContentUploader extends S3Uploader[DemoActor.Content] {

      lazy val bucketName = "resources.aws.bucketname"

      lazy val awsSecret = "resources.aws.secret.key"

      lazy val awsAccess = "resources.aws.access.key"

      override def uploadToS3(filez: Array[File]) = doUpload(filez, bucketName)

    }

  }

  def upload[T <: UploadData](src: T)(implicit uploader: S3Uploader[T]) = uploader.uploadToS3(src.data)

  class DemoActor extends Actor {

    import DemoActor.UploaderImpl._

    override def receive = {
      case (x: DemoActor.Content) =>
        DemoActor.upload(x)
    }

  }


}
然后一切顺利。 namespacing是否存在一些问题?

1 个答案:

答案 0 :(得分:5)

找不到它,因为必须明确键入隐式前向引用才能被考虑,而这个引用不是。

如果这令人困惑,可能有两种方法可以解决这个问题。首先,您可以声明隐式的类型。从对象中删除implicit,并声明指向它的val

implicit val contentUploader: S3Uploader[DemoActor.Content] = ContentUploader

第二种方法是将class DemoActor声明移到文件的末尾,因此它保留在object DemoActor声明之后。

它的工作原理是编译器必须在完全键入文件的其余部分之前搜索隐式,因此它当时不知道对象ContentUploader是否满足搜索。