scala 给定`()=> IO[T]`,我能得到`IO[()=> T]`吗?

dgsult0t  于 5个月前  发布在  Scala
关注(0)|答案(2)|浏览(72)

我有一件物品

object Producer {
      def apply() :IO[Foo] = ???
    }

字符串
我正在编写(平面Map)几个返回IO的函数。其中一个隐藏了一个Ref,并公开了与之兼容的update方法:

object State {
      def update(update: Bar => Bar) :IO[Bar] = ???
    }


这一个我控制,虽然引入重大的变化将导致一个大的重构。
问题:

for {
      bar <- State.update { arg => ??? }
    } yield ()


在某些情况下,但不是所有情况下,我需要一个Foo在传递给State.update的lambda中生成一个新的Bar。是否可以在不总是将foo <- Producer作为第一步的情况下完成?

0tdrvxhp

0tdrvxhp1#

IO[A]的一种思考方式是它是() => A(或() => Future[A])。
所以() => IO[T]IO[() => T]的想法是一样的,只是有不同的 * 人体工程学 *(和不同的方式来评估里面的东西)。所以你应该能够从一个进入另一个,然后返回,例如:

val a: () => IO[A] = ...
val b: IO[() => A] = IO { () => a.runUnsafeSync }

val c: IO[() => A] = ...
val d: () => IO[A] = () => c.map(_())

字符串
然而,魔鬼在细节中:

  • 这样的runUnsafeSync不会将取消从外部IO传播到内部IO
  • 类似地,它不会在IOLocal中传播状态更新

所以用这种方式进行防弹转换会相当困难
而且几乎从来没有它将是有用的,因为与人体工程学有一个公约:

  • IO[A]表明有副作用(或至少有可能有副作用)
  • X => non-IO表示纯计算-取X,然后返回sth,没有变化,也不与外部世界对话
  • () =>没有提供新的信息,所以这样的纯函数必须是常量(它可能基本上是一个惰性值)

所以如果我们遵循惯例,我们可以通过执行以下操作将fa: () => IO[A]转换为IO[() => A]

IO.defer {
  fa()
}.map { a =>
  // a is a constant value, so function we created is constant
  // and everything that obtains it through e.g. map/flatMap
  // would get the same value no matter how many times it would be called.

  // However, evaluation whole IO twice could result in 2 functions
  // returning different constant values.
  () => a
}


这应该会使转换变得微不足道,但这也意味着所有这些() =>IO[A]的上下文中都是毫无意义的。除非你需要这样做来使你的代码适应一些需要() => F[A]F[() => A]的外部接口,否则它只是噪音。

tyu7yeag

tyu7yeag2#

你的问题是:我们能从A => IO[B]IO[A => B]吗?从概念上考虑一下。
假设我们有一个函数String => IO[Int],它接受用户名,从数据库中读取用户数据,并返回用户的年龄( Package 在IO中,因为我们有一个数据库调用)。然后,您正在寻找一个高阶函数,它可以将name -> age函数转换为一个有效的值IO[String => Int]。这怎么可能工作呢?String => Int的实现可能是什么?以下两种情况都不存在:

  • 使用函数而不是用户名查找数据库
  • 一些其他的实现,可以获得纯粹的年龄,而不做数据库查找

这是两个完全不同的东西。这里有一个关于它们区别的提示:

  • Monad的flatMap接受一个A => F[B]类型的函数,或者在你的例子中是A => IO[B]
  • 应用函子的apply接受一个F[A => B]类型的函数,或者在你的例子中是IO[A => B]
  • 适用性是较弱的抽象。

像你建议的那样使用for-comp是正确的方法。

for {
  producedBar <- Producer.apply
  bar <- State.update { arg => producedBar }
} yield ()

字符串
或者写得不一样

Producer.apply().flatMap(producedBar => State.update(arg => producedBar))


它清楚地表明,状态更新取决于另一个函数,该函数指定了新的更新值。或者在我们前面的例子中-它表明您需要首先获得您的数字,然后才能将您的“三重”函数应用于它。
请注意,这里只讨论了操作的 * 顺序 * 或 * 依赖链 *。它没有说明它们在真实的世界中的实际执行情况;我们只是描述了懒惰的副作用程序的算法。(不像使用例如急切评估的Future,在这种情况下,您的关注将更加合理)
你能解释一下这有什么问题吗?

相关问题