我已经阅读了有关monad的博客文章Reader
。
该帖子确实很棒,并详细说明了该主题,但我不明白为什么Reader
在这种情况下应该使用monad。
帖子说:假设有一个功能 query: String => Connection => ResultSet
def查询(sql:String)= conn:Connection => conn.createStatement.executeQuery(sql)
我们可以运行一些查询,如下所示:
def doSomeQueries(conn:Connection)= { val rs1 = query(“ SELECT COUNT(*)FROM Foo”)(conn) val rs2 = query(“ SELECT COUNT(*)FROM Bar”)(conn) rs1.getInt(1 )+ rs2.getInt(1) }
到目前为止还算不错,但是帖子建议改用Reader
monad:
类Reader [E,A](运行:E => A){ def map [B](f:A => B):Reader [E,B] = 新Reader(е=> f(run(е)) ) def flatMap [B](f:A => Reader [E,B]):Reader [E,B] = new Reader(е=> f(run(е))。run(е)) } val查询( sql:String):Reader [Connection,ResultSet] = new Reader(conn => conn.createStatement.executeQuery(sql)) def doSomeQueries(conn:Connection)= for { rs1 < -query (“ SELECT COUNT(*)FROM Foo “) rs2 <-查询(” SELECT COUNT(*)FROM Bar“”) }产生rs1.getInt(1)+ rs2.getInt(1)
好的,我知道我不需要connection
显式地遍历所有调用。所以呢 ?
为什么用Reader
monad解决方案比上一个更好?
更新:修复了def查询中的错字:=应该是=>此注释仅存在是因为SO坚持要求编辑长度必须至少为6个字符。所以我们开始。
最重要的原因是,读者单子让你建立复杂的计算组成。请考虑非阅读器示例中的以下行:
val rs1 = query("SELECT COUNT(*) FROM Foo")(conn)
我们conn
手动传递的事实意味着这一行本身并没有真正的意义-只有在doSomeQueries
提供方法的上下文中才能理解和推理这一行conn
。
通常这没什么问题-定义和使用局部变量(至少在val
某种意义上)显然没有错。但是,有时候,从独立的可组合片段中构建计算更为方便(或出于其他原因而被期望),读者monad可以提供帮助。
query("SELECT COUNT(*) FROM Foo")
在第二个示例中考虑。假设我们知道是什么query
,这是一个完全自包含的表达式-不需要像conn
这样的变量需要通过某些封闭范围来绑定。这意味着您可以更加自信地重用和重构,并且在进行推理时没有太多东西可以挂起来。
再次,这不再是必需的-它很大程度上取决于样式。如果您决定尝试一下(我建议您这样做),您可能会很快发展出一些偏好和直觉,以了解哪些地方使您的代码更易懂,哪些地方没有让您的代码更易懂。
另一个优点是您可以使用ReaderT
(或通过添加Reader
到其他堆栈中)来组合不同种类的“效果” 。不过,这一系列问题可能值得其提出自己的问题和答案。
最后一点:您可能希望自己doSomeQueries
看起来像这样:
def doSomeQueries: Reader[Connection, Int] = for {
rs1 <- query("SELECT COUNT(*) FROM Foo")
rs2 <- query("SELECT COUNT(*) FROM Bar")
} yield rs1.getInt(1) + rs2.getInt(1)
或者,如果这确实是该行的结尾:
def doSomeQueries(conn: Connection) = (
for {
rs1 <- query("SELECT COUNT(*) FROM Foo")
rs2 <- query("SELECT COUNT(*) FROM Bar")
} yield rs1.getInt(1) + rs2.getInt(1)
).run(conn)
在您当前的版本中,您实际上并未使用conn
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句