【问题标题】:Scala -- How to use Functors on non-Function types?Scala——如何在非函数类型上使用函子?
【发布时间】:2011-11-26 04:23:19
【问题描述】:

在阅读此博客上对 Functor 的描述时:

https://hseeberger.wordpress.com/2010/11/25/introduction-to-category-theory-in-scala/

Functor 有一个通用的定义和一个更具体的定义:

trait GenericFunctor[->>[_, _], ->>>[_, _], F[_]] {
  def fmap[A, B](f: A ->> B): F[A] ->>> F[B]
}
trait Functor[F[_]] extends GenericFunctor[Function, Function, F] {
  final def fmap[A, B](as: F[A])(f: A => B): F[B] =
    fmap(f)(as)
}

显然,这意味着 Functor 可以与 Function 对象之外的其他更高种类的类型一起使用。有人可以举个例子或解释如何或为什么或在什么情况下会做?也就是说,在 Scala 中 GenericFunctor 的另一个实现会是什么——它使用与 Function 不同的类型构造函数?谢谢!

编辑:

澄清一下:

object Functor {

  def fmap[A, B, F[_]](as: F[A])(f: A => B)(implicit functor: Functor[F]): F[B] =
    functor.fmap(as)(f)

  implicit object ListFunctor extends Functor[List] {
    def fmap[A, B](f: A => B): List[A] => List[B] =
      as => as map f
  }
}
scala> fmap(List(1, 2, 3))(x => x + 1)
res0: List[Int] = List(2, 3, 4)

澄清一下,根据我的理解,ListFunctor 在 GenericFunctor 中实现 1-arg fmap,而 repl 脚本中的代码在 Trait Functor 中调用 fmap,而 Trait Functor 又调用 fmap 实现(例如在 ListFunctor 中)。

这不会改变整个问题,只是认为它会帮助人们尝试提供答案。提供的任何见解将不胜感激。

【问题讨论】:

    标签: function scala functional-programming functor category-theory


    【解决方案1】:

    在您的示例中,Functor 是 Scala 类型类别中的内函子,Function1 作为箭头。

    还有其他类别。例如,假设对象是 Scala 类型的类别,如果 B 是 A 的子类型,则有一个箭头 A >~> B。 Scalaz 中的这个类别称为Liskov。从Liskov 类别到Function1 类别有一个“健忘”函子:

    import scalaz._
    import Scalaz._
    trait Forget[F[-_]] extends GenericFunctor[>~>, Function1, F] {
      def fmap[A, B](f: A >~> B): F[A] => F[B] = fa => f.subst(fa)
    }
    

    请注意,您可以通过将一个或多个参数固定为GenericFunctor 来构建一些有趣的函子。比如……

    常量函子将一个类别中的每个对象映射到另一个类别中的单个对象:

    type ConstantFunctor[->>[_, _], ->>>[_, _], C] =
      GenericFunctor[->>,->>>,({type F[x] = C})#F]
    // def fmap[A, B](f: A ->> B): C ->>> C
    

    endofunctor 将类别映射到自身:

    type EndoFunctor[->>[_, _], F[_]] = GenericFunctor[->>, ->>, F]
    // def fmap[A, B](f: A ->> B): F[A] ->> F[B]
    

    恒等函子将每个对象和箭头映射到自身:

    type IdentityFunctor[->>[_, _]] = EndoFunctor[->>, ({type F[x] = x})#F]
    // def fmap[A, B](f: A ->> B): A ->> B
    

    当然,您的 Functor 特征只是 Function1 类别中的 EndoFunctor。

    type Functor[F[_]] = EndoFunctor[Function1, F]
    // def fmap[A, B](f: A => B): F[A] => F[B]
    

    【讨论】:

      【解决方案2】:

      您可以想象一个函子将Either[A,B] 的实例提升为Either[F[A],F[B]],其中F 可以是List、Option 等。

      EDIT 实现示例:

      trait GenericFunctor[->>[_, _], ->>>[_, _], F[_]] {
        def fmap[A, B](f: A ->> B): F[A] ->>> F[B]
      }
      
      trait EitherFunctor[F[_]] extends GenericFunctor[Either,Either,F]
      
      object ListFunctor extends EitherFunctor[List] {
        def fmap[A,B]( f: Either[A,B] ): Either[List[A],List[B]] = 
          f match {
            case Left(a) => Left( List(a) )
            case Right(b) => Right( List(b) )
          }
      }
      

      EDIT2 另一个(可能有用的)例子是函子从PartialFunction(类型->>)到Function(类型->>>):

      trait PartialFunctor[F[_]] 
      extends GenericFunctor[PartialFunction,Function,F] {
        final def fmap[A, B](as: F[A])(f: PartialFunction[A,B]): F[B] =
          fmap(f)(as)
      }
      
      object OptionFunctor extends PartialFunctor[Option] {
        def fmap[A,B]( f: PartialFunction[A,B] ): Option[A] => Option[B] =
          (opt:Option[A]) => opt match {
            case Some(a) => f.lift(a)
            case None => None
          }
      }
      
      object ListFunctor extends PartialFunctor[List] {
        private def mapPartial[A,B]( f: PartialFunction[A,B], as: List[A] ): List[B] =
          as match {
            case Nil => Nil
            case h :: t => if( f isDefinedAt h ) f(h) :: mapPartial( f, t )
                           else mapPartial( f, t )
          }
      
        def fmap[A,B]( f: PartialFunction[A,B] ): List[A] => List[B] =
          (lst:List[A]) => mapPartial(f, lst)
      
      }
      

      第二个示例允许实现 Scala 集合中定义的 collect 操作:

      def collect[A,B,F[_]]( as: F[A] )
                           ( pf: PartialFunction[A,B] )
                           ( implicit functor: PartialFunctor[F] ) = 
        functor.fmap( as )( pf )
      

      【讨论】:

      • 我不认为every-functor是一个functor。我看不出您将如何实现任一类别。
      【解决方案3】:

      Data.Category 是一个很好的例子(在 Haskell 中)。部分翻译来自http://hackage.haskell.org/packages/archive/data-category/0.4.1/doc/html/src/Data-Category-Functor.html 的实例之一:

      type OpFun[A, B] = B => A
      
      case class OpFunctor[F[_]](functor: Functor[F[_]]) extends GenericFunctor[OpFun, OpFun, F] {
        def fmap[A, B](f: B => A): F[B] => F[A] = fb => functor.fmap(fb)(f)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-24
        • 2013-03-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多