在Cats和Scala中使用可应用函子函数

dba5bblo  于 5个月前  发布在  Scala
关注(0)|答案(1)|浏览(61)

如何在Scala 3和Cats中实现类似的Haskell函数?

ghci> (+) <$> (+1) <*> (+1) $ 10
22

字符串
有一个使用mapN的解决方案。这里提到了Using functions as applicative functors/cartesians,但我想要一个使用<*>而不是mapN的解决方案。我尝试了类似的方法,但无法编译

import cats._
  import cats.implicits._

  type Func1[Y] = Int => Y

  val add1: Func1[Int] = (x: Int) => x + 1
  val addXY: Func1[Func1[Int]] = (x: Int) => (y: Int) => x + y

  val foo: Func1[Func1[Func1[Int]]] = Applicative[Func1].pure(addXY)
  val bar: Func1[Func1[Int]] = Applicative[Func1].pure(add1)
  val foobar = foo <*> bar 
  foobar(10)  // should be 22 but it can't compile


请多指教。谢谢

xiozqbni

xiozqbni1#

Scala Cats允许函数作为Applicative示例,就像Haskell一样。

(+) <$> (+1) <*> (+1) $ 10

字符串
我们可以在Scala中将这些函数编写为

def add1(x: Int) = x + 1

def addXY(x: Int)(y: Int) = x + y


现在<*>的工作方式和Haskell中的一样,但是<$>并不作为中缀存在(尤其是因为$在基于JVM的语言中不是符号字符)。请记住,f <$> x只是等价于fmap f x,或者(根据Applicative定律)pure f <*> x
所以我们可以写

val foobar = addXY.pure[[X] =>> Function1[Int, X]] <*> add1 <*> add1
println(foobar(10))


现在,虽然Cats没有提供我所知道的中缀<$>,但我们可以很容易地自己编写一个作为扩展方法。我们不能将其命名为<$>,因为美元符号在JVM上很有趣,但我们可以编写<@>

extension[A, B](left: A => B)
  def <@>[F[_]: Functor](right: F[A]): F[B] =
    right.fmap(left)


现在我们可以说

val foobar = addXY <@> add1 <*> add1
println(foobar(10))

完整示例:

package example

import cats.*
import cats.implicits.*

def add1(x: Int) = x + 1

def addXY(x: Int)(y: Int) = x + y

extension[A, B](left: A => B)
  def <@>[F[_]: Functor](right: F[A]): F[B] =
    right.fmap(left)

@main def main(args: String*): Unit = {
  val foobar = addXY <@> add1 <*> add1
  println(foobar(10))
}

Scala 2

以上所有内容都是在Scala 3中编写的。如果您使用的是Scala 2,那么您必须做一些复杂的工作才能使Scala按照您想要的方式处理函数。正如您已经看到的,您必须编写type别名才能获得正确的类型级泛型行为(例如,如果你想调用pure)。def函数必须显式转换为后缀为_的函数。extension方法需要是老式的implicit class
所以总的来说,这是用Scala 2编写的同一个程序。

package example

import cats._
import cats.implicits._

import scala.language.higherKinds

object Main {

  implicit class FmapExtension[A, B](
    val left: A => B,
  ) extends AnyVal {

    def <@>[F[_]: Functor](right: F[A]): F[B] =
      right.fmap(left)

  }

  def add1(x: Int) = x + 1

  def addXY(x: Int)(y: Int) = x + y

  def main(args: Array[String]): Unit = {
    val foobar = (addXY _) <@> (add1 _) <*> (add1 _)
    println(foobar(10))
  }

}

相关问题