3

我正在尝试编写一个通用方法,它将任何具有scalaz.IsEmptytypeclass 实例的内容包装到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(通过使用类型 lambda来指示C具有 shape 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和 更高类型的方法?

4

2 回答 2

4
scala> asOption0(List(1,2,3))
<console>:17: error: could not find implicit value for parameter
                     ev: scalaz.IsEmpty[[A]List[Int]]

此错误告诉您它找不到IsEmpty列表的实例,这是因为类型参数无关紧要。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)
于 2013-02-17T22:03:01.477 回答
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's 文档字符串的第一部分:

表示MA已被解构为M[_] 应用于类型的类型构造函数的类型A,以及相应的类型类实例TC[M]

伴随对象中的隐式转换提供了一种为部分应用的类型构造函数获取类型类实例的方法,以代替SI-2712中描述的直接编译器支持。

于 2013-02-20T18:30:39.007 回答