首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Typeclass的模糊隐值

Typeclass的模糊隐值
EN

Stack Overflow用户
提问于 2018-07-15 21:15:40
回答 2查看 1.6K关注 0票数 4

我试图抽象出为特定类型触发的json解析逻辑。

我首先创建了一个Parser特性,如下所示:

代码语言:javascript
复制
trait Parser {
  def parse[T](payload : String) : Try[T]
}

我有一个名为JsonParser的特性的实现,即:

代码语言:javascript
复制
class JsonParser extends Parser {

  override def parse[T](payload: String): Try[T] = parseInternal(payload)

  private def parseInternal[T:JsonParserLike](payload:String):Try[T] = {
    implicitly[JsonParserLike[T]].parse(payload)
  }
} 

JsonParserLike的定义如下:

代码语言:javascript
复制
trait JsonParserLike[T] {
  def parse(payload: String): Try[T]
}

object JsonParserLike {
  implicit val type1Parser:JsonParserLike[Type1] = new JsonParserLike[Type1] 
  {
    //json parsing logic for Type1
  }

  implicit val type2Parser:JsonParserLike[Type2] = new JsonParserLike[Type2]
  {
     //json parsing logic for Type2
  }
}

当我尝试编译上面的内容时,编译失败的有:

代码语言:javascript
复制
ambiguous implicit values:
[error]  both value type1Parse in object JsonParserLike of type => parser.jsonutil.JsonParserLike[parser.models.Type1]
[error]  and value type2Parser in object JsonParserLike of type => parser.jsonutil.JsonParserLike[parser.models.Type2]
[error]  match expected type parser.jsonutil.JsonParserLike[T]
[error]   override def parse[T](payload: String): Try[T] = parseInternal(payload)

不知道为什么隐含的决议在这里失败了。是因为parse特性中的Parser方法没有参数T的参数

我尝试了另一种方法如下:

代码语言:javascript
复制
trait Parser {
  def parse[T](payload : String) : Try[T]
}

class JsonParser extends Parser {

  override def parse[T](payload: String): Try[T] = {
    import workflow.parser.JsonParserLike._
    parseInternal[T](payload)
  }

  private def parseInternal[U](payload:String)(implicit c:JsonParserLike[U]):Try[U] = {
     c.parse(payload)
  }
}

上面的内容给出了以下错误:

代码语言:javascript
复制
could not find implicit value for parameter c: parser.JsonParserLike[T]
[error]     parseInternal[T](payload)
[error]  

               ^

编辑:从REPL添加会话

代码语言:javascript
复制
scala> case class Type1(name: String)
defined class Type1

scala> case class Type2(name:String)
defined class Type2

scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.util.{Failure, Success, Try}

trait JsonParserLike[+T] {
  def parse(payload: String): Try[T]
}

object JsonParserLike {

  implicit val type1Parser:JsonParserLike[Type1] = new JsonParserLike[Type1] {

    override def parse(payload: String): Try[Type1] = Success(Type1("type1"))
  }

  implicit val type2Parser:JsonParserLike[Type2] = new JsonParserLike[Type2] {

    override def parse(payload: String): Try[Type2] = Success(Type2("type2"))
  }
}

// Exiting paste mode, now interpreting.

import scala.util.{Failure, Success, Try}
defined trait JsonParserLike
defined object JsonParserLike

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Parser {
  def parse[T](payload : String) : Try[T]
}

class JsonParser extends Parser {

  override def parse[T](payload: String): Try[T] = parseInternal(payload)

  private def parseInternal[T:JsonParserLike](payload:String):Try[T] = {
    implicitly[JsonParserLike[T]].parse(payload)
  }
}

// Exiting paste mode, now interpreting.

<pastie>:24: error: ambiguous implicit values:
 both value type1Parser in object JsonParserLike of type => JsonParserLike[Type1]
 and value type2Parser in object JsonParserLike of type => JsonParserLike[Type2]
 match expected type JsonParserLike[T]
  override def parse[T](payload: String): Try[T] = parseInternal(payload)
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-07-20 11:59:58

在这两个例子中,混合了不同类型的多态性似乎会带来额外的复杂性。下面是一个类型类的最小示例:

代码语言:javascript
复制
// type class itself
trait JsonParser[T] {
  def parse(payload: String): Try[T]
}

// type class instances
object JsonParserInstances {
  implicit val type1Parser: JsonParser[Type1] = new JsonParser[Type1] {
    def parse(payload: String): Try[Type1] = ???
  }

  implicit val type2Parser: JsonParser[Type2] = new JsonParser[Type2] {
    def parse(payload: String): Try[Type2] = ???
  }
}

// type class interface
object JsonInterface {
  def parse[T](payload: String)(implicit T: JsonParser[T]): Try[T] = {
    T.parse(payload)
  }
}

def main(args: Array[String]): Unit = {
  import JsonParserInstances._
  JsonInterface.parse[Type1]("3")
  JsonInterface.parse[Type2]("3")
}

更多信息:

票数 1
EN

Stack Overflow用户

发布于 2018-07-15 21:53:34

正如我在评论中已经解释过的,问题是方法

代码语言:javascript
复制
override def parse[T](payload: String): Try[T] = parseInternal(payload)

不接受任何JsonParserLike[T]实例。因此,编译器无法在调用站点(已知JsonParserLike[T]类型)插入正确的T实例。

要使其工作,必须向T的参数列表中添加某种唯一标识类型parse的令牌。一种粗糙的方法是添加一个JsonParserLike[T]本身:

代码语言:javascript
复制
import util.Try

trait Parser {
  def parse[T: JsonParserLike](payload : String) : Try[T]
}

class JsonParser extends Parser {

  override def parse[T: JsonParserLike](payload: String): Try[T] = 
    parseInternal(payload)

  private def parseInternal[T:JsonParserLike](payload:String):Try[T] = {
    implicitly[JsonParserLike[T]].parse(payload)
  }
} 

trait JsonParserLike[T] {
  def parse(payload: String): Try[T]
}

object JsonParserLike {
  implicit val type1Parser: JsonParserLike[String] = ???
  implicit val type2Parser: JsonParserLike[Int] = ???
}

现在它编译了,因为parseInternal所需的parseInternal是作为隐式参数自动插入到parse的。

这可能不是您想要的,因为它在Parser接口和JsonParserLike类型集之间创建了一个硬依赖关系。您可能希望从类似shapeless.Typeable的东西中获得一些灵感,以摆脱Parser接口中的JsonParserLike,或者立即依赖于切尔斯

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51352288

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档