这是this问题的后续措施。

这是我想要理解的代码(来自http://apocalisp.wordpress.com/2010/10/17/scalaz-tutorial-enumeration-based-io-with-iteratees/):

object io {
  sealed trait IO[A] {
    def unsafePerformIO: A
  }

  object IO {
    def apply[A](a: => A): IO[A] = new IO[A] {
      def unsafePerformIO = a
    }
  }

  implicit val IOMonad = new Monad[IO] {
    def pure[A](a: => A): IO[A] = IO(a)
    def bind[A,B](a: IO[A], f: A => IO[B]): IO[B] = IO {
      implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
                                        (x:A) => () => f(x).unsafePerformIO)()
    }
  }
}

此代码是这样使用的(我假设隐含了import io._)
def bufferFile(f: File) = IO {   new BufferedReader(new FileReader(f)) }

def closeReader(r: Reader) = IO {   r.close }

def bracket[A,B,C](init: IO[A], fin: A => IO[B], body: A => IO[C]): IO[C] = for { a <- init
      c <- body(a)
      _ <- fin(a) }   yield c

def enumFile[A](f: File, i: IterV[String, A]): IO[IterV[String, A]] =  bracket(bufferFile(f),
          closeReader(_:BufferedReader),
          enumReader(_:BufferedReader, i))

我现在正在尝试了解implicit val IOMonad的定义。这就是我的理解方式。这是scalaz.Monad,因此需要定义pure特性的bindscalaz.Monad抽象值。
pure接受一个值并将其转换为“容器”类型中包含的值。例如,它可能需要一个Int并返回一个List[Int]。这似乎很简单。
bind采用“容器”类型和将容器保存的类型映射为另一种类型的函数。返回的值是相同的容器类型,但是现在保留了新的类型。一个示例是获取List[Int]并使用将List[String]映射到Int的函数将其映射到Stringbindmap几乎一样吗?

我受困于bind的实现。这是代码:
def bind[A,B](a: IO[A], f: A => IO[B]): IO[B] = IO {
  implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
      (x:A) => () => f(x).unsafePerformIO)()
}

此定义使用IO[A]并将其映射到IO[B],该函数使用A并返回IO[B]。我猜想这样做,它必须使用flatMap来“展平”结果(对吗?)。
= IO { ... }
 = new IO[A] {
  def unsafePerformIO = implicitly[Monad[Function0]].bind(() => a.unsafePerformIO,
      (x:A) => () => f(x).unsafePerformIO)()
  }
}

我认为?
implicitly方法查找实现Monad[Function0]的隐式值(值,对吗?)。这个隐式定义从何而来?我猜这是来自implicit val IOMonad = new Monad[IO] {...}定义,但是我们现在处于该定义内,事情变得有点循环,我的大脑开始陷入无限循环:)

另外,bind(() => a.unsafePerformIO)的第一个参数似乎是一个不带任何参数并返回a.unsafePerformIO的函数。我应该怎么读? bind将容器类型作为其第一个参数,因此() => a.unsafePerformIO可能解析为容器类型吗?

最佳答案

IO[A]旨在表示返回ActionA,其中Action的结果可能取决于环境(含义是任何东西,变量的值,文件系统,系统时间...),并且该Action的执行也可能会修改环境。实际上, Action 的标量类型为Function0Function0[A]在调用时返回A,当然可以允许它依赖并修改环境。 IO是另一个名称的Function0,但它旨在将那些依赖于环境的Function0与其他依赖于环境的Function0(实际上是纯值)区分开(标记?)(如果您说f是一个总是返回相同值的function [A],值,没有任何副作用,f和它的结果之间没有太大区别)。确切地说,标记为IO的功能必须具有副作用的并不多。就是那些没有这样标记的人必须没有。但是请注意,将不纯函数包装在IO中是完全自愿的,但是,当您获得Function0是纯净的时,您将无法保证。 在scala 中,使用IO当然不是主导风格。



完全正确,但是“容器”可能意味着很多东西。由纯净返回的那一个必须尽可能轻,它必须是没有区别的。列表的要点是它们可以具有任意数量的值。由pure返回的一个必须有一个。 IO的要点在于它取决于并影响环境。纯属返回的人绝对不能做这种事情。所以实际上是包装在Function0中的纯() => a IO



并非如此,bind与flatMap相同。在编写时,map将接收从IntString的函数,但是在这里,您具有从IntList[String]的函数。

现在,暂时忘记IO,并考虑bind/flatMap对于Action(即Function0)意味着什么。
让我们

val askUserForLineNumber: () => Int = {...}
val readingLineAt: Int => Function0[String] = {i: Int  => () => ...}

现在,如果我们必须像bind/flatMap一样组合这些项目,以获得返回String的 Action ,则必须清楚:向读者询问行号,读取该行并返回它。那会是
val askForLineNumberAndReadIt= () => {
  val lineNumber : Int = askUserForLineNumber()
  val readingRequiredLine: Function0[String] = readingLineAt(line)
  val lineContent= readingRequiredLine()
  lineContent
}

更一般地
def bind[A,B](a: Function0[A], f: A => Function0[B]) = () => {
  val value = a()
  val nextAction = f(value)
  val result = nextAction()
  result
}

和更短:
def bind[A,B](a: Function0[A], f: A => Function0[B])
  = () => {f(a())()}

所以我们知道bind必须是什么Function0pure也很清楚。我们可以做的
object ActionMonad extends Monad[Function0] {
  def pure[A](a: => A) = () => a
  def bind[A,B](a: () => A, f: A => Function0[B]) = () => f(a())()
}

现在,IO是变相的Function0。不仅仅是做a(),,我们还必须做a.unsafePerformIO。并定义一个,而不是() => body,我们编写IO {body}所以可能有
object IOMonad extends Monad[IO] {
  def pure[A](a: => A) = IO {a}
  def bind[A,B](a: IO[A], f: A => IO[B]) = IO {f(a.unsafePerformIO).unsafePerformIO}
}

在我看来,这已经足够了。但实际上,它重复了ActionMonad。您所引用的代码中的要点是避免这种情况,并重复使用对Function0所做的操作。从IOFunction0(带有() => io.unsafePerformIo)以及从Function0IO(带有IO { action() })很容易。如果您有f:A => IO [B],则也可以将其更改为f: A => Function0[B],只需将IO组成为Function0转换,即(x: A) => f(x).unsafePerformIO即可。

IO绑定(bind)中发生的情况是:
  • () => a.unsafePerformIO:将a转换为Function0
  • (x:A) => () => f(x).unsafePerformIO):将f转换为A => Function0[B]
  • 隐式地[Monad [Function0]]:获取Function0的默认monad,与
  • 上方的ActionMonad相同
  • bind(...):将bind monad的Function0应用于刚刚转换为Function0
  • 的参数af
  • 随附的IO{...}:将结果转换回IO

  • (不确定我是否很喜欢)

    10-08 03:27