参数化类与函数

时间:2016-11-20 15:11:04

标签: scala generics typeclass functor category-theory

我查询了参数化类与参数化函数之间的区别。

我提供了Functor的实现,如下所示:

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

和其他函数参数化的方法如下:

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

我们应该在哪个地方使用另一个?

另一个跟进问题: 为什么我们将参数作为F [_]传递给仿函数而不是作为F [A]或F [B]。当我们使用F [A]或F [B]时会出现什么情况?

1 个答案:

答案 0 :(得分:6)

总是更喜欢第二个。使用第一个实例,您可以将实例实现为无意义的:

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

case class notEvenRemotelyAFunctor[A]() extends WrongFunctor[List,A,Int] {

  def map(fa: List[A])(f: A => Int) : List[Int] = 
    if(f(fa.head) < 4) List(3) else List(4)
}

type Id[X] = X
case object ILikeThree extends WrongFunctor[Id, Int, Int] {

  def map(fa: Int)(f: Int => Int): Int = if(fa == 3) 3 else f(fa)
}

即使你做得对,你也需要一个固定的函子实现每个不同类型的对象,你想要使用fmap。但重要的是,第二个问题至少使编写那种错误的“仿函数”变得更加困难;较少的非仿函数会滑落:

trait Functor[F[_]] {

  def map[A,B](fa: F[A])(f: A => B) : F[B]
}
case object ILikeThreeAgain extends Functor[Id] {

  def map[A,B](fa: A)(f: A => B) : B =
    ??? // how do I write the above here?
}

此处的关键字是 parametricity 参数多态。直觉是,如果通常定义某些东西,您可以从所涉及的类型中获得它将满足的属性。例如,请参阅Bartosz Milewski blog - Parametricity: Money for Nothing and Theorems for Free以获取更好的解释,或查看规范的Theorems for free论文。

后续问题

  

另一个跟进问题:为什么我们将参数作为F [_]传递给仿函数而不是作为F [A]或F [B]。当我们使用F [A]或F [B]时会出现什么情况?

因为那是仿函数的一部分;它是一个“构造函数”:

  1. 对于每种输入类型A,它会将您作为输出提供另一种类型F[A]
  2. ,对于每个函数f: A => B,另一个函数fmap(f): F[A] => F[B]满足fmap(id[A]) == id[F[A]]fmap(f andThen g) == fmap(f) andThen fmap(g)
  3. 因此,对于1.你需要一种在类型上表示函数的方法;那就是F[_]

    请注意,在您的签名中使用map方法在这种情况下等同于fmap

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

    现在关于如何与真实范畴理论联系起来:

    上述Functor[F[_]]特征的实例旨在代表 Scala - 丰富的仿函数

    F: Scala Scala

    让我们解开这个。

    有一个(通常是隐含定义的)类别 Scala ,包含对象类型,而morphisms函数f:A⇒B。这个类别是笛卡尔闭合的,其中内部hom是类型A⇒B,和产品(A,B)。我们可以使用 Scala - 丰富的类别和仿函数。什么是 Scala 丰富的类别?基本上你可以定义使用Scala语言:你有

    1. 一组对象(您需要将其表示为类型)
    2. 对于每个A,B类型C[A,B],其身份id[X]: C[X,Y]和作品andThen[X,Y,Z]: (C[X,Y], C[Y,Z]) => C[X,Z]满足类别公理
    3. 丰富的仿函数F:C→D是

      1. 从C对象到D A -> F[A]
      2. 的对象的函数
      3. 对于每对对象A,B:C Scala 中的态射,即满足仿函数法fmap: C[A,B] => C[F[A], F[B]]fmap(id[A]) == id[F[A]] fmap(f andThen g) == fmap(f) andThen fmap(g) >

        Scala 本身就是自然丰富的Scala[X,Y] = X => Y和丰富的仿函数F: Scala Scala 是什么实例你的Functor[F[_]]特征是要代表的。

        当然,这需要各种关于Scala如何打破这种情况的资格,以及态度平等等。但故事的寓意是:你的基本语言 L (在这种情况下就像Scala一样)是可能试图成为一个笛卡尔闭(或至少对称的幺半闭)类别,并且可以通过它定义的函子对应于 L - 丰富的函子。