简体   繁体   English

Scala,typeclass和“找不到隐式值”

[英]Scala, typeclass and “could not find implicit value”

I am facing some weird problem with typeclass below: for some reason implicit object ContentUploader is not resolved on call to upload method of DemoActor . 遇到以下类型类奇怪的问题:由于某种原因,在调用DemoActor 上载方法时未解析隐式对象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)


}

What have I missed here? 我在这里错过了什么?

UPD UPD

if I move definition of class for DemoActor inside object DemoActor, like 如果我在对象 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)
    }

  }


}

then everything works well. 然后一切正常。 Are there some issues with namespacing? 命名空间是否存在一些问题?

It is not finding it because implicit forward references must be explicitly typed to be considered, and this one isn't. 找不到它是因为必须明确键入隐式前向引用才能被考虑,而这个不是。

If this is confusing, maybe two ways of fixing it might make it clear. 如果这令人困惑,则可能有两种解决方法可能很清楚。 First, you can declare the type of the implicit. 首先,您可以声明隐式的类型。 Remove the implicit from the object, and declare a val pointing to it: 从对象中删除implicit对象,并声明一个指向它的val

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

The second way is moving the class DemoActor declaration to the end of the file, so it stays after the the object DemoActor declaration. 第二种方法是将class DemoActor声明移动到文件的末尾,因此它将保留在object DemoActor声明之后。

The reason it works like this is that the compiler must search for the implicit before the rest of the file is fully typed, so it doesn't know, at that time, that object ContentUploader satisfy the search. 之所以这样工作,是因为编译器必须在文件的其余部分完全键入之前搜索隐式对象,因此当时尚不知道对象ContentUploader满足搜索条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM