在scala中混合纯功能和副作用

时间:2017-07-26 12:28:02

标签: scala functional-programming

我有一个简单的api方法:

def addWishToBoard(wish: Wish, boardId: BoardId, userId: UserId): Option[Board]

如果boardIduserId匹配,则会增加希望。

电路板只存储在一个列表中:

private var boards = List.empty[Board]

我在这里使用var来模拟副作用。

实施代码:

def addWishToBoard(wish: Wish, boardId: BoardId, userId: UserId): Option[Board] = {
    val board = find(boardId)
      .filter(_.ownerId == userId)
      .map(board => board.copy(wishes = wish :: board.wishes.toList))

    board.foreach(b => boards = b :: boards)

    board
  }

有没有使用val for board实现这种副作用的功能方法?如何组合返回Unit的副作用和返回Option[Board]的纯函数?

3 个答案:

答案 0 :(得分:1)

如果我们设法制作电路板,您可以接受将被调用的功能。

def addWishToBoard(wish: Wish, boardId: BoardId, userId: UserId)(sideEffect: Board => Board = identity): Option[Board] = 
  find(boardId)
  .filter(_.ownerId == userId)
  .map(board => sideEffect(board.copy(wishes = wish :: board.wishes.toList)))

你也可以使用选项fold

def addWishToBoard(wish: Wish, boardId: BoardId, userId: UserId): Option[Board] = 
  find(boardId)
  .filter(_.ownerId == userId)
  .map(board =>board.copy(wishes = wish :: board.wishes.toList))
  .fold(None){v => 
    sideEffect(v) 
    Some(v)
  }

答案 1 :(得分:0)

目前尚不清楚你想在这里实现什么...... 有很多方法可以在不使用val的情况下编写它。例如:

boards
  .find(boardId)
  .filter(_.ownerId == userId)
  .map(board => board.copy(wishes = wish :: board.wishes.toList))
  .map(b => boards = b :: boards; b)

或者,或许,

def addBoard(b: Board) = {
  boards = b :: boards
  b
}


 boards
  .find(boardId)
  .filter(_.ownerId == userId)
  .map(board => board.copy(wishes = wish :: board.wishes.toList))
  .map(addBoard)

(顺便说一句,你继续使用这个功能将#34;版本和#34;添加到列表中,而不删除以前的副本 - 这似乎没有多大意义)。

答案 2 :(得分:-2)

scala> case class Board()
defined class Board

scala> val ob = Option.empty[Board]
ob: Option[Board] = None

然后定义以下

scala> implicit class AnyOps[A](val any: A) extends AnyVal {
     | def tap(effect: A => Unit): A = { effect(any); any }
     | }
defined class AnyOps

然后使用它

scala> ob.tap(println)
None
res9: Option[Board] = None

从功能编程的角度来看,副作用应该通过IO完成;类似的东西:

scala> import scalaz._; import Scalaz._; import effect._
import scalaz._
import Scalaz._
import effect._

scala>  OptionT(ob.point[IO]) >>! { b =>
 |        IO.putStrLn(b.toString).liftM[OptionT]
 |      }
res12: scalaz.OptionT[scalaz.effect.IO,Board] = 
OptionT(scalaz.effect.IO$$anon$7@3fd8d23d)

这里的返回类型(当然)封装了效果。但是,我强烈怀疑你的程序需要大量重写才能正常运行(即在任何地方都没有var),所以tap解决方案可能就是你要找的东西