类型类型的类型类实例*其中类型类型* - > *预计

时间:2016-03-17 12:34:10

标签: scala functional-programming scalaz higher-kinded-types

我正在尝试为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应用于字符串?

1 个答案:

答案 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)