如何为泛型类型编写scalaz.IsEmpty参数

时间:2013-02-17 18:46:31

标签: scala generics typeclass scalaz higher-kinded-types

我正在尝试编写一个泛型方法,将包含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]

是否可以同时编写一个适用于StringList和更高类型的方法?

2 个答案:

答案 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的帮助下,可以编写适用于许多不同类型的通用asOptionUnapply支持的类型)并且不需要任何其他隐式转换:

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中所述。