【问题标题】:Understanding FoldLeft in ScalaZ理解 ScalaZ 中的 FoldLeft
【发布时间】:2017-11-16 18:48:46
【问题描述】:

我正在阅读一些关于 ScalaZ 的文章,并且有一个关于理解它的问题。在这个article中,我们泛化了sum函数,抽象出要求和的类型。

def sum[T](xs: List[T])(implicit m: Monoid[T]) = //...

在哪里

trait Monoid[A] 定义如下:

trait Monoid[A] {
  def mappend(a1: A, a2: A): A
  def mzero: A
}

是的,这很清楚。这里的 Monoid 对应于algebraic monoid structure。现在在这个article 中,它对列表进行了抽象。为此,我们定义了以下特征:

trait FoldLeft[F[_]] {
  def foldLeft[A, B](xs: F[A], b: B, f: (B, A) => B): B
}
object FoldLeft {
  implicit val FoldLeftList: FoldLeft[List] = new FoldLeft[List] {
    def foldLeft[A, B](xs: List[A], b: B, f: (B, A) => B) = xs.foldLeft(b)(f)
  }
}

所以现在我们可以定义 sum 函数如下:

def sum[M[_]: FoldLeft, A: Monoid](xs: M[A]): A = {
  val m = implicitly[Monoid[A]]
  val fl = implicitly[FoldLeft[M]]
  fl.foldLeft(xs, m.mzero, m.mappend)
}

我不是理论类别专家,但对我来说它看起来像 Applicative functor。那是对的吗?我们能否提供与类别理论的这种相似性。

【问题讨论】:

    标签: scala scalaz applicative foldleft foldable


    【解决方案1】:

    Applicative 函子是一个类型类,有两个操作:A => F[A]F[A => B] => F[A] => F[B]。您提到的所有操作都没有这样的签名。 FoldLeft 更像Foldable。这是一个不同类型的类,是Traversable(又名Traverse)的父类。而TraversableApplicative 相连。

    trait Functor[F[_]] {
      def fmap[A, B](f: A => B)(fa: F[A]): F[B]
    }
    
    trait Applicative[F[_]] extends Functor[F] {
      def pure[A](a: A): F[A]
      def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
      override def fmap[A, B](f: A => B)(fa: F[A]): F[B] = ap(pure(f))(fa)
    }
    
    trait Foldable[T[_]] {
      def foldr[A, B](op: A => B => B)(seed: B)(ta: T[A]): B =
        (foldMap(op)(ta) _)(seed)
      def foldMap[A, M](f: A => M)(ta: T[A])(implicit monoid: Monoid[M]): M =
        foldr[A, M](a => m => monoid.append(f(a), m))(monoid.empty)(ta)
    }
    
    trait Traversable[T[_]] extends Functor[T] with Foldable[T] {
      def traverse[F[_]: Applicative, A, B](k: A => F[B])(ta: T[A]): F[T[B]] = 
        sequence[F, B](fmap[A, F[B]](k)(ta))
      def sequence[F[_]: Applicative, A](tfa: T[F[A]]): F[T[A]] = 
        traverse[F, F[A], A](fa => fa)(tfa)
      override def fmap[A, B](f: A => B)(ta: T[A]): T[B] = traverse[Id, A, B](f)(ta)
      override def foldr[A, B](op: A => B => B)(seed: B)(ta: T[A]): B =
        (traverse[Const[B => B]#λ, A, B](op)(ta) _)(seed)
      override def foldMap[A, M: Monoid](f: A => M)(ta: T[A]): M =
        traverse[Const[M]#λ, A, M](f)(ta)
    }
    
    type Id[A] = A
    
    trait Const[C] {
      type λ[A] = C
    }
    

    类型类Functor 意味着你有一个“容器”F[_] 并且你知道如何在这个容器中应用函数f: A => B。类型类Applicative 意味着您知道如何将值a: A 打包到此容器中,以及如何将“函数”F[A => B] 应用于“值”F[A]Foldable 表示您知道如何使用二元运算 A => B => B 和起始值 B 和接收结果 B 折叠容器。 Traversable 意味着你知道如何遍历你的容器在每个“节点”中执行应用效果A => F[B]

    【讨论】:

    • 非常有趣。非常感谢。但我想了解 ScalaZ Functor[F[_]] 是否是实际的理论类别函子。如果是这样,我不清楚它作用于什么类别。我会说f: A => B 是实际的理论类别箭头。
    • 但是对于任何给定的a: A,我们还需要找到F[A] 使其成为真正的函子。
    • 它作用于 Scala 中的类型类别。 Functor F[_] 将对象(类型)A 映射到对象(类型)F[A] 和态射(函数)f: A => B 映射到态射(函数)fmap(f): F[A] => F[B](加上一些定律)。
    • 所以函数式编程函子是特定类别的范畴论函子的部分案例,Scala 中的类型类别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    相关资源
    最近更新 更多