计算表达式是面向方面编程的替代方法吗?

Are Computation Expressions an alternative approach to Aspect-oriented Programming?

计算表达式是面向方面编程的替代方法吗?

这是 F# 用于管理横切关注点的解决方案吗?

看了下面这篇文章,不由想起了AOP(即面向切面编程)

article 中,作者提供了一个处理日志记录的计算表达式示例,但在不混淆业务逻辑的主要意图的情况下隔离了代码的实际日志记录方面。

我的想法准确吗?

是的,除其他外,monad 是处理横切关注点的一种很好的(惯用的)方法。 monad 是一个更通用的概念,但它们的用途之一是在系统中建模 effects

在 FP 术语中,effect 这个词通常表示副作用。许多横切关注点,如日志记录、审计、性能监控、缓存和计量(根据定义)都有副作用。由于它们涉及到辅助有状态资源的 'writing' 数据,因此可以使用 State 或 Writer monads 对它们进行建模。

通常可以使用 Reader monad(或者可能是 State monad)来解决身份验证、授权和验证等其他交叉问题。

F# 计算表达式为 monadic 组合器(returnbind 本质上提供了语法糖,与Haskell 的 do 表示法。然而,与 Haskell 相反,您必须自己为 monad 定义计算表达式构建器,除了少数已经内置到语言中的构建器(asyncseq)。

使用 monad 来解决横切关注点是一种不同于 AOP 的方法。

在面向对象编程中,有两种根本不同的 AOP:

  • 装饰器
  • 编译时编织,其中典型的 .NET 示例是 PostSharp

正如我在 my book, I consider compile-time weaving to be a heavy-handed and inflexible way to address the problem. Using a Decorator 中所解释的那样,这是实现相同目标的一种更优雅、更灵活的方法。

我发表该声明的动机是我更喜欢关注点分离

如果您在 OOP 中使用编译时编织,您通常会有这样的代码(C# 示例):

[Log]
public void SaveOrder(Order order)
{
    // Implementation goes here...
}

这里的问题是,虽然关注点是分开的,但它们仍然 耦合 。你不能决定SaveOrder登录,除非你重新编译。

使用计算表达式有点像:

log { return saveOrder order }

同样,横切关注点与实现一起编译

然而,相似之处在于可以使用单子组合器将关注点组合在一起,这在编译时织入中不容易实现。在 OOP 中,您不能轻易采用 记录的方法,并且 'magically' 在将对象组合在一起时使其记录。

另一方面,使用 monad,您可以 pure function and compose it with a monadic context. Such later-bound logging is structurally equivalent to using Decorators

因此,我认为将计算表达式用于横切关注点是合适的,只要您将此类代码推迟到应用程序的入口点(其 Composition Root)即可。