如何实现Scala原生风格的List.map?兼论Functor使用差异
实现Scala原生风格的List.map方法及相关澄清
问题背景
Scala官网曾给出一个重实现List的map方法的示例,调用方式为独立函数式调用:
def strlen(s: String): Int = s.length map(strlen, List("a", "bb", "ccc")) // 结果:List(1, 2, 3)
而Scala原生List的map是作为实例方法调用的,风格如下:
List("a","b").map(x => x.length)
实现原生风格的map方法
要实现能以List实例.map(转换函数)方式调用的map,只需将map定义为List类/特质的成员方法,或者通过扩展方法为现有List添加该方法,以下以自定义List为例:
方式1:在List特质中直接定义map
// 自定义List的代数数据类型 sealed trait MyList[+A] { // 作为成员方法实现map def map[B](f: A => B): MyList[B] = this match { case Nil => Nil case Cons(head, tail) => Cons(f(head), tail.map(f)) } } case object Nil extends MyList[Nothing] case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A] // 伴生对象提供便捷创建方法 object MyList { def apply[A](elems: A*): MyList[A] = if (elems.isEmpty) Nil else Cons(elems.head, apply(elems.tail: _*)) } // 调用示例 val testList = MyList("a", "bb", "ccc") println(testList.map(_.length)) // 输出:Cons(1,Cons(2,Cons(3,Nil)))
方式2:通过扩展方法为现有List添加自定义map
如果不想修改原生List的定义,也可以用Scala的扩展方法实现:
extension [A](list: List[A]) { def customMap[B](f: A => B): List[B] = list match { case Nil => Nil case head :: tail => f(head) :: tail.customMap(f) } } // 调用示例 List("a", "b").customMap(_.length) // 结果:List(1, 2)
关于Functor的澄清
官网示例中的map是作为独立函数实现的,符合函数式编程中Functor抽象的形式(即map: (F[A], A=>B) => F[B]),这种实现是对Functor抽象的具象化。但Scala官方源码中的List.map并没有依赖Functor抽象:
- Scala标准库的List是直接通过模式匹配(匹配Nil和::)实现map方法,作为类的成员方法存在;
- 虽然Scala有
scala.util.control.Functortrait,但List并未继承它,官方集合设计更偏向面向对象的成员方法风格,Functor的抽象更多由第三方库(如Cats、ZIO Prelude)提供支持。
内容的提问来源于stack exchange,提问作者datnt
相关产品推荐
相关产品推荐

