2016-09-23 47 views
1

给出一个虚构的F型类:使用'隐def`

scala> trait F[A] {} 
defined trait F 

和这个定义,它采用了context bound到要求输入AF一个类型的类实例:

scala> def f[A : F](x: A) = ??? 
f: [A](x: A)(implicit evidence$1: F[A])Nothing 

我所限定的Person和类型类实例:

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

scala> implicit val person: F[Person] = new F[Person] {} 
person: F[Person] = [email protected] 

而下面的编译:

scala> f(Person("foo")) 
scala.NotImplementedError: an implementation is missing 

但是,没有String实现,因此它失败。

scala> f("foobar") 
<console>:17: error: could not find implicit value for evidence parameter of type F[String] 
     f("foobar") 
     ^

我然后使用定义的F[String]

scala> implicit def fInstance(x: String) = new F[String] {} 
fInstance: (x: String)F[String] 

但是,我不能运行:

scala> f("foobar") 
<console>:18: error: could not find implicit value for evidence parameter of type F[String] 
     f("foobar") 
     ^

,因为我没有一个隐含的F[String],而是String => F[String]

什么是使用这样的implicit def满足F[String]约束的正确方法,即用型的String成功地调用f功能?

我得到它的工作通过:

scala> implicit val x: F[String] = implicitly[String => F[String]].apply("foobar") 
x: F[String] = [email protected] 

scala> f("foobar") 
scala.NotImplementedError: an implementation is missing 
    at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230) 
    at .f(<console>:12) 
    ... 33 elided 

但我不知道这是否是正确的/干净的方式来做到这一点。

+0

如果不知道类型类型想要捕获什么样的信息和操作,这真的很难回答,但是给定这个特殊的'F',你可以用'Person'完全相同的方式定义一个'String'实例。实例。 –

+0

对于这种情况,如果提供'String',我只能构造'F [String]',即'F [String]'实例依赖于'String'参数。 –

+0

作为一项后续工作,在同伴类以外使用“隐式”,即孤立的'隐式'实例是否糟糕? –

回答

0

您的fInstance定义了隐式转换,即将String转换为F[String]的方法。用于生成类型类实例,接受隐式参数的方法,可以使用:

implicit def fInstance(implicit x: String) = new F[String] {} 

它在FP库通常用于导出来自另一个类型类:

implicit def optionMonoid[A](implicit S: Semigroup[A]): Monoid[Option[A]] = ??? 
// or, which is the same 
// implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = ??? 

的想法是,F[String]可以操作在一般的任何字符串中,不依赖于提供给函数的实际参数。当然,你可以随时提供实例明确:

f("foobar")(new F[String] { }) 

作为后续,类型类的重要组成部分是,你可以定义它们的ad-hoc,即不具有所有访问和StringF定义,你不得不将范围内的含义限制在Scala中并导入它们,所以它完全没问题。

+0

',你被迫将范围内的含义引入Scala并导入它们,所以它完全没问题。关于这个语句如何应用(或者不)孤儿'隐式的? –

+0

@KevinMeredith你不允许在源文件中简单地写一个'def'或'val'(如果它是应用程序代码而不是Scala脚本) - 它必须属于一个对象或类。所以你必须用'object implicits {implicit def fString:F [String] = ??? }',但是你不能在其他代码中使用它,除非你在使用之前在其中导入Implicits.fString'。 –

1

您定义了一个隐式转换。如果你想使用def来提供typeclass实例,你只需要编写相同的代码,就像你写一个隐式的val一样,但是用def代替val。

implicit def fInstance = new F[String] {} 

通常情况下,只有在需要类型参数的情况下才能使用def,就像这里一样。

implicit def fInstance[A] = new F[List[A]] {} 

或者

implicit def fInstance[A](implicit ev: F[A]) = new F[List[A]] {} 
0

这是你定义的简化版本(你可以从fInstance删除implicit):

implicit val singleFInstance: F[String] = fInstance("") // or fInstance("foobar"), etc. 

这是否是做正确的事,很很大程度上取决于Ff应该是什么意思。

但一般来说:如果F确实是一个类型级,fInstance(string)给出取决于字符串(不只是不同的情况,但不同的行为)不同的结果,并f的签名是正确的,那么这是错误的,你应该接受那叫f("foobar")没有意义。