Scala 使用“隐式定义”生成Typeclass实例?

Scala 使用“隐式定义”生成Typeclass实例?,scala,typeclass,Scala,Typeclass,鉴于: 然后是一个隐含的: scala> trait Resource[A] { def f: String } defined trait Resource scala> case class Foo(x: String) defined class Foo 以下工作: scala> implicit def fooToResource(foo: Foo): Resource[Foo] = new Resource[Foo] { def f = foo.

鉴于:

然后是一个隐含的:

scala> trait Resource[A] { def f: String }
defined trait Resource

scala> case class Foo(x: String)
defined class Foo
以下工作:

scala> implicit def fooToResource(foo: Foo): Resource[Foo] = 
        new Resource[Foo] { def f = foo.x }
我定义了一个函数:

scala> implicitly[Resource[Foo]](Foo("foo")).f
res2: String = foo
但是,以下代码无法编译:

scala> def f[A](x: A)(implicit ev: Resource[A]): String = ev.f
f: [A](x: A)(implicit ev: Resource[A])String
scala>f(Foo(“Foo”))
:17:错误:找不到参数ev:Resource[Foo]的隐式值
f(Foo(“Foo”))
其次,我试着:

scala> f(Foo("foo"))
<console>:17: error: could not find implicit value for parameter ev: Resource[Foo]
       f(Foo("foo"))
scala>f2(Foo(“bippy”))
:17:错误:找不到参数ev:Resource[Foo]的隐式值
f2(Foo(“bippy”))
^
最后,我尝试:

scala> f2(Foo("bippy"))
<console>:17: error: could not find implicit value for parameter ev: Resource[Foo]
       f2(Foo("bippy"))
        ^
scala>def g(foo:foo)(隐式ev:Resource[foo]):String=ev.f
g:(foo:foo)(隐式ev:Resource[foo])字符串
scala>g(Foo(“5”))
:17:错误:找不到参数ev:Resource[Foo]的隐式值
g(Foo(“5”))
^

然而,它也失败了。如何修复
f

隐式def fooToResource不是类型类实例,但如果提供
Foo
,则会返回一个,这就是以下行工作的原因:

scala> def g(foo: Foo)(implicit ev: Resource[Foo]): String = ev.f
g: (foo: Foo)(implicit ev: Resource[Foo])String

scala> g(Foo("5"))
<console>:17: error: could not find implicit value for parameter ev: Resource[Foo]
       g(Foo("5"))
        ^
解决方案是将
Resource.f
函数更改为采用
A
类型的参数:

implicitly[Resource[Foo]](Foo("foo")).f
然后,您可以为
Foo
定义
Resource
类型类实例,如下所示:

trait Resource[A] { 
  def f(a: A): String 
}
我们可以重写
f
以使用更改的
资源

case class Foo(x: String)

implicit val fooResource = new Resource[Foo] {
  def f(foo: Foo) = foo.x
}
你(我认为)需要什么:

def f[A](a: A)(implicit resA: Resource[A]): String = resA.f(a)

implicit def fooToResource
不是类型类实例,但如果提供
Foo
,则会返回一个类型类实例,这就是以下行起作用的原因:

scala> def g(foo: Foo)(implicit ev: Resource[Foo]): String = ev.f
g: (foo: Foo)(implicit ev: Resource[Foo])String

scala> g(Foo("5"))
<console>:17: error: could not find implicit value for parameter ev: Resource[Foo]
       g(Foo("5"))
        ^
解决方案是将
Resource.f
函数更改为采用
A
类型的参数:

implicitly[Resource[Foo]](Foo("foo")).f
然后,您可以为
Foo
定义
Resource
类型类实例,如下所示:

trait Resource[A] { 
  def f(a: A): String 
}
我们可以重写
f
以使用更改的
资源

case class Foo(x: String)

implicit val fooResource = new Resource[Foo] {
  def f(foo: Foo) = foo.x
}
你(我认为)需要什么:

def f[A](a: A)(implicit resA: Resource[A]): String = resA.f(a)

好的,Peter Neyens的回答是,这不是一个类型类,这是一个隐式转换,您应该避免-应该有一些警告,要求您导入scala.language.implicitConversions

作为补充,以下是第一个
隐式运行的原因:

隐含的意思是:

f(Foo("hello world")) // String = hello world
当您在不提供参数的情况下隐式编写
时,它将在作用域中查找类型为
T
的隐式并返回它。但是,您使用一个参数隐式调用(我相信永远没有合法的理由这样做),因此它只会返回您的参数,
Foo(“Foo”)
,一个
Foo
的实例。除非您明确声明
T
应该是资源[Foo]。如果您编写了一个类型归属,例如
(Foo(“Foo”):Resource[Foo])
,那么它的工作方式也是一样的<代码>隐式地
在这里不相关

关键是
Foo(“Foo”)
不是预期的
Resource[Foo]
,而是一个
Foo
。编译器会拒绝这种情况,只是此时,您上面定义的隐式转换开始生效,并且您的
Foo
实例被转换为
资源[Foo]
。然后,您可以调用
f


接下来,您可以调用
f(Foo(“Foo”)
。有一个隐式参数,但是这次您不提供它。因此编译器寻找一个实例(虽然它第一次没有这样做),但由于没有这样的实例,所以失败了

同意Peter Neyens的回答,这不是一个类型类,这是一个隐式转换,应该避免-应该有一些警告,要求您导入scala.language.implicitConversions

作为补充,以下是第一个
隐式运行的原因:

隐含的意思是:

f(Foo("hello world")) // String = hello world
当您在不提供参数的情况下隐式编写
时,它将在作用域中查找类型为
T
的隐式并返回它。但是,您使用一个参数隐式调用(我相信永远没有合法的理由这样做),因此它只会返回您的参数,
Foo(“Foo”)
,一个
Foo
的实例。除非您明确声明
T
应该是资源[Foo]。如果您编写了一个类型归属,例如
(Foo(“Foo”):Resource[Foo])
,那么它的工作方式也是一样的<代码>隐式地
在这里不相关

关键是
Foo(“Foo”)
不是预期的
Resource[Foo]
,而是一个
Foo
。编译器会拒绝这种情况,只是此时,您上面定义的隐式转换开始生效,并且您的
Foo
实例被转换为
资源[Foo]
。然后,您可以调用
f


接下来,您可以调用
f(Foo(“Foo”)
。有一个隐式参数,但是这次您不提供它。因此编译器寻找一个实例(虽然它第一次没有这样做),但由于没有这样的实例,所以失败了

这个关于
的额外信息实际上是对我的回答的一个很好的补充,谢谢@Didier Dupont.谢谢。一开始对我来说没有任何意义,所以我认为它可能值得分享
我相信没有合法的理由这么做,曾经
没有理由做什么?请您详细说明一下好吗?@KevinMeredith没有理由使用显式参数隐式调用
隐式调用[Resource[Foo]](Foo(“Foo”)
@AlexeyRomanov-下面的伪代码喷射json示例如何:
json->String
def jsonToString[A:RootJsonFormat](x:A):String=隐式[RootJsonFormat[A]]。write(x).toString
?这不是惯用语吗?这个关于
的额外信息实际上是对我的回答的一个很好的补充,谢谢@Didier Dupont.谢谢。一开始对我来说没有任何意义,所以我认为它可能值得分享
我相信没有合法的理由这么做,曾经
没有理由做什么?你能详细说明一下吗?@KevinM