【问题标题】:scala type mismatch Higher kinded typescala 类型不匹配 高级类型
【发布时间】:2017-01-10 17:57:34
【问题描述】:

我想建模一个Mapper,它接收一个As (T[A]) 的容器,以便使用f: A => B 的函数我们得到另一个容器T[B]。经过数小时的实验(参见注释代码),我提出了以下解决方案:

  sealed trait Mapper[ A, T[ A ], B ] {
    //type Out <: T[B]
    type Out[X] //= T[X]
    def map( l: T[ A ], f: A => B ): Out[B]
  }

  object Mappers {

    implicit def typedMapper[ A, T[ A ] <: Iterable[ A ], B ]: Mapper[ A, T, B ] =
      new Mapper[ A, T, B ] {
        override type Out[X] = Iterable[X]
        //override type Out <: Iterable[ B ]
        //def map( l: T[ A ], f: A => B ) : this.Out = {
        def map( l: T[ A ], f: A => B ) : Out[B] = {
          println( "map" )
          l.map( f )
        }
      }

    implicit def IntMapper = typedMapper[Int, List, Int]
  }

  //def testMapper[ A, T[ A ], B ]( l: T[ A ], f: A => B )( implicit mapper: Mapper[ A, T, B ] ): T[B] = {
  def testMapper[ A, T[ A ], B ]( l: T[ A ], f: A => B )( implicit mapper: Mapper[ A, T, B ] ) : Mapper[A, T, B]#Out[B]= {
    println( mapper )
    mapper.map(l, f)
  }

我现在可以按如下方式使用它:

import Mappers.IntMapper
val l9 = testMapper( List( 1, 2, 3 ), { x: Int => x + 1 } )
println(l9)

虽然它有效,但我仍然不知道如何将 Out 直接限制为 T[B]。如果我这样做,我似乎总是会遇到类型不匹配。谁能指出没有类型别名或直接使用T[B] 的更清洁/更简单的方法?

TIA

【问题讨论】:

  • 您发布的代码无法编译,您能发布一个可以编译的版本吗?
  • 您正在提供范畴论中所谓的“函子”的具体实现。有一个广泛使用的库,称为“scalaz”,以及“cat”。要获得好的总结,请查看blog.tmorris.net/posts/functors-and-things-using-scala/…
  • Mapper 不是Functor:您可以限制可能的As 和Bs,而Functor 必须适用于所有AB。这使得它在某些情况下的用处大大降低,但我想它一定有一些用处。
  • 我认为你得到类型不匹配的原因是当你 map A =&gt; B over T[A] where T[A] &lt;: Iterable[A] 时,你没有得到 T[?] 回来;你会得到一个Iterable[B]。您失去了对原始Iterable 的了解,因此默认的Out = T[A] 不起作用。
  • @AlvaroCarrasco 对此表示歉意。代码已更正。 Jist 取消注释行 type Out[X] //= T[X]

标签: scala type-mismatch higher-kinded-types type-alias


【解决方案1】:

这是您想要的注释的第一个近似值。类型成员已被消除,揭示了你想要做的更深层次的问题。

trait Mapper[A, T[_], B] {
  def map(ta: T[A])(f: A => B): T[B]
}

// Note that Iterable[A]#map has type [B](A => B)Iterable[B]. You can't have typedMapper
// like yours from above just yet, because T#map is not certain to return another T;
// it only promises an Iterable.
implicit def iterableMapper[A, B]: Mapper[A, Iterable, B] = new Mapper[A, Iterable, B] {
  // Multiple param lists support the type inferencer
  override def map(i: Iterable[A])(f: A => B) = i.map(f)
}

// Curried and arg-swapped version of Mapper
type MapperOf[A, B] = { type l[T[_]] = Mapper[A, T, B] }
def map[A, B, T[_]: MapperOf[A, B]#l](ta: T[A])(f: A => B): T[B] = implicitly[Mapper[A, T, B]].map(ta)(f)

map(??? : Iterable[Any])(_.toString) // OK (at compile time, at least :P)
map(List(1,2,3))(_*2) // NOPE! The inferencer has already decided T = List, before
                      // looking for implicits, so the resolution fails to notice that
                      // iterableMapper would work.
map[Int, Int, Iterable](List(1,2,3))(_*2) // Works

这是在推动类型推断器的极限,这就是您需要手动指定类型参数的原因。

请注意,Iterable 对于集合层次结构并不重要。它之所以存在,主要是因为 Java 拥有它。为了使其对集合具有适当的通用性,您需要一些美味的 CanBuildFrom 黑暗魔法。

import collection._, generic._ // Open the gates of hell

implicit def collectionMapper[A, Coll[A] <: GenTraversableLike[A, Coll[A]], B]
    (implicit builderFactory: CanBuildFrom[Coll[A], B, Coll[B]]):
    Mapper[A, Coll, B] =
  new Mapper[A, Coll, B] {
    override def map(coll: Coll[A])(f: A => B): Coll[B] = coll.map(f)
  }

map(List(1))(_*2): List[Int] // Works
map(Seq(1).view)(_*2): Seq[Int] // Works, but note how we lose the knowledge of the view
                                // Exercise for the reader: fix that.
map(BitSet(1))(_*2): SortedSet[Int] // Works, but we lose the BitSet-ness
                                    // Another exercise: fix it.

【讨论】:

  • 我一直在玩你的第一个例子。如果我将 trait Mapper[A, T[_], B] 更改为特征 Mapper[A, T[A], B],仍然可以像宣传的那样工作。如果我更改 iterableMapper[ A, B ] 使 map 函数只有一个参数列表,它仍然可以工作(版本 12.2)。现在我不明白为什么需要type MapperOf[A, B]。为什么这行不通? def map4[ A, B, T[_] ]( ta: T[ A ] , f: A =&gt; B ): T[ B ] = implicitly[ Mapper1[ A, T, B ] ].map( ta ) ( f )(未找到隐式)。 TIA
  • 至于使用CanBuildFrom 的解决方案,我不知道。在尝试之前,我必须了解它是如何工作的。
  • MapperOf 是纯糖。您可以转储它并将以下上下文绑定重写为 { type l[T[_]] = Mapper[A, T, B] }#l,但这很难看。它真正所做的只是将类型级别函数 Mapper 从 (, * -> *, *) -> * 转换为 (, ) -> ( -> *) - > *。上下文绑定总是去糖到映射的隐式参数,(隐式 $_some_garbage_internal_name$$$: Mapper[A, T, B])。你得到 INF 的原因是你不知道 A、T 或 B 是什么。您需要用户自己将其作为隐式参数提供给您。
  • 当你把T[_]改成T[A]的时候,和外面的A没有关系。你在T[A]的上下文中定义了一个新的A。
  • 你应该给map 两个参数列表,因为1)它使语法更清晰(map(...) { ... ) vs. map(..., { ... }))2)让推理器高兴(map(..., { x =&gt; ... }) => error: missing parameter type , 但是map(...) { x =&gt; ... } 从被映射的事物的类型中推断出x 的类型)3)在柯里化时使其更漂亮(map(...)(_)map(...).)。
猜你喜欢
  • 2012-08-27
  • 1970-01-01
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
  • 2011-08-25
  • 2019-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多