The chain functions differently.

Scala functions have the following chaining methods:

 fn1.andThen(fn2)
 fn1.compose(fn2)

But how can I write this case:

I have a function cleanUp()that should always be called as the last step. And I have a bunch of other functions, for example:

class Helper {
  private[this] val umsHelper = new UmsHelper()
  private[this] val user = umsHelper.createUser()
  def cleanUp = ... // delete user/ and other entities

  def prepareModel(model: TestModel) = {
    // create model on behalf of the user
  }

  def commitModel() = {
    // commit model on behalf of the user
  }
}

And some external code might use code like this:

val help = new Helper()
help.prepareModel()
help.commitModel()
// last step should be called implicitly cleanUp

How can this be written functionally, this chain will always call the function cleanUpimplicitly as the last step?

: C++. ( , ) fn1 andLater fn2 andLater fn3 cleanUp (fn1 andLater fn2 andLater fn3 andLater cleanUp). cleanUp, , - ( )

+6
3

:

"" "", , : Monads. , .

"CleanableContext", .

, . Context

trait Context[A] { self => 
  def flatMap[B](f:A => Context[B]): Context[B] = f(value)
  def map[B](f:A => B): Context[B] = flatMap(f andThen ((b:B) => Context(b)))
  def value: A
}

object Context {
  def apply[T](x:T): Context[T] = new Context[T] { val value = x  }
}

a CleanableContext, " ", "" :

trait CleanableContext[A] extends Context[A] {
  override def flatMap[B](f:A => Context[B]): Context[B] = {
    val res = super.flatMap(f)
    cleanup
    res
  }
  def cleanup: Unit
}

, UserContext, .

object UserContext {
  def apply(x:UserManager): CleanableContext[User] = new CleanableContext[User] {
    val value = x.createUser
    def cleanup = x.deleteUser(value)
  }
}

, -:

trait Model
trait TestModel extends Model
trait ValidatedModel extends Model
trait OpResult
object Ops {
  def prepareModel(user: User, model: TestModel): Model = new Model {}

  def validateModel(model: Model): ValidatedModel = new ValidatedModel {}

  def commitModel(user: User, vmodel: ValidatedModel): OpResult = new OpResult {}
}

, :

import Ops._
val ctxResult = for {
  user <- UserContext(new UserManager{})
  validatedModel <- Context(Ops.prepareModel(user, testModel)).map(Ops.validateModel)
  commitResult <- Context(commitModel(user, validatedModel))
} yield commitResult

"" Context value:

val result = ctxResult.value

, - Context, . , , . .

, , .

. , .

+5

, ​​ " " . "" .

, API , . :

: ( , )

trait User
trait Model
trait TestModel extends Model
trait ValidatedModel extends Model
trait OpResult
// Some external resource provider
trait Ums {
  def createUser: User
  def deleteUser(user: User)
}

.

class Context {
  private val ums = new Ums{ 
    def createUser = new User{} 
    def deleteUser(user: User) = ???
  } 

  def withUserDo[T](ops: User => T):T = {
    val user = ums.createUser
    val result = ops(user)
    ums.deleteUser(user)
    result
  }
}

() . .

object Context {
  def prepareModel(model: TestModel): User => Model = ???

  val validateModel: Model => ValidatedModel = ???

  val commitModel: ValidatedModel => OpResult = ???
}

, , :

val ctx  = new Context 
val testModel = new TestModel{}

val result = ctx.withUserDo{ user => 
  val preparedModel = prepareModel(testModel)(user)
  val validatedModel = validateModel(preparedModel)
  commitModel(validatedModel)
}

, , :

val result = ctx.withUserDo{
  prepareModel(testModel) andThen validateModel andThen commitModel
}
+3

autoClean, cleanUp .

HelperStuff, .

Inside the Helper object, create a private HelperStuff implementation, and then get the autoClean method method that does the job of keeping the Helper instance private and secure from rouge users.

Helper.autoClean { helperStuff =>

  //write all your code here. clean up will happen automatically
  helper.foo()
  helper.commitModel()

}

Here is the autoClean function for you

trait HelperStuff {
 def foo(): Unit
 def commitModel: Unit
 def cleanUp(): Unit
}

object Helper {

  private class Helper extends HelperStuff {
   def foo(): Unit = println("foo")
   def cleanUp(): Unit = println("cleaning done")
  }

  private val helper = new Helper()

  def autoClean[T](code: HelperStuff => T): T = {
    val result = code(helper)
    helper.cleanUp()
    result
  }

}
+1
source

Source: https://habr.com/ru/post/1659502/


All Articles