当前位置: 首页 > 知识库问答 >
问题:

何时以及为什么应该在Scala中使用应用函数

巫马昆杰
2023-03-14

我知道Monad可以用Scala表示如下:

trait Monad[F[_]] {
  def flatMap[A, B](f: A => F[B]): F[A] => F[B]
}

我明白为什么它是有用的。例如,给定两个函数

getUserById(userId: Int): Option[User] = ...
getPhone(user: User): Option[Phone] = ...

我可以很容易地编写函数<code>getPhoneByUserId(userId:Int),因为<code>选项是一个单子:

def getPhoneByUserId(userId: Int): Option[Phone] = 
  getUserById(userId).flatMap(user => getPhone(user))

...

现在我看到Scala中的< code >应用函子:

trait Applicative[F[_]] {
  def apply[A, B](f: F[A => B]): F[A] => F[B]
}

我想知道我什么时候应该使用它而不是monad。我想Option和List都是Application。您能否举一个简单的例子来使用应用与Option和List一起使用,并解释为什么我应该使用它而不是平面图

共有2个答案

笪智志
2023-03-14

仿函数用于将计算提升到一个类别。

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

它完美地适用于一个变量的函数。

val f = (x : Int) => x + 1

但是对于2及以上的函数,在提升到一个类别后,我们有以下签名:

val g = (x: Int) => (y: Int) => x + y
Option(5) map g // Option[Int => Int]

它是一个应用函子的签名。并应用

trait Applicative[F[_]] {
  def apply[A, B](f: F[A => B]): F[A] => F[B]
} 

最后:

(Applicative[Option] apply (Functor[Option] map g)(Option(5)))(Option(10))

应用函子是用于将特殊值(类别中的值)应用于提升函数的函子。

丁宏浚
2023-03-14

引用我自己的话:

那么,当我们有monad的时候,为什么还要费心于应用函子呢?首先,为我们想要使用的一些抽象提供monad实例是不可能的-验证是一个完美的例子。

其次(与此相关),它只是一个可靠的开发实践,使用最不强大的抽象来完成工作。原则上,这可能允许优化,否则是不可能的,但更重要的是,它使我们编写的代码更具可重用性。

扩展一下第一段:有时你无法在一元代码和可应用代码之间进行选择。关于为什么要使用Scalaz的< code>Validation(它没有也不可能有monad实例)来建模验证的讨论,请参阅该答案的其余部分。

关于优化点:这在Scala或Scalaz中通常是相关的,可能还需要一段时间,但请参见Haskell的<code>数据文档。二进制</code>:

应用样式有时会导致更快的代码,因为二进制将尝试通过将读取组合在一起来优化代码。

编写可应用的代码可以让你避免对计算之间的依赖关系做出不必要的声明——类似的一元代码会让你做出这样的声明。一个足够聪明的库或编译器原则上可以利用这个事实。

为了使这个想法更具体一点,请考虑以下一元代码:

case class Foo(s: Symbol, n: Int)

val maybeFoo = for {
  s <- maybeComputeS(whatever)
  n <- maybeComputeN(whatever)
} yield Foo(s, n)

理解的脱糖或多或少类似于以下内容:

val maybeFoo = maybeComputeS(whatever).flatMap(
  s => maybeComputeN(whatever).map(n => Foo(s, n))
)

我们知道maybeComputeN(无论如何)不依赖于s(假设这些是行为良好的方法,不会在幕后更改某些可变状态),但编译器没有-从它的角度来看,它需要知道s才能开始计算n

应用版本(使用Scalaz)如下所示:

val maybeFoo = (maybeComputeS(whatever) |@| maybeComputeN(whatever))(Foo(_, _))

在这里,我们明确表示这两个计算之间没有依赖关系。

(是的,这个|@|语法非常可怕 - 请参阅此博客文章以获取一些讨论和替代方案。

最后一点确实是最重要的。选择最不强大的工具来解决您的问题是一个非常强大的原则。例如,有时确实需要在getPhoneByUserId方法中使用一元合成,但通常不需要。

令人遗憾的是,Haskell和Scala目前都使单子的使用(语法等)比应用函子的使用方便得多,但这主要是历史上的偶然事件,而习语括号之类的开发是朝着正确方向迈出的一步。

 类似资料:
  • 问题内容: 为什么以及何时应该在php中使用该函数?使用后是否应该始终使用它?我读到我必须使用它来防止会话固定,这是唯一原因吗? 问题答案: 什么啊 就像函数名称所说的那样,它是一个函数,它将用新的ID替换当前的会话ID,并保留当前的会话信息。 它有什么作用? 它主要有助于防止会话固定攻击。会话固定攻击是恶意用户试图利用系统中的漏洞固定(设置)另一个用户的会话ID(SID)的地方。这样,他们将拥有

  • 和 2。 正如Linux手册http://man7.org/Linux/man-pages/man2/shmget.2.html中提到的那样 IPC_PRIVATE不是标志字段,而是key_t类型。如果将此特殊值用于key,则系统调用将忽略shmflg中除最低有效9位以外的所有位,并创建一个新的共享内存段。

  • 问题内容: 我知道他们两个都禁用了Nagle的算法。 我什么时候应该/不应该使用它们中的每一个? 问题答案: 首先,不是所有人都禁用Nagle的算法。 Nagle的算法用于减少有线中更多的小型网络数据包。该算法是:如果数据小于限制(通常是MSS),请等待直到收到先前发送的数据包的ACK,同时累积用户的数据。然后发送累积的数据。 这将对telnet等应用程序有所帮​​助。但是,在发送流数据时,等待A

  • 问题内容: 在该类中,有两个字符串,和。 有什么不同?我什么时候应该使用另一个? 问题答案: 如果你的意思是和则: 用于在文件路径列表中分隔各个文件路径。考虑在上的环境变量。您使用a分隔文件路径,因此在上将是;。 是或用于拆分到特定文件的路径。例如在上,或

  • 通过和,我们将获得两种非常相似的在ES6中编写函数的方法。在其他语言中,lambda函数通常以匿名的方式区别于其他语言,但在ECMAScript中,任何函数都可以是匿名的。这两种类型都有独特的使用域(即当需要显式绑定或显式不绑定时)。在这两个领域之间,有很多情况下,任何一种表示法都可以。 ES6中的箭头函数至少有两个限制: null null

  • 问题内容: 为什么不应该使用函数的技术原因是什么?(例如,或)? 为什么我要使用其他东西,即使它们在我的网站上工作? 如果它们在我的网站上不起作用,为什么我会收到类似的错误 警告:mysql_connect():没有那个文件或目录 问题答案: MySQL 扩展: 未在积极开发中 被正式弃用的PHP 5.5(发布2013年6月)。 自 PHP 7.0起 已完全删除 (2015 年 12 月发布) 这