我正在尝试编写一个泛型方法,将包含scalaz.IsEmpty
类型类实例的任何内容包装到Option
中。对于空值,它应该返回None
,如果它是非空的,则将其包装到Some
中。这是我到目前为止所得到的:
import scalaz._
import Scalaz._
def asOption0[C](c: C)(implicit ev: IsEmpty[({ type B[A] = C })#B]) =
if (ev.isEmpty(c)) None else Some(c)
def asOption1[A, C[_]](c: C[A])(implicit ev: IsEmpty[C]) =
if (ev.isEmpty(c)) None else Some(c)
asOption0
适用于基本类型,例如String
(使用type lambda表示C
具有形状B[_]
)和asOption1
适用于具有一元类型构造函数的类型,如List
:
scala> asOption0("")
res1: Option[String] = None
scala> asOption1(List(1,2,3))
res0: Option[List[Int]] = Some(List(1, 2, 3))
scala> asOption0(List(1,2,3))
<console>:17: error: could not find implicit value for parameter
ev: scalaz.IsEmpty[[A]List[Int]]
scala> asOption1("hello")
<console>:17: error: could not find implicit value for parameter
ev: scalaz.IsEmpty[Comparable]
是否可以同时编写一个适用于String
,List
和更高类型的方法?
答案 0 :(得分:4)
scala> asOption0(List(1,2,3))
<console>:17: error: could not find implicit value for parameter
ev: scalaz.IsEmpty[[A]List[Int]]
此错误告诉您它无法找到列表的IsEmpty
实例,这是因为type参数无关紧要。无论类型参数如何,Scalaz都对任何列表都是隐式的。
该方法请求IsEmpty[List[Int]]
,而Scalaz只有一个IsEmpty[List[_]]
可用。由于IsEmpty
并不关心列表的内容,因此我们只需提供asOption0
的更详细版本即可让IsEmpty
方法感到满意:
def asOption0[C](c: C)(implicit ev: IsEmpty[({ type B[_] = C })#B]) =
if (ev.isEmpty(c)) None else Some(c)
implicit def detailedIsEmpty[A, C[_]](implicit ev: IsEmpty[C]) =
ev.asInstanceOf[IsEmpty[({ type B[_] = C[A] })#B]]
asOption0("test") //> res0: Option[String] = Some(test)
asOption0(List(1, 2, 3)) //> res1: Option[List[Int]] = Some(List(1, 2, 3))
asOption0("") //> res2: Option[String] = None
asOption0(List[Int]()) //> res3: Option[List[Int]] = None
我再看一下这个问题,找到了一个看起来更清洁的解决方案。我担心这不是OP正在寻找的结果。
trait IsEmptyLike[F] {
def isEmpty(fa: F): Boolean
}
object IsEmptyLike {
implicit def case0[A](implicit ev: IsEmpty[({ type B[_] = A })#B]) =
new IsEmptyLike[A] {
def isEmpty(fa: A): Boolean = ev.isEmpty(fa)
}
implicit def case1[A[_], B](implicit ev: IsEmpty[A]) =
new IsEmptyLike[A[B]] {
def isEmpty(fa: A[B]): Boolean = ev.isEmpty(fa)
}
implicit def case2[A[_, _], B, C](implicit ev: IsEmpty[({ type D[X] = A[B, X] })#D]) =
new IsEmptyLike[A[B, C]] {
def isEmpty(fa: A[B, C]): Boolean = ev.isEmpty(fa)
}
}
def asOption[C](c: C)(implicit ev: IsEmptyLike[C]) =
if (ev.isEmpty(c)) None else Some(c)
答案 1 :(得分:2)
在scalaz.Unapply
的帮助下,可以编写适用于许多不同类型的通用asOption
(Unapply
支持的类型)并且不需要任何其他隐式转换:
import scalaz._
import Scalaz._
def asOption[MA](ma: MA)(implicit U: Unapply[IsEmpty, MA]): Option[MA] =
if (U.TC.isEmpty(U(ma))) None else Some(ma)
asOption("") //> res0: Option[String] = None
asOption("hello") //> res1: Option[String] = Some(hello)
asOption(List[Int]()) //> res2: Option[List[Int]] = None
asOption(List(1,2)) //> res3: Option[List[Int]] = Some(List(1, 2))
asOption(Map[Int,Int]()) //> res4: Option[Map[Int,Int]] = None
asOption(Map(1 -> 2)) //> res5: Option[Map[Int,Int]] = Some(Map(1 -> 2))
以下是Unapply
文档字符串的第一部分:
表示已被解构为类型构造函数
MA
的类型M[_]
应用于类型A
,以及相应的类型类实例TC[M]
。随播对象中的隐式转换提供了获取类型类的方法 部分应用类型构造函数的实例,而不是直接编译器支持 如SI-2712中所述。