在函数式编程中处理增量数据建模变化 [英] Handling incremental Data Modeling Changes in Functional Programming

查看:17
本文介绍了在函数式编程中处理增量数据建模变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为开发人员,我在工作中必须解决的大多数问题都与数据建模有关.例如,在 OOP Web 应用程序世界中,我经常不得不更改对象中的数据属性以满足新的需求.

Most of the problems I have to solve in my job as a developer have to do with data modeling. For example in a OOP Web Application world I often have to change the data properties that are in a object to meet new requirements.

如果幸运的话,我什至不需要以编程方式添加新的行为"代码(函数、方法).相反,我可以通过注释属性 (Java) 来声明性地添加验证甚至 UI 选项.

If I'm lucky I don't even need to programmatically add new "behavior" code (functions,methods). Instead I can declarative add validation and even UI options by annotating the property (Java).

在函数式编程中,由于模式匹配和数据构造函数(Haskell,ML),添加新数据属性似乎需要大量代码更改.

In Functional Programming it seems that adding new data properties requires lots of code changes because of pattern matching and data constructors (Haskell, ML).

我如何尽量减少这个问题?

How do I minimize this problem?

这似乎是一个公认的问题,因为 Xavier Leroy 在第 24 页很好地说明对象和类与模块"- 总而言之,对于那些没有 PostScript 查看器的人来说,它基本上说 FP 语言比 OOP 语言更适合在数据对象上添加新行为,但 OOP 语言更适合添加新数据对象/属性.

This seems to be a recognized problem as Xavier Leroy states nicely on page 24 of "Objects and Classes vs. Modules" - To summarize for those that don't have a PostScript viewer it basically says FP languages are better than OOP languages for adding new behavior over data objects but OOP languages are better for adding new data objects/properties.

是否在 FP 语言中使用了任何设计模式来帮助缓解这个问题?

我已阅读 Phillip Wadler 的使用 Monads 的建议 来帮助解决这个模块化问题,但我不确定我是如何理解的?

I have read Phillip Wadler's recommendation of using Monads to help this modularity problem but I'm not sure I understand how?

推荐答案

正如 Darius Bacon 所指出的,这本质上是表达问题,一个长期存在的问题,没有普遍接受的解决方案.然而,缺乏两全其美的方法并不能阻止我们有时想要走一种方式或另一种方式.现在,您要求一个函数式语言的设计模式",那么让我们试一试吧.下面的示例是用 Haskell 编写的,但不一定适合 Haskell(或任何其他语言).

As Darius Bacon noted, this is essentially the expression problem, a long-standing issue with no universally accepted solution. The lack of a best-of-both-worlds approach doesn't stop us from sometimes wanting to go one way or the other, though. Now, you asked for a "design pattern for functional languages", so let's take a shot at it. The example that follows is written in Haskell, but isn't necessarily idiomatic for Haskell (or any other language).

首先,快速回顾一下表达问题".考虑以下代数数据类型:

First, a quick review of the "expression problem". Consider the following algebraic data type:

data Expr a = Lit a | Sum (Expr a) (Expr a)

exprEval (Lit x) = x
exprEval (Sum x y) = exprEval x + exprEval y

exprShow (Lit x) = show x
exprShow (Sum x y) = unwords ["(", exprShow x, " + ", exprShow y, ")"]

这表示简单的数学表达式,仅包含文字值和加法.使用我们这里的函数,我们可以获取一个表达式并对其求值,或者将其显示为 String.现在,假设我们要添加一个新函数——比如说,将一个函数映射到所有文字值上:

This represents simple mathematical expressions, containing only literal values and addition. With the functions we have here, we can take an expression and evaluate it, or show it as a String. Now, say we want to add a new function--say, map a function over all the literal values:

exprMap f (Lit x) = Lit (f x)
exprMap f (Sum x y) = Sum (exprMap f x) (exprMap f y)

简单!我们可以整天不停地编写函数而不会出汗!代数数据类型很棒!

Easy! We can keep writing functions all day without breaking a sweat! Algebraic data types are awesome!

