Bla*_*son 23 monads functional-programming dependency-injection scala
我最近观看了一段关于你如何能够提出IO monad的视频,谈话是在scala中进行的.我实际上想知道函数返回IO [A]是什么意思.包含在IO对象中的lambda表达式是突变的内容,并且在某些时候它们必须被观察到更高的变化,我的意思是执行,以便发生某些事情.你是不是只是把问题推到了其他地方的树上?
我能看到的唯一好处是它允许延迟评估,因为如果你不调用unsafePerformIO操作,就不会出现副作用.此外,我猜该程序的其他部分可以使用/共享代码并在它想要副作用发生时拒绝.
我想知道这是不是全部?可测试性有任何优势吗?我假设不是你必须观察那种否定这种效果的效果.如果使用了traits/interfaces,则可以控制依赖项,但不能在这些依赖项上发生效果时控制.
我在代码中汇总了以下示例.
case class IO[+A](val ra: () => A){
def unsafePerformIO() : A = ra();
def map[B](f: A => B) : IO[B] = IO[B]( () => f(unsafePerformIO()))
def flatMap[B](f: A => IO[B]) : IO[B] = {
IO( () => f(ra()).unsafePerformIO())
}
}
case class Person(age: Int, name: String)
object Runner {
def getOlderPerson(p1: Person,p2:Person) : Person =
if(p1.age > p2.age)
p1
else
p2
def printOlder(p1: Person, p2: Person): IO[Unit] = {
IO( () => println(getOlderPerson(p1,p2)) ).map( x => println("Next") )
}
def printPerson(p:Person) = IO(() => {
println(p)
p
})
def main(args: Array[String]): Unit = {
val result = printPerson(Person(31,"Blair")).flatMap(a => printPerson(Person(23,"Tom"))
.flatMap(b => printOlder(a,b)))
result.unsafePerformIO()
}
}
Run Code Online (Sandbox Code Playgroud)
您可以看到效果如何推迟到主要我认为很酷.在从视频中感受到这一点后,我想出了这个.
我的实施是否正确,我的理解是否正确.
我也想知道是否应该将其与ValidationMonad结合使用,如ValidationMonad [IO [Person]]所以我们可以在发生异常时短路?想一想.
布莱尔
Set*_*sue 28
对于函数的类型签名来说,记录它是否具有副作用是有价值的.您的IO实现具有价值,因为它确实实现了这一点.它使您的代码更好地记录; 如果您重构代码以尽可能地将逻辑从逻辑中分离出来而不是逻辑,那么您已经使非IO涉及的函数更易于组合和更易于测试.您可以在没有显式IO类型的情况下进行相同的重构; 但使用显式类型意味着编译器可以帮助您进行分离.
但那只是一个开始.在您的问题的代码中,IO操作被编码为lambda,因此是不透明的; 除了运行它之外,你无法用IO动作做任何事情,并且在运行时它的效果是硬编码的.
这不是实现IO monad的唯一可行方法.
例如,我可能会使我的IO操作案例类扩展一个共同的特征.然后我就可以,例如,写一个运行的功能,并认为它是否返回正确的测试样的 IO行动.
在那些表示不同类型的IO操作的case类中,我可能不会包含操作时操作所执行操作的硬编码实现.相反,我可以使用类型类模式将其解耦.这将允许交换IO动作的不同实现.例如,我可能有一组与生产数据库通信的实现,另一组与模拟内存数据库通信以进行测试.
在Bjarnason&Chiusano的书" Scala中的函数式编程"一书的第13章("外部效应和I/O")中,对这些问题进行了很好的处理.特别参见13.2.2,"简单IO类型的优点和缺点".
更新(2015年12月):重新"交换IO行为的不同实现",现在越来越多的人使用"免费monad"来做这类事情; 参见John De Goes的博客文章" FP的现代建筑 ".
dre*_*xin 19
使用IO monad的好处是拥有纯粹的程序.你不会把副作用推到链条的上方,而是消除它们.如果你有一个不纯的功能,如下所示:
def greet {
println("What is your name?")
val name = readLine
println(s"Hello, $name!")
}
Run Code Online (Sandbox Code Playgroud)
您可以通过将其重写为以下内容来消除副作用:
def greet: IO[Unit] = for {
_ <- putStrLn("What is your name?")
name <- readLn
_ <- putStrLn(s"Hello, $name!")
} yield ()
Run Code Online (Sandbox Code Playgroud)
第二个功能是参考透明的.
一个很好的解释为什么使用IO monads导致纯程序可以在 RúnarBjarnason的scala.io 幻灯片中找到(视频可以在这里找到).
| 归档时间: |
|
| 查看次数: |
4764 次 |
| 最近记录: |