链功能不同 [英] Chain functions in different way

查看:73
本文介绍了链功能不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Scala函数具有以下链接方法:

Scala functions has following methods for chaining:

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

但是这种情况怎么写:

我有函数cleanUp(),必须始终在最后一步中调用它. 我还有很多其他功能,例如:

I have function cleanUp() which has to be called always as last step. And I have a bunch of other functions, like that:

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 can use code something like this:

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

如何以一种功能性的方式编写代码,因此链接将始终 隐式调用cleanUp函数作为最后一步?

How this can be written in a functional way, that chaining will always call cleanUp function implicitly as last step?

注意::我将其视为C ++中的析构函数的类似物.在最后一步cleanUp(fn1 andLater fn2 andLater fn3 andLater cleanUp)中必须调用某些链接(与完成此链接无关).直接编写cleanUp方法不正确的是,很有可能有人会错过此步骤,并且用户将被泄漏(将留在数据库中)

Note: I see it as analogue of destructor in C++. Some chaining (doesn't matter how this chain is done) fn1 andLater fn2 andLater fn3 have to call as last step cleanUp (fn1 andLater fn2 andLater fn3 andLater cleanUp). Wrong with directly writing cleanUp method is there is a big chance someone will miss this step and user will be leaked (will be stayed in database)

推荐答案

这是更高级的替代方法:

当您听到上下文"和步骤"时,会直接想到一种功能模式:Monads.汇总您自己的monad实例可以简化将有效步骤放在一起的用户端,同时提供保证在执行这些步骤后将清除上下文.

When you hear "context" and "steps", there's a functional pattern that directly comes to mind: Monads. Rolling up your own monad instance can simplify the user-side of putting valid steps together, while providing warranties that the context will be cleaned up after them.

在这里,我们将开发一种遵循该模式的"CleanableContext"构造.

Here, we are going to develop a "CleanableContext" construction that follows that pattern.

我们的构造基于最简单的monad,其唯一功能是保存值.我们将其称为Context

We base our construct on the most simple monad, one whose only function is to hold a value. We're going to call that 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  }
}

然后我们有一个CleanableContext,它具有清除"功能,能够自行清除":

Then we have a CleanableContext, which is capable of "cleaning up after itself" provided some 'cleanup' function:

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的对象,该对象将负责管理用户的创建和销毁.

And now, we have an object that's able to produce a cleanable UserContext that will take care of managing the creation and destruction of users.

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

假设我们已经定义了模型和业务功能:

Let's say that we have also our model and business functions already defined:

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 {}
}

用法

有了这种可重复使用的机器,我们的用户可以以简洁的方式表达我们的流程:

Usage

With that reusable machinery in place, our users can express our process in a succinct way:

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

该过程的结果仍被封装,可以使用value方法从Context中取出":

The result of the process is still encapsulated, and can be taken "out" from the Context with the value method:

val result = ctxResult.value

请注意,我们需要将业务操作封装到Context中,以用于此 monadic组合.还要注意,我们不需要手动创建或清除用于操作的用户.这已经为我们做好了.

Notice that we need to encapsulate the business operations into a Context to be used in this monadic composition. Note as well that we don't need to manually create nor cleanup the user used for the operations. That's taken care of for us.

此外,如果我们需要一种以上的托管资源,则可以使用此方法通过将不同的上下文组合在一起来管理其他资源.

Furthermore, if we needed more than one kind of managed resource, this method could be used to take care of managing additional resources by composing different contexts together.

有了这个,我只是想为这个问题提供另一个角度.管道更为复杂,但是它为用户通过组合创建安全的过程奠定了坚实的基础.

With this, I just want to provide another angle to the problem. The plumbing is more complex, but it creates a solid ground for users to create safe processes through composition.

这篇关于链功能不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