【问题标题】:What is the intent behind `F[Something[F]]`?`OF[Something[F]]` 背后的意图是什么?
【发布时间】:2020-04-03 19:42:47
【问题描述】:

我看到很多F[Request[F]]F[Monitor[F]]F[Something[F]]。尽管我了解如何处理此问题的机制,但我无法直观地理解为什么某些效果 (F) 应该像这样出现(既环绕 Something 并出现在 Something 内部)。洞察力?

【问题讨论】:

  • 简短回答,Something 会产生副作用,因此它是效果类型的参数化。因此Something[F]。那么,Something 的创建可能也是一个副作用(主要是因为某些东西是共享的可变状态),因此它的创建也必须包裹一些效果。为简单起见,我们说这两种效果是相同的。
  • 如果你对F[Something[F]]的起源感兴趣,你应该阅读递归方案,Free/Cofree,无标签最终。

标签: scala scala-cats cats-effect


【解决方案1】:

一个简单的 HTTP 服务器可以用以下函数表示: 请求 => 响应

但是,我们通常需要执行有效的操作,例如从数据库中检索数据或调用外部服务。这种情况我们会这样定义: 请求 => F[响应]

http4s 对称为 HttpRoutes[F] 的路由(端点)有自己的抽象,它是文档中提到的 Kleisli[OptionT[F, ?], Request, Response] 的类型别名。

我们从 cat 文档中了解到,Kleisli 只是返回单子值的函数的组合,例如 Option[A]、Either[L, R] 等。

函数最有用的属性之一是它们可以组合。也就是说,给定一个函数 A => B 和一个函数 B => C,我们可以将它们组合起来创建一个新的函数 A => C。

在这种情况下,http4s 的 Kleislei 接受 Request 并返回 F[Response],只需 Request => F[Response]。 F 是一种效果类型。

对上面的 Request 和 Response 类型稍作修改,我们得到以下内容:

type HttpRoutes[F] = Kleisli[OptionT[F, *], Request[F], Response[F]]

HttpRoutes[F] 被声明为一系列简单的 case 语句。每个 case 语句都尝试匹配并可选地从传入的 Request[F] 中提取。与第一个匹配案例关联的代码用于生成 F[Response[F]]。

最简单的 case 语句匹配所有请求而不提取任何内容。请求的右侧必须返回 F[Response[F]]。

我们可以在这里看到 F[Response[F]] 的简单演示

scala> val getRoot = Request[IO](Method.GET, uri"/")
getRoot: org.http4s.Request[cats.effect.IO] = Request(method=GET, uri=/, headers=Headers())

scala> val io = service.orNotFound.run(getRoot)
io: cats.effect.IO[org.http4s.Response[cats.effect.IO]] = <function1>

这里cats.effect.IO[org.http4s.Response[cats.effect.IO]] 尚未解决,这是异步结果。它将在将来的某个时候以简单的 Future/Promise 解决。我们可以强制它运行:

scala> val response = io.unsafeRunSync
response: org.http4s.Response[cats.effect.IO] = Response(status=200, headers=Headers())
  1. Kleisli
  2. Http4s
  3. Gabriel Volpe 的 Scala 实用 FP

【讨论】:

  • 我真的很喜欢这篇文章。添加(我对这些概念也很陌生)-效果是F,所以你用Response[F]产生效果。那是F[Response[F]]Response[T[_]] 需要 F 来实现抽象类和接口,这些抽象类和接口也需要底层类型 F 以确保在运行时实现和产生的 Effect 类型(FutureTaskIO)是全面一致。我认为这是一种类型安全的事情。
  • 内部 F 到底是什么意思?就像在 Response[F] 中一样,它说明了什么是 Response[F] ?它在内部做什么?我已经为此 stackoverflow.com/questions/67916337/… 发布了一个具体问题
【解决方案2】:

F[_] 只是一个抽象。它可以是 List 或 IO 类型为 * -> * 的其他东西。您可以定义常用功能的主要思想。一个很好的例子是 Functor 类型类和这个类中的 map 函数。 所以在你的代码中,你可以说你 F 是一个仿函数 (F[_]: Functor) 并且在它之后允许你使用一个映射函数。

【讨论】:

  • 问题不在于F[_] 的一般含义(您的答案是关于)。而是关于F[X[F]] 的直觉是什么,F 出现在X 周围和内部。
  • 哦,知道了。有时会发生这种情况。例如在我的代码中,F[X[F]] 出现在模块创建中。内部 F 描述了模块 X 的一种效果类型,外部 F 显示了一种构造效果。不需要内外使用相同的效果类型,可以使用F[X[G]],但为了简化,F和G认为是一样的,因为,其实它是现实生活中的一个IO .
  • 我认为最好添加一个关于为什么 X[F] 的文档。 F[X] 很好解释。例如,资源数据类型在它的定义typelevel.org/cats-effect/docs/std/resourcedef use[B](f: A =&gt; F[B]): F[B] 中解释得很好。但是,具有 F 效应的 Response 究竟应该做什么并不明显。响应需要执行的操作是副作用,因此表示为外部操作描述。这就是文档和其他任何地方都缺少的差距。 Resource 在某种程度上被定义为一个 IO Action,......
  • 过程中获取、释放和使用的动作。但是什么是概念上的响应,所以需要做 F 的东西?在文档中找不到该概念部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-28
  • 2022-12-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多