事实上,它们太棒了,我们想让我们的表情类型更具表现力.让我们扩展它以支持乘法,我们只会……呃……哦,天哪,那会很尴尬,不是吗?我们必须修改我们刚刚编写的每个函数.绝望!

In fact, they're so awesome, we want to make our expression type more, errh, expressive. Let's extend it to support multiplication, we'll just... uhh... oh dear, that's going to be awkward, isn't it? We have to modify every function we just wrote. Despair!

事实上,也许扩展表达式本身比添加使用它们的函数更有趣.因此,假设我们愿意在另一个方向上进行权衡.我们该怎么做?

In fact, maybe extending the expressions themselves is more interesting than adding functions that use them. So, let's say we're willing to make the trade-off in the other direction. How might we do that?

好吧,半途而废是没有意义的.让我们颠倒一切并反转整个程序.这是什么意思?嗯,这就是函数式编程,还有什么比高阶函数更函数式的呢?我们要做的是将表示表达式值的数据类型替换为表示对表达式的操作.我们不需要选择构造函数,而是需要记录所有可能的操作,如下所示:

Well, no sense doing things halfway. Let's up-end everything and invert the whole program. What does that mean? Well, this is functional programming, and what's more functional than higher-order functions? What we'll do is replace the data type representing expression values with one representing actions on the expression. Instead of choosing a constructor we'll need a record of all possible actions, something like this:

data Actions a = Actions {
    actEval :: a,
    actMap  :: (a -> a) -> Actions a }

那么我们如何创建一个没有数据类型的表达式呢?好吧,我们的函数现在是数据,所以我想我们的数据需要是函数.我们将使用常规函数创建构造函数",返回操作记录:

So how do we create an expression without a data type? Well, our functions are data now, so I guess our data needs to be functions. We'll make "constructors" using regular functions, returning a record of actions:

mkLit x = Actions x (f -> mkLit (f x))

mkSum x y = Actions 
    (actEval x + actEval y) 
    (f -> mkSum (actMap x f) (actMap y f))

我们现在可以更轻松地添加乘法吗?当然可以!

Can we add multiplication more easily now? Sure can!

mkProd x y = Actions 
    (actEval x * actEval y) 
    (f -> mkProd (actMap x f) (actMap y f))

哦,但是等等——我们之前忘记添加一个 actShow 动作,让我们添加它,我们就......呃,好吧.

Oh, but wait--we forgot to add an actShow action earlier, let's add that in, we'll just... errh, well.

无论如何,使用这两种不同的风格会是什么样子?

At any rate, what does it look like to use the two different styles?

expr1plus1 = Sum (Lit 1) (Lit 1)
action1plus1 = mkSum (mkLit 1) (mkLit 1)
action1times1 = mkProd (mkLit 1) (mkLit 1)

几乎相同,当您不扩展它们时.

Pretty much the same, when you're not extending them.

作为一个有趣的旁注,请考虑在actions"样式中,表达式中的实际值完全隐藏——actEval 字段仅承诺给出我们提供正确类型的东西,它如何提供它是它自己的事情.由于惰性求值,该字段的内容甚至可能是一个复杂的计算,仅在需要时执行.Actions a 值对于外部检查是完全不透明的,它只向外部世界展示定义的动作.

As an interesting side note, consider that in the "actions" style, the actual values in the expression are completely hidden--the actEval field only promises to give us something of the correct type, how it provides it is its own business. Thanks to lazy evaluation, the contents of the field may even be an elaborate computation, performed only on demand. An Actions a value is completely opaque to external inspection, presenting only the defined actions to the outside world.

这种编程风格——用一组动作"代替简单的数据,同时将实际的实现细节隐藏在一个黑盒子里,使用类似构造函数的函数来构建新的数据位,能够交换非常不同的值"使用相同的一组动作"等等——很有趣.大概有个名字,但我好像不太记得了...

This programming style--replacing simple data with a bundle of "actions" while hiding the actual implementation details in a black box, using constructor-like functions to build new bits of data, being able to interchange very different "values" with the same set of "actions", and so on--is interesting. There's probably a name for it, but I can't quite seem to recall...

这篇关于在函数式编程中处理增量数据建模变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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