Currying functional parameters / composition is the closest equivalent. However, it's a mistake to even ask this question, because patterns exist to compensate for weaknesses in the host language.
If C++/Java/C#/any other practically identical language had a decoration feature built into the language, you wouldn't think of it as a pattern. It just so happens that "patterns" are patterns for structuring systems in early-bound imperative objective-oriented languages, usually without autoboxing, and with relatively thin protocols for the root class.
Edit: Also a lot of these are studied as patterns in these languages because there's no obvious built in higher order functions, higher order typing, and the type systems are relatively useless. Clearly, that's not a universal problem with these languages, but at the time these patterns started to be codified those issues were present.
; define a collection with some missing (nil) values
(def nums [1 2 3 4 nil 6 7 nil 9])
; helper higher order function to "wrap" an existing function with an alternative implementation to be used when a certain predicate matches the value
(defn wrap-alternate-handler [pred alternate-f f]
(fn [x]
(if (pred x)
(alternate-f x)
(f x))))
; create a "decorated" increment function that handles nils differently
(def wrapped-inc
(wrap-alternate-handler nil? (constantly "Nil found!") inc))
(map wrapped-inc nums)
=> (2 3 4 5 "Nil found!" 7 8 "Nil found!" 10)
You can "decorate" functions by wrapping them inside other functions, typically using some form of higher order function to perform the wrapping.
Simple example in Clojure:
; define a collection with some missing (nil) values
(def nums [1 2 3 4 nil 6 7 nil 9])
; helper higher order function to "wrap" an existing function with an alternative implementation to be used when a certain predicate matches the value
(defn wrap-alternate-handler [pred alternate-f f]
(fn [x]
(if (pred x)
(alternate-f x)
(f x))))
; create a "decorated" increment function that handles nils differently
(def wrapped-inc
(wrap-alternate-handler nil? (constantly "Nil found!") inc))
(map wrapped-inc nums)
=> (2 3 4 5 "Nil found!" 7 8 "Nil found!" 10)
This technique is used extensively in functional libraries. A good example is wrapping web request handlers using Ring middleware - the linked example wraps parameter handling for html request around any existing handler.
class Window w where
draw :: w -> IO ()
description :: w -> String
data VerticalScrollingWindow w = VerticalScrollingWindow w
instance Window w => Window (VerticalScrollingWindow w) where
draw (VerticalScrollingWindow w)
= draw w >> drawVerticalScrollBar w -- `drawVerticalScrollBar` defined elsewhere
description (VerticalScrollingWindow w)
= description w ++ ", including vertical scrollbars"
Something like this:
class Window w where
draw :: w -> IO ()
description :: w -> String
data VerticalScrollingWindow w = VerticalScrollingWindow w
instance Window w => Window (VerticalScrollingWindow w) where
draw (VerticalScrollingWindow w)
= draw w >> drawVerticalScrollBar w -- `drawVerticalScrollBar` defined elsewhere
description (VerticalScrollingWindow w)
= description w ++ ", including vertical scrollbars"
class WindowDecorator w where
decorate :: (Window a) => a -> w a
unDecorate :: (Window a) => w a -> a
drawDecorated :: w a -> IO ()
drawDecorated = draw . unDecorate
decoratedDescription :: w a -> String
decoratedDescription = description . unDecorate
instance (WindowDecorator w) => Window w where
draw = drawDecorated
description = decoratedDescription
data VerticalScrollWindow w = VerticalScrollWindow w
instance WindowDecorator VerticalScrollWindow where
decorate = VerticalScrollWindow
unDecorate (VerticalScrollWindow w ) = w
drawDecorated (VerticalScrollWindow w ) = verticalScrollDraw >> draw w
data HorizontalScrollWindow w = HorizontalScrollWindow w
instance WindowDecorator HorizontalScrollWindow where
decorate = HorizontalScrollWindow
unDecorate (HorizontalScrollWindow w .. ) = w
drawDecorated (HorizontalScrollWindow w ..) = horizontalScrollDraw >> draw w
In Haskell, this OO pattern translates pretty much directly, you only need a dictionary. Note that a direct translation is not actually a good idea. Trying to force a OO concept into Haskell is kind of backwords, but you asked for it so here it is.
The Window Interface
Haskell has classes, which has all the functionality of an Interface and then some. So we will use the following Haskell class:
class Window w where
draw :: w -> IO ()
description :: w -> String
The Abstract WindowDecorator class
This one is a bit more tricky since Haskell has no concept of inheritance. Usually we would not provide this type at all and let the decorators implement Window directly, but lets follow the example completely. In this example, a WindowDecorator is a window with a constructor taking a window, lets augment this with a function giving the decorated window.
class WindowDecorator w where
decorate :: (Window a) => a -> w a
unDecorate :: (Window a) => w a -> a
drawDecorated :: w a -> IO ()
drawDecorated = draw . unDecorate
decoratedDescription :: w a -> String
decoratedDescription = description . unDecorate
instance (WindowDecorator w) => Window w where
draw = drawDecorated
description = decoratedDescription
Note that we provide a default implementation of Window, it can be replaced, and all instances of WindowDecorator will be a Window.
The decorators
Making decorators can then be done as follows:
data VerticalScrollWindow w = VerticalScrollWindow w
instance WindowDecorator VerticalScrollWindow where
decorate = VerticalScrollWindow
unDecorate (VerticalScrollWindow w ) = w
drawDecorated (VerticalScrollWindow w ) = verticalScrollDraw >> draw w
data HorizontalScrollWindow w = HorizontalScrollWindow w
instance WindowDecorator HorizontalScrollWindow where
decorate = HorizontalScrollWindow
unDecorate (HorizontalScrollWindow w .. ) = w
drawDecorated (HorizontalScrollWindow w ..) = horizontalScrollDraw >> draw w
Ok, first of all lets try to find all the main components of decorator pattern in respect to OOP. This pattern is basically used to decorate i.e add additional features to an existing object. This is the simplest possible definition of this pattern. Now if we try to find the same components that are there in this definition in the world of FP, we can say that additional features = new functions and object are not there in FP, rather FP has what you call data or data structure in various forms. So in FP terms this patterns becomes, adding additional functions for FP data structures or enhancing existing function with some additional features.
The Joy of Clojure talks about this very issue in chapter 13.3, "A lack of design patterns". According to the JoC, the -> and ->> macros are somewhat analogous to the decorator pattern.
type Window = {Description: string}
type HorizontalScroll = {HPosition: int}
type VerticalScroll = {VPosition: int}
type Feature =
| HorizontalScroll of HorizontalScroll
| VerticalScroll of VerticalScroll
let drawWithFeatures w (f: Feature) =
match f with
| HorizontalScroll h -> {Description= w.Description + "Horizontal"}
| VerticalScroll v -> {Description= w.Description + "Vertical"}
type FeatureTwo = Red of int| Blue of int | Green of int | Feature of Feature
let drawWithMoreFeatures (w: Window) (ft:FeatureTwo)=
match ft with
| Red x -> {Description = w.Description + "Red"}
| Green x -> {Description = w.Description + "Green"}
| Blue x -> {Description = w.Description + "Blue"}
| Feature f -> drawWithFeatures w f
let window = {Description = "Window title"}
let horizontalBar = HorizontalScroll {HPosition = 10}
let verticalBar = VerticalScroll {VPosition = 20}
[Red 7; Green 3; Blue 2; Feature horizontalBar; Feature verticalBar] |> List.fold drawWithMoreFeatures window
type Window = {Description: string}
type HorizontalScroll = {HPosition: int}
type VerticalScroll = {VPosition: int}
type Feature =
| HorizontalScroll of HorizontalScroll
| VerticalScroll of VerticalScroll
let drawWithFeatures w (f: Feature) =
match f with
| HorizontalScroll h -> {Description= w.Description + "Horizontal"}
| VerticalScroll v -> {Description= w.Description + "Vertical"}
type FeatureTwo = Red of int| Blue of int | Green of int | Feature of Feature
let drawWithMoreFeatures (w: Window) (ft:FeatureTwo)=
match ft with
| Red x -> {Description = w.Description + "Red"}
| Green x -> {Description = w.Description + "Green"}
| Blue x -> {Description = w.Description + "Blue"}
| Feature f -> drawWithFeatures w f
let window = {Description = "Window title"}
let horizontalBar = HorizontalScroll {HPosition = 10}
let verticalBar = VerticalScroll {VPosition = 20}
[Red 7; Green 3; Blue 2; Feature horizontalBar; Feature verticalBar] |> List.fold drawWithMoreFeatures window
F# attempt
This is my attempt at creating something sensible in F# since you asked for many examples. I'm a little rusty so hopefully nobody will shame me :P. The decorator basically requires two parts, new behaviors and new data. New behaviors are exceedingly easy in functional languages as they are "just another function", since functions are inherently decoupled from objects. New data is actually similarly easy and there's several ways you can achieve this, the simplest being a tuple. You can see I've created a new datatype that is a superset of the previous, and I've called the existing function for that existing behavior. So we have our old data and old behavior still respected, but we also have new behavior and new data.
发布评论
评论(10)
在函数式编程中,您可以将给定函数包装在新函数中。
给出一个与您的问题中引用的类似的人为的 Clojure 示例:
我的原始绘图函数:
我的函数包装器:
这些返回一个新函数,可以在使用原始绘图函数的任何地方使用,但也可以绘制滚动条。
In functional programming, you would wrap a given function in a new function.
To give a contrived Clojure example similar to the one quoted in your question:
My original drawing function:
My function wrappers:
These return a new function that can be used anywhere the original drawing function is used, but also draw the scrollbars.
柯里化函数参数/组合是最接近的等价物。然而,即使问这个问题也是错误的,因为模式的存在是为了弥补宿主语言的弱点。
如果 C++/Java/C#/任何其他几乎相同的语言具有内置于该语言中的装饰功能,您不会将其视为一种模式。碰巧的是,“模式”是用于在早期绑定的命令式面向目标语言中构建系统的模式,通常没有自动装箱,并且根类具有相对较薄的协议。
编辑:此外,其中很多都是作为这些语言中的模式进行研究的,因为没有明显的内置高阶函数、高阶类型,并且类型系统相对无用。显然,这不是这些语言的普遍问题,但在这些模式开始编纂时,这些问题就已经存在了。
Currying functional parameters / composition is the closest equivalent. However, it's a mistake to even ask this question, because patterns exist to compensate for weaknesses in the host language.
If C++/Java/C#/any other practically identical language had a decoration feature built into the language, you wouldn't think of it as a pattern. It just so happens that "patterns" are patterns for structuring systems in early-bound imperative objective-oriented languages, usually without autoboxing, and with relatively thin protocols for the root class.
Edit: Also a lot of these are studied as patterns in these languages because there's no obvious built in higher order functions, higher order typing, and the type systems are relatively useless. Clearly, that's not a universal problem with these languages, but at the time these patterns started to be codified those issues were present.
您可以通过将函数包装在其他函数中来“装饰”函数,通常使用某种形式的高阶函数来执行包装。
Clojure 中的简单示例:
该技术在函数库中广泛使用。一个很好的例子是 使用 Ring 中间件包装 Web 请求处理程序 - 链接的示例将 html 请求的参数处理包装在任何现有处理程序周围。
You can "decorate" functions by wrapping them inside other functions, typically using some form of higher order function to perform the wrapping.
Simple example in Clojure:
This technique is used extensively in functional libraries. A good example is wrapping web request handlers using Ring middleware - the linked example wraps parameter handling for html request around any existing handler.
像这样的东西:
Something like this:
在 Haskell 中,这种 OO 模式几乎可以直接翻译,你只需要一本字典。请注意,直接翻译实际上并不是一个好主意。试图将面向对象的概念强加到 Haskell 中是一种隐晦的说法,但你要求它,所以它就在这里。
窗口接口
Haskell 有类,它具有接口的所有功能,甚至还有一些功能。因此,我们将使用以下 Haskell 类:
抽象 WindowDecorator 类
由于 Haskell 没有继承的概念,因此这个类有点棘手。通常我们根本不会提供这种类型,而是让装饰器直接实现
Window
,但让我们完全遵循示例。在此示例中,WindowDecorator
是一个带有带有窗口的构造函数的窗口,让我们用提供装饰窗口的函数来扩充它。请注意,我们提供了
Window
的默认实现,它可以被替换,并且WindowDecorator
的所有实例都将是一个Window
。装饰器
制作装饰器可以按如下方式完成:
完成
最后我们可以定义一些窗口:
In Haskell, this OO pattern translates pretty much directly, you only need a dictionary. Note that a direct translation is not actually a good idea. Trying to force a OO concept into Haskell is kind of backwords, but you asked for it so here it is.
The Window Interface
Haskell has classes, which has all the functionality of an Interface and then some. So we will use the following Haskell class:
The Abstract WindowDecorator class
This one is a bit more tricky since Haskell has no concept of inheritance. Usually we would not provide this type at all and let the decorators implement
Window
directly, but lets follow the example completely. In this example, aWindowDecorator
is a window with a constructor taking a window, lets augment this with a function giving the decorated window.Note that we provide a default implementation of
Window
, it can be replaced, and all instances ofWindowDecorator
will be aWindow
.The decorators
Making decorators can then be done as follows:
Finishing Up
Finally we can define some windows:
好的,首先让我们尝试找到 OOP 方面装饰器模式的所有主要组件。该模式主要用于装饰,即向现有对象添加附加功能。这是该模式最简单的定义。现在,如果我们尝试在 FP 的世界中找到此定义中存在的相同组件,我们可以说 FP 中不存在附加功能 = 新功能和对象,而是 FP 拥有您想要的东西。调用各种形式的数据或数据结构。因此,用 FP 术语来说,这种模式就变成了为 FP 数据结构添加附加功能或通过一些附加功能增强现有功能。
Ok, first of all lets try to find all the main components of decorator pattern in respect to OOP. This pattern is basically used to decorate i.e add additional features to an existing object. This is the simplest possible definition of this pattern. Now if we try to find the same components that are there in this definition in the world of FP, we can say that additional features = new functions and object are not there in FP, rather FP has what you call data or data structure in various forms. So in FP terms this patterns becomes, adding additional functions for FP data structures or enhancing existing function with some additional features.
Clojure 的乐趣 在第 13.3 章“缺乏设计模式”中讨论了这个问题。根据 JoC 的说法,
-> ;
和->>
宏在某种程度上类似于装饰器模式。The Joy of Clojure talks about this very issue in chapter 13.3, "A lack of design patterns". According to the JoC, the
->
and->>
macros are somewhat analogous to the decorator pattern.我不是 100% 确定,但我认为 高级函数式编程的 C9 讲座系列很好地解释了这个问题。
除此之外,您可以在 F# 中使用相同的技术(它支持相同的 OO 机制),在这种特殊情况下我会这样做。
我想这是一个品味和你想要解决的问题的问题。
I'm not 100% sure but I think the C9 lecture series on advanced functional programming explains the problem really good.
Aside from this you can use just the same technique inside F# (it supports just the same OO mechanism) and in this special case I would do so.
I guess it's a matter of tast and the problem you are trying to solve.
F# 尝试
这是我尝试在 F# 中创建一些有意义的东西,因为您要求提供很多示例。我有点生疏,所以希望没有人会羞辱我:P。装饰器基本上需要两个部分,新行为和新数据。在函数式语言中,新行为非常容易,因为它们“只是另一个函数”,因为函数本质上与对象解耦。新数据实际上同样简单,有多种方法可以实现这一点,最简单的是元组。您可以看到我创建了一个新数据类型,它是前一个数据类型的超集,并且我已经为该现有行为调用了现有函数。因此,我们的旧数据和旧行为仍然受到尊重,但我们也有新行为和新数据。
F# attempt
This is my attempt at creating something sensible in F# since you asked for many examples. I'm a little rusty so hopefully nobody will shame me :P. The decorator basically requires two parts, new behaviors and new data. New behaviors are exceedingly easy in functional languages as they are "just another function", since functions are inherently decoupled from objects. New data is actually similarly easy and there's several ways you can achieve this, the simplest being a tuple. You can see I've created a new datatype that is a superset of the previous, and I've called the existing function for that existing behavior. So we have our old data and old behavior still respected, but we also have new behavior and new data.
下面是一个使用 JSGI(用于 JavaScript 的 Web 服务器 API)的示例:
当然,可以堆叠兼容的中间件(例如
Lint(Logger(ContentLength(app)))
)Here's an example using JSGI, a web server API for JavaScript:
Compliant middleware can be stacked, of course (e.x.
Lint(Logger(ContentLength(app)))
)