我正在尝试为String
创建scalaz的IsEmpty
类型类的实例。这是我到目前为止所得到的:
implicit val stringIsEmpty = new IsEmpty[({ type t[+A] = String })#t] {
def isEmpty[A](fa: String) = ???
def empty[A] = ???
def plus[A](a: String, b: => String) = ???
}
def f[F[_]: IsEmpty, A](fa: F[A]): F[A] = fa
现在,String
属于*
,而IsEmpty[F]
期望F
属于* -> *
,因此类型为lambda f[+A] = String
。
这样可行,但仅当字符串键入为t
时,即:
// doesn't compile
f("hello")
// compiles if I extract `t` into a type alias and annotate the string with it
type t[+A] = String
implicit val stringIsEmpty = new IsEmpty[t] { /** **/ }
f("hello": t[Nothing])
有没有办法以这样的方式实现IsEmpty
,然后我可以正常地将f
应用于字符串?
答案 0 :(得分:1)
事实证明,您可以使用Unapply
魔法将类型*
转换为类型* -> *
。
这是我的解决方案:
def f[F[_]: IsEmpty, A](fa: F[A]): F[A] = fa
def fU[FA](fa: FA)(implicit U: Unapply[IsEmpty, FA]) =
f[U.M, U.A](U(fa))(U.TC)
现在你可以做fU("hello")
。返回类型也将正确推断为String
。
据我所知,你确实需要这个辅助功能,这是一种痛苦,但我想这是缺乏direct compiler support.
的代价。当我意识到Applicative
有一个String
的实例并且您可以执行"hello".replicateM(3)
之类的内容时,我得到了这个想法。
我像这样表达了这个表达式:
scala> import scala.reflect.runtime.universe
import scala.reflect.runtime.universe
scala> universe.reify("hello".replicateM(3)).tree
res32: reflect.runtime.universe.Tree = Scalaz.ToApplicativeOpsUnapply("hello")(Unapply.unapplyA(Applicative.monoidApplicat
ive(Scalaz.stringInstance))).replicateM(3)
并且ToApplicativeOpsUnapply
揭示了幕后的秘密:
implicit def ToApplicativeOpsUnapply[FA](v: FA)(implicit F0: Unapply[Applicative, FA]) =
new ApplicativeOps[F0.M,F0.A](F0(v))(F0.TC)