簡體   English   中英

Scala更高的kinded類型方差

[英]Scala higher kinded type variance

我正在用更高級的類型浸泡我的腳趾,探索一個非常基本的Scala示例:

trait Mappable[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]
}

object Mappable {
  implicit object MappableOption extends Mappable[Option] {
    def map[A, B](fa: Option[A])(f: A => B): Option[B] = fa.map(f)
  }
  implicit object MappableSeq extends Mappable[Seq] {
    def map[A, B](fa: Seq[A])(f: A => B): Seq[B] = fa.map(f)
  }
}

def bananaTuple[F[_], T](f: F[T])(implicit F: Mappable[F]): F[(String, T)] =
  F.map(f)(("banana", _))

這有效:

bananaTuple(Option(42)) // Some((banana,42))
bananaTuple(Seq(42))    // List((banana,42))

但這不編譯:

bananaTuple(Some(42))
bananaTuple(List(42))

我得到的編譯錯誤:

could not find implicit value for parameter F: ch.netzwerg.hkt.HigherKindedTypes.Mappable[Some] bananaTuple(Some(42))

not enough arguments for method bananaTuple: (implicit F: ch.netzwerg.hkt.HigherKindedTypes.Mappable[Some])Some[(String, Int)]. Unspecified value parameter F. bananaTuple(Some(42))

如何在游戲中帶來變化?

我們可以使用更多參數多態來完成這項工作:

object MappableExample {
  trait Mappable[F[_]] {
    type Res[_]
    def map[A, B](f: A => B)(c: F[A]): Res[B]
  }

  implicit def seqMappable[C[X] <: Seq[X]] = new Mappable[C] {
    type Res[X] = Seq[X]
    override def map[A, B](f:A => B)(c: C[A]): Seq[B] = c.map(f)
  }

  implicit def optionMappable[C[X] <: Option[X]]: Mappable[C] = new Mappable[C] {
    type Res[X] = Option[X]
    override def map[A, B](f: A => B)(c: C[A]): Option[B] = c.map(f)
  }

  def map[A, B, C[_]](xs: C[A])(f: A => B)(implicit mappable: Mappable[C]): mappable.Res[B] = {
    mappable.map(f)(xs)
  }

  def main(args: Array[String]): Unit = {
    println(map(List(1,2,3))(("banana", _)))
    println(map(Some(1))(("banana", _)))
  }
}

產量:

List((banana,1), (banana,2), (banana,3))
Some((banana,1))

編譯器現在將Some推斷為Mappable[Some]#Res[Int]Mappable[List]#Res[Int] ,這非常難看。 人們可以期望編譯器實際上能夠推斷出正確的類型,而不需要對Mappable特性進行任何協變/ Mappable ,這是我們無法做到的,因為我們在不變的位置使用它。

子類型多態性允許我們將某種類型或其任何子類型的值傳遞給方法。 如果方法采用Fruit類型的值,我們也可以在內部傳遞Apple(畢竟蘋果是一種水果)。 因此,如果您希望能夠將Mappable.MappableOption傳遞給bananaTuple方法,則必須使MappableOption成為MappableSome的子類型(因為bananaTuple的第一個參數的類型指示了隱含的類型)。 這意味着您需要Mappable逆變(如果Some <: Option ,則可以使用Mappable[Some] >: Mappable[Option] )。

但是你不能在Mappable[F[_]]Mappable[F[_]]逆變量,因為F出現在map協變位置(作為函數參數)。 請注意,F也出現在map逆變位置(作為返回值)。

如果你設法在Mappable[F[_]]制作Mappable[F[_]]逆變,它應該可以工作,但我不確定是否使逆變量變得有意義。 也就是說,如果你想要一個子類型關系,例如Apple <: Fruit導致Mappable[Apple] >: Mappable[Fruit] (這不會編譯,因為Apple和Fruit不是類型構造函數,但我只是使用簡單在這里提出要點的類型)。

在類型中進行類型逆變並解決出現在協變位置的逆變類型問題是一個常見問題,如果你在其他地方搜索它可能會更好( 這里是一個例子)。 我仍然認為最好為你想要使用的每個類型提供一個隱式對象,也就是說,為SeqList提供單獨的隱式對象。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM