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

如何为泛型类型编写scalaz.IsEmpty参数,scala,generics,typeclass,scalaz,higher-kinded-types,Scala,Generics,Typeclass,Scalaz,Higher Kinded Types,我正在尝试编写一个通用方法,将任何具有typeclass实例的对象包装到选项中。对于空值,它应该返回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

我正在尝试编写一个通用方法,将任何具有typeclass实例的对象包装到
选项中。对于空值,它应该返回
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
(使用a表示
C
具有
B[\u]
)的形状,
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]
scala>asOption0(“”)
res1:选项[字符串]=无
scala>asOption1(列表(1,2,3))
res0:Option[List[Int]=Some(List(1,2,3))
scala>asOption0(列表(1,2,3))
:17:错误:找不到参数的隐式值
ev:scalaz.IsEmpty[[A]List[Int]]
scala>A选项1(“你好”)
:17:错误:找不到参数的隐式值
ev:scalaz.IsEmpty[可比]
是否可以编写一个同时适用于
字符串
列表
和更高类型的方法

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

编辑 我又看了一眼这个问题,找到了一个看起来更干净的解决方案。我担心这不是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)
在的帮助下,可以编写一个通用的
a选项
,该选项适用于许多不同的类型(那些由
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))
以下是
的docstring的第一部分:

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

伴随对象中的隐式转换提供了获取类型类的方法 部分应用类型构造函数的实例,代替直接编译器支持 如中所述


隐式参数的隐式转换。。。有趣!我从来没见过。如果
asOption0
也适用于例如
Map
,则似乎需要进行另一个转换:但这提出了一个问题:为什么Scalaz为
Map提供了一个
IsEmpty[({type F[V]=Map[K,V]})的实例
而不是
IsEmpty[({type F[[u]=Map[K,V]})F]})
。我看到您更改了您的问题。你能解释一下你到底想要实现什么吗?@EECOLOR我更改了标题,以便更好地匹配我问题的最后一句话。我希望有一个适用于任何类型的解决方案,不仅适用于
*
*->*
。另一个“显式”隐式转换似乎对于例如
Map
是必要的,这是不令人满意的。我确实觉得只要提供适当的typeclass实例就足以使用
a选项
。对不起,前面的标题有误导性!我编辑了我的答案以添加另一个解决方案。然而,这可能仍然不是你想要的。如果不是,你能解释一下你在找什么吗?@EECOLOR似乎我通过使用
scalaz.Unapply
找到了我问题的一个答案。此解决方案不需要任何额外的隐式转换,只需要
IsEmpty
的一个实例。
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))