【问题标题】:Functor for scala.collection.Map[Int, T]scala.collection.Map[Int, T] 的函子
【发布时间】:2021-05-09 16:39:22
【问题描述】:

我正在尝试查找是否可以为 Map 类型创建 Functor 的信息。 文档中有ListOption 的信息,但不适用于我的情况。

能否告诉我是否可以创建Functor[Map[Int, T]]

下面我将附上List 的类似函子的实现。

trait Functor[F[_]]:
    def map[A, B](list: F[A])(f: A => B): F[B]

given Functor[List] with
    def map[A, B](list: List[A])(f: A => B): List[B] = ???

【问题讨论】:

  • 是的,有可能;作为可能的证据,您可以看到 cats(old) Scalaz 都提供了它。这是否回答你的问题? - (奖励?) 查找 partial unification 并输入 lambdas。
  • @LuisMiguelMejíaSuárez,我在那里搜索但找不到。您能否为我的案例展示一段代码?

标签: scala dictionary functor


【解决方案1】:

扩展 Luis 和 Andrey 的答案,使用 Scala 2 和 scala-cats 尝试使用类型别名

import cats.Functor

type MapInt[T] = Map[Int, T]
Functor[MapInt].map(Map(1 -> "woo"))(a => a + "hoo") 
// : MapInt[String] = Map(1 -> "woohoo")

或使用 Scala 2 类型的 lambda "atrocity"

Functor[({type MapInt[T]=Map[Int, T]})#MapInt].map(Map(1 -> "woo"))(a => a + "hoo")
// : MapInt[String] = Map(1 -> "woohoo")

或使用kind-projector

Functor[Map[Int, *]].map(Map(1 -> "woo"))(a => a + "hoo")
// : MapInt[String] = Map(1 -> "woohoo")

scastie


关于 MapFunctor 的“种类”错误,请尝试使用 REPL 来探索这个想法(如果您以 sbt console 开头,它应该加载来自 build.sbt 的所有依赖项):

scala> import cats.Functor
import cats.Functor

scala> :kind -v Functor
cats.Functor's kind is X[F[A]]
(* -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> :kind -v Map
Map's kind is F[A1,+A2]
* -> * -(+)-> *
This is a type constructor: a 1st-order-kinded type.

scala> type MapInt[T] = Map[Int, T]
type MapInt

scala> :kind -v MapInt
MapInt's kind is F[A]
* -> *
This is a type constructor: a 1st-order-kinded type.

注意Functor 有高阶种类

(* -> *) -> *
\______/
   |
required shape of type argument to Functor

这意味着它需要一个一阶类型的类型构造函数,特别是一个接受单个类型参数的类型构造函数

* -> *
|
only one type argument expected

现在Map 类型构造函数本身确实具有Functor 所期望的一阶类型,但是它是错误的arity,因为它需要两个类型参数而不是一个

1st arg to Map
|
* -> * --> *
     |
   2nd arg to Map

因此,我们需要一个类型 lambda 来将 Map 的第一个类型参数固定为 Int,同时保持第二个类型参数空闲,这会将其转换为 Functor 的正确类型和数量的类型构造函数

scala> :kind -v MapInt
MapInt's kind is F[A]
* -> *

这是一个高阶类型构造函数的示例,它采用另一个二元一阶类型构造函数作为其类型参数

trait Foo[F[A, B]]

让我们检查 Map 现在是否适合而无需使用类型 lambda

scala> trait Foo[F[A, B]]
trait Foo

scala> :kind -v Foo
Foo's kind is X[F[A1,A2]]
(* -> * -> *) -> *
This is a type constructor that takes type constructor(s): a higher-kinded type.

scala> new Foo[Map] {}
val res2: Foo[Map] = $anon$1@589af27e

【讨论】:

  • 哦,有趣。 Kind-projector 弃用了?-syntax,现在改用*。嗯,我曾经给 * = Nothing 起别名,这似乎偶尔可以用作协变类型构造函数的参数...
  • @AndreyTyukin 正在准备 Scala 3 最终用 ? 替换存在通配符 _,我认为目标是 3.2 版本。
【解决方案2】:
  • Map[A, B]A => B 几乎相同,但具有有限域。
  • A => B 基本上是 Reader[A, B]
  • Reader[A, B] 是一个单子
  • 所有单子都是函子

所以,是的,让我们尝试做与Reader 相同的操作:它只是对赋予map 的函数进行后组合:

given mapFunctor[K]: Functor[[V] =>> Map[K, V]] with
  def map[A, B](m: Map[K, A])(f: A => B): Map[K, B] = m.view.mapValues(f).toMap

toMap 是必要的,因为mapValues 是非严格的(它拒绝立即映射所有值,并将函数组合保持在符号MapView 中)。

【讨论】:

  • 这个“=>>”是什么意思?
  • F[_] 应该有一个类型孔,但Map 有两个(一个用于键,一个用于值)。我们必须部分应用K 参数才能获得具有正确类型的类型构造函数。 =>> 是一个 lambda 类型:它与表达式 (v) => m(k, v) 基本相同,但在类型级别;在 2.13 中,通常会使用 kind-projector 插件编写 Map[K, ?],但在 3.x 中不再需要它
猜你喜欢
  • 2015-08-21
  • 2020-02-11
  • 1970-01-01
  • 1970-01-01
  • 2014-09-15
  • 2019-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多