Some of the reaction for Waterfront was related to the Application Context Pattern (ACP) - The pattern that allows most of Waterfront's
code to be purely functional. I'll try to explain the basics in this
post. Let me start with the motivation: the reason why FP is at odds
with GUI code.
(Pure) Functional code has no side effects, which implies immutability
of state. There are no fields nor global variables that can be
assigned to. Thus, one function can affect the computation carried out
by another function only by the passing of parameters. Most GUI
systems are built around the notion of event handlers which are
invoked by a message processing loop. There is no chain of calls from
one event handler to another.
In particular, if handler "A" computed some new value it cannot pass
it on to handler "B" because the system will call "B" only after "A"
returns. That's the predicament.
ACP overcomes this by capturing the applications current state in an
immutable map. All event handlers receive a single parameter which is
the "current" context and compute the "new" context. A typical handler
(henceforth: "context processing function") will carry out these
activities: (a) Examine the current context; (b) Perform some GUI
operations (setSize, setText, etc.); (c) Compute a new context based
on the current context and on information obtained from the GUI
(getText, etc.). The caller (henceforth: "dispatcher") takes the
returned context and will use it as the new current context, the next
time a context processing function is invoked.
This means that when you register event handler with a Swing widget
the handler needs to to call the ACP dispatcher passing it a context processing function.
The net effect of this approach is that only the dispatcher has to
deal with mutable state. The context processors are functional: they
merely compute the new state from the current.
application-context-pattern.clj (http://groups.google.com/group/ clojure/web/application-context-pattern.clj) shows a concrete example.
It's about 140 LOC (ripped off from the real Waterfront codebase)
structured as follows:
Lines 1..40: General-purpose helpers.
Lines 40..90: The ACP infrastructure
Lines 90..140: A quick sample, built around ACP.
The sample program opens a JFrame with two buttons: Input and Output.
A click on the input button will pop-up an input dialog box. A click
on the output button will pop-up a message box showing the last value
entered into the input box. There's also a JLabel showing the length
of the input, but let's ignore it for the moment.
The entry point into the ACP world is the bootstrap function. It takes
two parameters: a context processing function and an initial context.
In the example, this is carried out at the bottom of the run-it
function:
(.setLayout f (java.awt.FlowLayout.))
(doseq [x [b-in b-out]]
(.add f x) )
(doto f
(.setSize 500 300)
(.setDefaultCloseOperation javax.swing.JFrame/
DISPOSE_ON_CLOSE)
(.setVisible true))
(assoc ctx :frame f) ))]
(invoke-later #(bootstrap build-ui {})) ))
invoke-later is a utility function that is mapped to SwingUtilities/
invokeLater.
Let's drill down into the build-ui function: It takes the current
context (ctx parameter). Then it creates the frame and the buttons. It
uses new-action-listener (another utility) to register an action
listener with the buttons. The first listener looks like this:
((ctx :dispatch) get-input))))
It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
obtained, and evaluates it passing get-input as the context processing
function. The call to bootstrap initialized this dispatcher and added
the :dispatch mapping to the initial context.
get-input looks like this:
(defn- get-input [ctx]
(let [reply (javax.swing.JOptionPane/showInputDialog nil "Type in
something")]
(assoc ctx :user-input reply) ))
It pops-up an input box, and returns a new context which is the same
as the current context except that :user-input is now mapped to value
returned from the input box.
show-output is the context processing function for the output button:
(defn- show-output [ctx]
(javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
input)) )
Note that show-output returns nil which the dispatcher interprets as
"no change to ctx".
What we have is that get-input communicates with show-output by
returning a new context. There's no assignment into atoms or the
likes. The mutable state is encapsulated within the dispatcher.
It is now time for the JLabel to step into the plate. We now want to
add a JLabel that will show the length of the user input. We want this
label to be updated on the fly, with no explicit user request. This
type of behavior is common in GUI applications. To this end, the
dispatcher also supports the notion of observers. In ACP an observer
is a function takes two contexts: old-ctx and new-ctx.
An observer typically compares the contexts. If the mappings it is
interested in were changed, it carries out these activities:
(a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
context (that is: a context that is even newer than new-ctx). After a
context processing function was invoked, the dispatcher will run all
observers in a loop until a steady state was reached.
Here's the observer that takes care of updating the label:
(defn- input-observer [old-ctx new-ctx]
(when-not (= (old-ctx :user-input) (new-ctx :user-input))
(.setText (new-ctx :label) (str "Input length: " (count (new-
ctx :user-input)))) ))
One register an observer by adding it to (ctx :observers):
(assoc ctx :frame f :label label :observers (cons input-
observer (ctx :observers)))
There all sort of variations on these theme and some subtleties, but I
want to keep this post coherent so I'll skip these for now. Anyway,
all the fundamentals are here. As you can see this pattern is quite
powerful. Almost all functionality of Waterfront is built on top of
this.
For instance, the menu system is described as a DSL (a vector of maps)
mapped to the :menu key of the context. An observer keeps an eye on
the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
the plugin-loader is an observer that watches the :plugins list. When
a new entry is found, it loads this plugin. In fact, in Waterfront,
the startup function simply registers the plugin observer and loads
the list of plugins from a file.
> Some of the reaction for Waterfront was related to the Application > Context Pattern (ACP) - The pattern that allows most of Waterfront's
> code to be purely functional. I'll try to explain the basics in this
> post. Let me start with the motivation: the reason why FP is at odds
> with GUI code.
> (Pure) Functional code has no side effects, which implies immutability
> of state. There are no fields nor global variables that can be
> assigned to. Thus, one function can affect the computation carried out
> by another function only by the passing of parameters. Most GUI
> systems are built around the notion of event handlers which are
> invoked by a message processing loop. There is no chain of calls from
> one event handler to another.
> In particular, if handler "A" computed some new value it cannot pass
> it on to handler "B" because the system will call "B" only after "A"
> returns. That's the predicament.
> ACP overcomes this by capturing the applications current state in an
> immutable map. All event handlers receive a single parameter which is
> the "current" context and compute the "new" context. A typical handler
> (henceforth: "context processing function") will carry out these
> activities: (a) Examine the current context; (b) Perform some GUI
> operations (setSize, setText, etc.); (c) Compute a new context based
> on the current context and on information obtained from the GUI
> (getText, etc.). The caller (henceforth: "dispatcher") takes the
> returned context and will use it as the new current context, the next
> time a context processing function is invoked.
> This means that when you register event handler with a Swing widget
> the handler needs to to call the ACP dispatcher passing it a context > processing function.
> The net effect of this approach is that only the dispatcher has to
> deal with mutable state. The context processors are functional: they
> merely compute the new state from the current.
> application-context-pattern.clj (http://groups.google.com/group/ > clojure/web/application-context-pattern.clj) shows a concrete example.
> It's about 140 LOC (ripped off from the real Waterfront codebase)
> structured as follows:
> Lines 1..40: General-purpose helpers.
> Lines 40..90: The ACP infrastructure
> Lines 90..140: A quick sample, built around ACP.
> The sample program opens a JFrame with two buttons: Input and Output.
> A click on the input button will pop-up an input dialog box. A click
> on the output button will pop-up a message box showing the last value
> entered into the input box. There's also a JLabel showing the length
> of the input, but let's ignore it for the moment.
> The entry point into the ACP world is the bootstrap function. It takes
> two parameters: a context processing function and an initial context.
> In the example, this is carried out at the bottom of the run-it
> function:
> invoke-later is a utility function that is mapped to SwingUtilities/
> invokeLater.
> Let's drill down into the build-ui function: It takes the current
> context (ctx parameter). Then it creates the frame and the buttons. It
> uses new-action-listener (another utility) to register an action
> listener with the buttons. The first listener looks like this:
> ((ctx :dispatch) get-input))))
> It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
> obtained, and evaluates it passing get-input as the context processing
> function. The call to bootstrap initialized this dispatcher and added
> the :dispatch mapping to the initial context.
> It pops-up an input box, and returns a new context which is the same
> as the current context except that :user-input is now mapped to value
> returned from the input box.
> show-output is the context processing function for the output button:
> (defn- show-output [ctx]
> (javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
> input)) )
> Note that show-output returns nil which the dispatcher interprets as
> "no change to ctx".
> What we have is that get-input communicates with show-output by
> returning a new context. There's no assignment into atoms or the
> likes. The mutable state is encapsulated within the dispatcher.
> It is now time for the JLabel to step into the plate. We now want to
> add a JLabel that will show the length of the user input. We want this
> label to be updated on the fly, with no explicit user request. This
> type of behavior is common in GUI applications. To this end, the
> dispatcher also supports the notion of observers. In ACP an observer
> is a function takes two contexts: old-ctx and new-ctx.
> An observer typically compares the contexts. If the mappings it is
> interested in were changed, it carries out these activities:
> (a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
> context (that is: a context that is even newer than new-ctx). After a
> context processing function was invoked, the dispatcher will run all
> observers in a loop until a steady state was reached.
> Here's the observer that takes care of updating the label:
> (defn- input-observer [old-ctx new-ctx]
> (when-not (= (old-ctx :user-input) (new-ctx :user-input))
> (.setText (new-ctx :label) (str "Input length: " (count (new-
> ctx :user-input)))) ))
> One register an observer by adding it to (ctx :observers):
> (assoc ctx :frame f :label label :observers (cons input-
> observer (ctx :observers)))
> There all sort of variations on these theme and some subtleties, but I
> want to keep this post coherent so I'll skip these for now. Anyway,
> all the fundamentals are here. As you can see this pattern is quite
> powerful. Almost all functionality of Waterfront is built on top of
> this.
> For instance, the menu system is described as a DSL (a vector of maps)
> mapped to the :menu key of the context. An observer keeps an eye on
> the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
> the plugin-loader is an observer that watches the :plugins list. When
> a new entry is found, it loads this plugin. In fact, in Waterfront,
> the startup function simply registers the plugin observer and loads
> the list of plugins from a file.
On Fri, Feb 27, 2009 at 5:24 AM, linh <nguyenlinh.m...@gmail.com> wrote:
> thanks, this will be very useful for me
> On 27 Feb, 09:05, Itay Maman <itay.ma...@gmail.com> wrote:
> > Some of the reaction for Waterfront was related to the Application
> > Context Pattern (ACP) - The pattern that allows most of Waterfront's
> > code to be purely functional. I'll try to explain the basics in this
> > post. Let me start with the motivation: the reason why FP is at odds
> > with GUI code.
> > (Pure) Functional code has no side effects, which implies immutability
> > of state. There are no fields nor global variables that can be
> > assigned to. Thus, one function can affect the computation carried out
> > by another function only by the passing of parameters. Most GUI
> > systems are built around the notion of event handlers which are
> > invoked by a message processing loop. There is no chain of calls from
> > one event handler to another.
> > In particular, if handler "A" computed some new value it cannot pass
> > it on to handler "B" because the system will call "B" only after "A"
> > returns. That's the predicament.
> > ACP overcomes this by capturing the applications current state in an
> > immutable map. All event handlers receive a single parameter which is
> > the "current" context and compute the "new" context. A typical handler
> > (henceforth: "context processing function") will carry out these
> > activities: (a) Examine the current context; (b) Perform some GUI
> > operations (setSize, setText, etc.); (c) Compute a new context based
> > on the current context and on information obtained from the GUI
> > (getText, etc.). The caller (henceforth: "dispatcher") takes the
> > returned context and will use it as the new current context, the next
> > time a context processing function is invoked.
> > This means that when you register event handler with a Swing widget
> > the handler needs to to call the ACP dispatcher passing it a context
> > processing function.
> > The net effect of this approach is that only the dispatcher has to
> > deal with mutable state. The context processors are functional: they
> > merely compute the new state from the current.
> > application-context-pattern.clj (http://groups.google.com/group/ > > clojure/web/application-context-pattern.clj) shows a concrete example.
> > It's about 140 LOC (ripped off from the real Waterfront codebase)
> > structured as follows:
> > Lines 1..40: General-purpose helpers.
> > Lines 40..90: The ACP infrastructure
> > Lines 90..140: A quick sample, built around ACP.
> > The sample program opens a JFrame with two buttons: Input and Output.
> > A click on the input button will pop-up an input dialog box. A click
> > on the output button will pop-up a message box showing the last value
> > entered into the input box. There's also a JLabel showing the length
> > of the input, but let's ignore it for the moment.
> > The entry point into the ACP world is the bootstrap function. It takes
> > two parameters: a context processing function and an initial context.
> > In the example, this is carried out at the bottom of the run-it
> > function:
> > invoke-later is a utility function that is mapped to SwingUtilities/
> > invokeLater.
> > Let's drill down into the build-ui function: It takes the current
> > context (ctx parameter). Then it creates the frame and the buttons. It
> > uses new-action-listener (another utility) to register an action
> > listener with the buttons. The first listener looks like this:
> > ((ctx :dispatch) get-input))))
> > It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
> > obtained, and evaluates it passing get-input as the context processing
> > function. The call to bootstrap initialized this dispatcher and added
> > the :dispatch mapping to the initial context.
> > It pops-up an input box, and returns a new context which is the same
> > as the current context except that :user-input is now mapped to value
> > returned from the input box.
> > show-output is the context processing function for the output button:
> > (defn- show-output [ctx]
> > (javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
> > input)) )
> > Note that show-output returns nil which the dispatcher interprets as
> > "no change to ctx".
> > What we have is that get-input communicates with show-output by
> > returning a new context. There's no assignment into atoms or the
> > likes. The mutable state is encapsulated within the dispatcher.
> > It is now time for the JLabel to step into the plate. We now want to
> > add a JLabel that will show the length of the user input. We want this
> > label to be updated on the fly, with no explicit user request. This
> > type of behavior is common in GUI applications. To this end, the
> > dispatcher also supports the notion of observers. In ACP an observer
> > is a function takes two contexts: old-ctx and new-ctx.
> > An observer typically compares the contexts. If the mappings it is
> > interested in were changed, it carries out these activities:
> > (a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
> > context (that is: a context that is even newer than new-ctx). After a
> > context processing function was invoked, the dispatcher will run all
> > observers in a loop until a steady state was reached.
> > Here's the observer that takes care of updating the label:
> > (defn- input-observer [old-ctx new-ctx]
> > (when-not (= (old-ctx :user-input) (new-ctx :user-input))
> > (.setText (new-ctx :label) (str "Input length: " (count (new-
> > ctx :user-input)))) ))
> > One register an observer by adding it to (ctx :observers):
> > (assoc ctx :frame f :label label :observers (cons input-
> > observer (ctx :observers)))
> > There all sort of variations on these theme and some subtleties, but I
> > want to keep this post coherent so I'll skip these for now. Anyway,
> > all the fundamentals are here. As you can see this pattern is quite
> > powerful. Almost all functionality of Waterfront is built on top of
> > this.
> > For instance, the menu system is described as a DSL (a vector of maps)
> > mapped to the :menu key of the context. An observer keeps an eye on
> > the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
> > the plugin-loader is an observer that watches the :plugins list. When
> > a new entry is found, it loads this plugin. In fact, in Waterfront,
> > the startup function simply registers the plugin observer and loads
> > the list of plugins from a file.
> Does anyone have similar example using one of the cells > implementations? > What would be pros/cons between this and cells approach?
The cells implementations I've seen posted to this list (in fact pretty much all cells reimplementations) haven't yet approached the sophistication of kenny-cells.
It's like editors. Everyone reckons they can make one better than emacs (or vi) but underestimate the man-hours that have gone into them over the years and end up with something that maybe looks as good (or better) superficially but when you delve into the details, just doesn't cut the mustard.
I'd love to see some kind of clojure-cells but it's still a long way off.
On Feb 27, 12:59 pm, Andy Chambers <achambers.h...@googlemail.com>
wrote:
> The cells implementations I've seen posted to this list (in fact pretty much all
> cells reimplementations) haven't yet approached the sophistication of
> kenny-cells.
Agreed. And I wrote two of them. They're just toy implementations so
far.
> > The cells implementations I've seen posted to this list (in fact pretty much all
> > cells reimplementations) haven't yet approached the sophistication of
> > kenny-cells.
> Agreed. And I wrote two of them. They're just toy implementations so
> far.
I'm still not sure how it should look like given clojure immutablility.
It looks really nice. I have a question about those observers, though--
every time that a context-processing function is called, every
observer is called one by one, no matter what the context-processing
function was. This seems somewhat inefficient, more so than listeners
that listen to only certain functions and are called when the listened
function is activated. In your experience, is this not a big problem?
Or am I missing something?
On Feb 27, 1:05 am, Itay Maman <itay.ma...@gmail.com> wrote:
> Some of the reaction for Waterfront was related to the Application > Context Pattern (ACP) - The pattern that allows most of Waterfront's
> code to be purely functional. I'll try to explain the basics in this
> post. Let me start with the motivation: the reason why FP is at odds
> with GUI code.
> (Pure) Functional code has no side effects, which implies immutability
> of state. There are no fields nor global variables that can be
> assigned to. Thus, one function can affect the computation carried out
> by another function only by the passing of parameters. Most GUI
> systems are built around the notion of event handlers which are
> invoked by a message processing loop. There is no chain of calls from
> one event handler to another.
> In particular, if handler "A" computed some new value it cannot pass
> it on to handler "B" because the system will call "B" only after "A"
> returns. That's the predicament.
> ACP overcomes this by capturing the applications current state in an
> immutable map. All event handlers receive a single parameter which is
> the "current" context and compute the "new" context. A typical handler
> (henceforth: "context processing function") will carry out these
> activities: (a) Examine the current context; (b) Perform some GUI
> operations (setSize, setText, etc.); (c) Compute a new context based
> on the current context and on information obtained from the GUI
> (getText, etc.). The caller (henceforth: "dispatcher") takes the
> returned context and will use it as the new current context, the next
> time a context processing function is invoked.
> This means that when you register event handler with a Swing widget
> the handler needs to to call the ACP dispatcher passing it a context > processing function.
> The net effect of this approach is that only the dispatcher has to
> deal with mutable state. The context processors are functional: they
> merely compute the new state from the current.
> application-context-pattern.clj (http://groups.google.com/group/ > clojure/web/application-context-pattern.clj) shows a concrete example.
> It's about 140 LOC (ripped off from the real Waterfront codebase)
> structured as follows:
> Lines 1..40: General-purpose helpers.
> Lines 40..90: The ACP infrastructure
> Lines 90..140: A quick sample, built around ACP.
> The sample program opens a JFrame with two buttons: Input and Output.
> A click on the input button will pop-up an input dialog box. A click
> on the output button will pop-up a message box showing the last value
> entered into the input box. There's also a JLabel showing the length
> of the input, but let's ignore it for the moment.
> The entry point into the ACP world is the bootstrap function. It takes
> two parameters: a context processing function and an initial context.
> In the example, this is carried out at the bottom of the run-it
> function:
> invoke-later is a utility function that is mapped to SwingUtilities/
> invokeLater.
> Let's drill down into the build-ui function: It takes the current
> context (ctx parameter). Then it creates the frame and the buttons. It
> uses new-action-listener (another utility) to register an action
> listener with the buttons. The first listener looks like this:
> ((ctx :dispatch) get-input))))
> It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
> obtained, and evaluates it passing get-input as the context processing
> function. The call to bootstrap initialized this dispatcher and added
> the :dispatch mapping to the initial context.
> It pops-up an input box, and returns a new context which is the same
> as the current context except that :user-input is now mapped to value
> returned from the input box.
> show-output is the context processing function for the output button:
> (defn- show-output [ctx]
> (javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
> input)) )
> Note that show-output returns nil which the dispatcher interprets as
> "no change to ctx".
> What we have is that get-input communicates with show-output by
> returning a new context. There's no assignment into atoms or the
> likes. The mutable state is encapsulated within the dispatcher.
> It is now time for the JLabel to step into the plate. We now want to
> add a JLabel that will show the length of the user input. We want this
> label to be updated on the fly, with no explicit user request. This
> type of behavior is common in GUI applications. To this end, the
> dispatcher also supports the notion of observers. In ACP an observer
> is a function takes two contexts: old-ctx and new-ctx.
> An observer typically compares the contexts. If the mappings it is
> interested in were changed, it carries out these activities:
> (a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
> context (that is: a context that is even newer than new-ctx). After a
> context processing function was invoked, the dispatcher will run all
> observers in a loop until a steady state was reached.
> Here's the observer that takes care of updating the label:
> (defn- input-observer [old-ctx new-ctx]
> (when-not (= (old-ctx :user-input) (new-ctx :user-input))
> (.setText (new-ctx :label) (str "Input length: " (count (new-
> ctx :user-input)))) ))
> One register an observer by adding it to (ctx :observers):
> (assoc ctx :frame f :label label :observers (cons input-
> observer (ctx :observers)))
> There all sort of variations on these theme and some subtleties, but I
> want to keep this post coherent so I'll skip these for now. Anyway,
> all the fundamentals are here. As you can see this pattern is quite
> powerful. Almost all functionality of Waterfront is built on top of
> this.
> For instance, the menu system is described as a DSL (a vector of maps)
> mapped to the :menu key of the context. An observer keeps an eye on
> the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
> the plugin-loader is an observer that watches the :plugins list. When
> a new entry is found, it loads this plugin. In fact, in Waterfront,
> the startup function simply registers the plugin observer and loads
> the list of plugins from a file.
On Feb 28, 3:26 am, samppi <rbysam...@gmail.com> wrote:
> It looks really nice. I have a question about those observers, though--
> every time that a context-processing function is called, every
> observer is called one by one, no matter what the context-processing
> function was. This seems somewhat inefficient, more so than listeners
> that listen to only certain functions and are called when the listened
> function is activated. In your experience, is this not a big problem?
I believe that in many practical scenarios this is not a major
problem. Most observers will exit
almost immediately so iterating over them (even several hundreds of
them) will not be too expensive.
Anyway, if it does get into a problem I can think of two possible
solutions:
1) It is possible to create more than one context in a program (in
other words: the context is not a singleton). By splitting the contexts the number of observers
per invocation of a context-processor
will get smaller.
2) It is possible to create one observer (parent) that delegates to
other observers (children). The parent will contain the logic as to
whether the children need to be invoked. If it decides that the answer
is "No" it will immediately return, thus reducing the workload on the
observer invocation loop.
> On Feb 27, 1:05 am, Itay Maman <itay.ma...@gmail.com> wrote:
> > Some of the reaction for Waterfront was related to the Application > > Context Pattern (ACP) - The pattern that allows most of Waterfront's
> > code to be purely functional. I'll try to explain the basics in this
> > post. Let me start with the motivation: the reason why FP is at odds
> > with GUI code.
> > (Pure) Functional code has no side effects, which implies immutability
> > of state. There are no fields nor global variables that can be
> > assigned to. Thus, one function can affect the computation carried out
> > by another function only by the passing of parameters. Most GUI
> > systems are built around the notion of event handlers which are
> > invoked by a message processing loop. There is no chain of calls from
> > one event handler to another.
> > In particular, if handler "A" computed some new value it cannot pass
> > it on to handler "B" because the system will call "B" only after "A"
> > returns. That's the predicament.
> > ACP overcomes this by capturing the applications current state in an
> > immutable map. All event handlers receive a single parameter which is
> > the "current" context and compute the "new" context. A typical handler
> > (henceforth: "context processing function") will carry out these
> > activities: (a) Examine the current context; (b) Perform some GUI
> > operations (setSize, setText, etc.); (c) Compute a new context based
> > on the current context and on information obtained from the GUI
> > (getText, etc.). The caller (henceforth: "dispatcher") takes the
> > returned context and will use it as the new current context, the next
> > time a context processing function is invoked.
> > This means that when you register event handler with a Swing widget
> > the handler needs to to call the ACP dispatcher passing it a context > > processing function.
> > The net effect of this approach is that only the dispatcher has to
> > deal with mutable state. The context processors are functional: they
> > merely compute the new state from the current.
> > application-context-pattern.clj (http://groups.google.com/group/ > > clojure/web/application-context-pattern.clj) shows a concrete example.
> > It's about 140 LOC (ripped off from the real Waterfront codebase)
> > structured as follows:
> > Lines 1..40: General-purpose helpers.
> > Lines 40..90: The ACP infrastructure
> > Lines 90..140: A quick sample, built around ACP.
> > The sample program opens a JFrame with two buttons: Input and Output.
> > A click on the input button will pop-up an input dialog box. A click
> > on the output button will pop-up a message box showing the last value
> > entered into the input box. There's also a JLabel showing the length
> > of the input, but let's ignore it for the moment.
> > The entry point into the ACP world is the bootstrap function. It takes
> > two parameters: a context processing function and an initial context.
> > In the example, this is carried out at the bottom of the run-it
> > function:
> > invoke-later is a utility function that is mapped to SwingUtilities/
> > invokeLater.
> > Let's drill down into the build-ui function: It takes the current
> > context (ctx parameter). Then it creates the frame and the buttons. It
> > uses new-action-listener (another utility) to register an action
> > listener with the buttons. The first listener looks like this:
> > ((ctx :dispatch) get-input))))
> > It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
> > obtained, and evaluates it passing get-input as the context processing
> > function. The call to bootstrap initialized this dispatcher and added
> > the :dispatch mapping to the initial context.
> > It pops-up an input box, and returns a new context which is the same
> > as the current context except that :user-input is now mapped to value
> > returned from the input box.
> > show-output is the context processing function for the output button:
> > (defn- show-output [ctx]
> > (javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
> > input)) )
> > Note that show-output returns nil which the dispatcher interprets as
> > "no change to ctx".
> > What we have is that get-input communicates with show-output by
> > returning a new context. There's no assignment into atoms or the
> > likes. The mutable state is encapsulated within the dispatcher.
> > It is now time for the JLabel to step into the plate. We now want to
> > add a JLabel that will show the length of the user input. We want this
> > label to be updated on the fly, with no explicit user request. This
> > type of behavior is common in GUI applications. To this end, the
> > dispatcher also supports the notion of observers. In ACP an observer
> > is a function takes two contexts: old-ctx and new-ctx.
> > An observer typically compares the contexts. If the mappings it is
> > interested in were changed, it carries out these activities:
> > (a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
> > context (that is: a context that is even newer than new-ctx). After a
> > context processing function was invoked, the dispatcher will run all
> > observers in a loop until a steady state was reached.
> > Here's the observer that takes care of updating the label:
> > (defn- input-observer [old-ctx new-ctx]
> > (when-not (= (old-ctx :user-input) (new-ctx :user-input))
> > (.setText (new-ctx :label) (str "Input length: " (count (new-
> > ctx :user-input)))) ))
> > One register an observer by adding it to (ctx :observers):
> > (assoc ctx :frame f :label label :observers (cons input-
> > observer (ctx :observers)))
> > There all sort of variations on these theme and some subtleties, but I
> > want to keep this post coherent so I'll skip these for now. Anyway,
> > all the fundamentals are here. As you can see this pattern is quite
> > powerful. Almost all functionality of Waterfront is built on top of
> > this.
> > For instance, the menu system is described as a DSL (a vector of maps)
> > mapped to the :menu key of the context. An observer keeps an eye on
> > the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
> > the plugin-loader is an observer that watches the :plugins list. When
> > a new entry is found, it loads this plugin. In fact, in Waterfront,
> > the startup function simply registers the plugin observer and loads
> > the list of plugins from a file.
On Feb 27, 5:38 pm, Marko Kocić <marko.ko...@gmail.com> wrote:
> Interesting approach, nice explained.
> Does anyone have similar example using one of the cells
> implementations?
> What would be pros/cons between this and cells approach?
I think that in general, when comparing powerful facilities (such as
the Context-Pattern or Cells) one usually ends up with the conclusion
that they are largely equivalent: the former can be implemented in
terms of the latter and vice-versa. Thus, a discussion of pros and
cons is more about stylistic issues: how hard is it to achieve a
certain behavior in the two approaches?
With that in mind I think that cells are the natural choice for
implementing a complicated net of (well...) spread-sheet-like cells.
Contexts are more natural for UI since the observers are ran only
after a processor has finished. Even if the processor alters several
values in the context, the observers will be notified only when the
processor returns, seeing all the changes bundled together. In many
cases this is the desired behavior in UI apps. If a single user
request adds 100 strings to a list you want the corresponding widget
to be updated once, not 100 times.
I think the differences boil down to the fact the processors are
functional in their nature: the processor per-se does not notify the
observers, only the dispatcher does. Thus, you can compose two
processors (in the same manner you compose functions) without worrying
of side effects due to observers being fired.
Hi Itay,
I'm a little confused about one aspect of the context pattern.
If I understand this write, a listener is a function that takes an old
context, and returns a new context, BUT it also calls the appropriate
GUI functions (setText, setSize) to ensure that the gui state is
consistent with the new context. Is that correct?
If so then the listeners aren't purely functional. They don't affect
any mutable state in atoms or refs, but they mutate the current
running state of the GUI. So side-effects of these functions are
therefore important.
On Feb 28, 8:32 pm, CuppoJava <patrickli_2...@hotmail.com> wrote:
> Hi Itay,
> I'm a little confused about one aspect of the context pattern.
> If I understand this write, a listener is a function that takes an old
> context, and returns a new context, BUT it also calls the appropriate
> GUI functions (setText, setSize) to ensure that the gui state is
> consistent with the new context. Is that correct?
Yes.
> If so then the listeners aren't purely functional. They don't affect
> any mutable state in atoms or refs, but they mutate the current
> running state of the GUI. So side-effects of these functions are
> therefore important.
Processors/observers may mutate the UI. Yet, even if the processors
manipulate the UI, they will not cause observer-issued side effects.
Also, the affect of either processors or observers is limited to the
UI - their behavior WRT to the program-manged state is side-effect
free.
Hi Itay,
Thanks for posting this example. Being new to Clojure it's a nice example
to study since it solves a very realistic problem that many new to
functional programming will face.
I think I've unraveled most of how the code is working but there's one
function I'm not particularly clear about.
> Some of the reaction for Waterfront was related to the Application
> Context Pattern (ACP) - The pattern that allows most of Waterfront's
> code to be purely functional. I'll try to explain the basics in this
> post. Let me start with the motivation: the reason why FP is at odds
> with GUI code.
> (Pure) Functional code has no side effects, which implies immutability
> of state. There are no fields nor global variables that can be
> assigned to. Thus, one function can affect the computation carried out
> by another function only by the passing of parameters. Most GUI
> systems are built around the notion of event handlers which are
> invoked by a message processing loop. There is no chain of calls from
> one event handler to another.
> In particular, if handler "A" computed some new value it cannot pass
> it on to handler "B" because the system will call "B" only after "A"
> returns. That's the predicament.
> ACP overcomes this by capturing the applications current state in an
> immutable map. All event handlers receive a single parameter which is
> the "current" context and compute the "new" context. A typical handler
> (henceforth: "context processing function") will carry out these
> activities: (a) Examine the current context; (b) Perform some GUI
> operations (setSize, setText, etc.); (c) Compute a new context based
> on the current context and on information obtained from the GUI
> (getText, etc.). The caller (henceforth: "dispatcher") takes the
> returned context and will use it as the new current context, the next
> time a context processing function is invoked.
> This means that when you register event handler with a Swing widget
> the handler needs to to call the ACP dispatcher passing it a context
> processing function.
> The net effect of this approach is that only the dispatcher has to
> deal with mutable state. The context processors are functional: they
> merely compute the new state from the current.
> application-context-pattern.clj (http://groups.google.com/group/ > clojure/web/application-context-pattern.clj) shows a concrete example.
> It's about 140 LOC (ripped off from the real Waterfront codebase)
> structured as follows:
> Lines 1..40: General-purpose helpers.
> Lines 40..90: The ACP infrastructure
> Lines 90..140: A quick sample, built around ACP.
> The sample program opens a JFrame with two buttons: Input and Output.
> A click on the input button will pop-up an input dialog box. A click
> on the output button will pop-up a message box showing the last value
> entered into the input box. There's also a JLabel showing the length
> of the input, but let's ignore it for the moment.
> The entry point into the ACP world is the bootstrap function. It takes
> two parameters: a context processing function and an initial context.
> In the example, this is carried out at the bottom of the run-it
> function:
> invoke-later is a utility function that is mapped to SwingUtilities/
> invokeLater.
> Let's drill down into the build-ui function: It takes the current
> context (ctx parameter). Then it creates the frame and the buttons. It
> uses new-action-listener (another utility) to register an action
> listener with the buttons. The first listener looks like this:
> ((ctx :dispatch) get-input))))
> It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
> obtained, and evaluates it passing get-input as the context processing
> function. The call to bootstrap initialized this dispatcher and added
> the :dispatch mapping to the initial context.
> It pops-up an input box, and returns a new context which is the same
> as the current context except that :user-input is now mapped to value
> returned from the input box.
> show-output is the context processing function for the output button:
> (defn- show-output [ctx]
> (javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
> input)) )
> Note that show-output returns nil which the dispatcher interprets as
> "no change to ctx".
> What we have is that get-input communicates with show-output by
> returning a new context. There's no assignment into atoms or the
> likes. The mutable state is encapsulated within the dispatcher.
> It is now time for the JLabel to step into the plate. We now want to
> add a JLabel that will show the length of the user input. We want this
> label to be updated on the fly, with no explicit user request. This
> type of behavior is common in GUI applications. To this end, the
> dispatcher also supports the notion of observers. In ACP an observer
> is a function takes two contexts: old-ctx and new-ctx.
> An observer typically compares the contexts. If the mappings it is
> interested in were changed, it carries out these activities:
> (a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
> context (that is: a context that is even newer than new-ctx). After a
> context processing function was invoked, the dispatcher will run all
> observers in a loop until a steady state was reached.
> Here's the observer that takes care of updating the label:
> (defn- input-observer [old-ctx new-ctx]
> (when-not (= (old-ctx :user-input) (new-ctx :user-input))
> (.setText (new-ctx :label) (str "Input length: " (count (new-
> ctx :user-input)))) ))
> One register an observer by adding it to (ctx :observers):
> (assoc ctx :frame f :label label :observers (cons input-
> observer (ctx :observers)))
> There all sort of variations on these theme and some subtleties, but I
> want to keep this post coherent so I'll skip these for now. Anyway,
> all the fundamentals are here. As you can see this pattern is quite
> powerful. Almost all functionality of Waterfront is built on top of
> this.
> For instance, the menu system is described as a DSL (a vector of maps)
> mapped to the :menu key of the context. An observer keeps an eye on
> the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
> the plugin-loader is an observer that watches the :plugins list. When
> a new entry is found, it loads this plugin. In fact, in Waterfront,
> the startup function simply registers the plugin observer and loads
> the list of plugins from a file.
Suppose you have three observers: o1, o2, o3. run-observers evaluates
them in this order.
Let's assume we don't have run-observers-till-fixpoint. Thus, after
the evaluation of a processor we will use run-observers to run these
observers.
Under this scenario, what will happen if o3 will return a new context?
o1 and o2 were already evaluated so they will not be able to react to
the updates in the context issued by o3.
run-observers-till-fixpoint takes care of that by repeatedly looping
thru all observers until the context stabilizes. This has the
disadvantage of a possible infinite loop, but the advantage of making
the context pattern indifferent of the order of the observers. My
experience shows that observers ordering issues are more complicated
to solve than infinite loops among observers.
> Hi Itay,
> Thanks for posting this example. Being new to Clojure it's a nice example
> to study since it solves a very realistic problem that many new to
> functional programming will face.
> I think I've unraveled most of how the code is working but there's one
> function I'm not particularly clear about.
> > Some of the reaction for Waterfront was related to the Application > > Context Pattern (ACP) - The pattern that allows most of Waterfront's
> > code to be purely functional. I'll try to explain the basics in this
> > post. Let me start with the motivation: the reason why FP is at odds
> > with GUI code.
> > (Pure) Functional code has no side effects, which implies immutability
> > of state. There are no fields nor global variables that can be
> > assigned to. Thus, one function can affect the computation carried out
> > by another function only by the passing of parameters. Most GUI
> > systems are built around the notion of event handlers which are
> > invoked by a message processing loop. There is no chain of calls from
> > one event handler to another.
> > In particular, if handler "A" computed some new value it cannot pass
> > it on to handler "B" because the system will call "B" only after "A"
> > returns. That's the predicament.
> > ACP overcomes this by capturing the applications current state in an
> > immutable map. All event handlers receive a single parameter which is
> > the "current" context and compute the "new" context. A typical handler
> > (henceforth: "context processing function") will carry out these
> > activities: (a) Examine the current context; (b) Perform some GUI
> > operations (setSize, setText, etc.); (c) Compute a new context based
> > on the current context and on information obtained from the GUI
> > (getText, etc.). The caller (henceforth: "dispatcher") takes the
> > returned context and will use it as the new current context, the next
> > time a context processing function is invoked.
> > This means that when you register event handler with a Swing widget
> > the handler needs to to call the ACP dispatcher passing it a context > > processing function.
> > The net effect of this approach is that only the dispatcher has to
> > deal with mutable state. The context processors are functional: they
> > merely compute the new state from the current.
> > application-context-pattern.clj (http://groups.google.com/group/ > > clojure/web/application-context-pattern.clj) shows a concrete example.
> > It's about 140 LOC (ripped off from the real Waterfront codebase)
> > structured as follows:
> > Lines 1..40: General-purpose helpers.
> > Lines 40..90: The ACP infrastructure
> > Lines 90..140: A quick sample, built around ACP.
> > The sample program opens a JFrame with two buttons: Input and Output.
> > A click on the input button will pop-up an input dialog box. A click
> > on the output button will pop-up a message box showing the last value
> > entered into the input box. There's also a JLabel showing the length
> > of the input, but let's ignore it for the moment.
> > The entry point into the ACP world is the bootstrap function. It takes
> > two parameters: a context processing function and an initial context.
> > In the example, this is carried out at the bottom of the run-it
> > function:
> > invoke-later is a utility function that is mapped to SwingUtilities/
> > invokeLater.
> > Let's drill down into the build-ui function: It takes the current
> > context (ctx parameter). Then it creates the frame and the buttons. It
> > uses new-action-listener (another utility) to register an action
> > listener with the buttons. The first listener looks like this:
> > ((ctx :dispatch) get-input))))
> > It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
> > obtained, and evaluates it passing get-input as the context processing
> > function. The call to bootstrap initialized this dispatcher and added
> > the :dispatch mapping to the initial context.
> > It pops-up an input box, and returns a new context which is the same
> > as the current context except that :user-input is now mapped to value
> > returned from the input box.
> > show-output is the context processing function for the output button:
> > (defn- show-output [ctx]
> > (javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
> > input)) )
> > Note that show-output returns nil which the dispatcher interprets as
> > "no change to ctx".
> > What we have is that get-input communicates with show-output by
> > returning a new context. There's no assignment into atoms or the
> > likes. The mutable state is encapsulated within the dispatcher.
> > It is now time for the JLabel to step into the plate. We now want to
> > add a JLabel that will show the length of the user input. We want this
> > label to be updated on the fly, with no explicit user request. This
> > type of behavior is common in GUI applications. To this end, the
> > dispatcher also supports the notion of observers. In ACP an observer
> > is a function takes two contexts: old-ctx and new-ctx.
> > An observer typically compares the contexts. If the mappings it is
> > interested in were changed, it carries out these activities:
> > (a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
> > context (that is: a context that is even newer than new-ctx). After a
> > context processing function was invoked, the dispatcher will run all
> > observers in a loop until a steady state was reached.
> > Here's the observer that takes care of updating the label:
> > (defn- input-observer [old-ctx new-ctx]
> > (when-not (= (old-ctx :user-input) (new-ctx :user-input))
> > (.setText (new-ctx :label) (str "Input length: " (count (new-
> > ctx :user-input)))) ))
> > One register an observer by adding it to (ctx :observers):
> > (assoc ctx :frame f :label label :observers (cons input-
> > observer (ctx :observers)))
> > There all sort of variations on these theme and some subtleties, but I
> > want to keep this post coherent so I'll skip these for now. Anyway,
> > all the fundamentals are here. As you can see this pattern is quite
> > powerful. Almost all functionality of Waterfront is built on top of
> > this.
> > For instance, the menu system is described as a DSL (a vector of maps)
> > mapped to the :menu key of the context. An observer keeps an eye on
> > the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
> > the plugin-loader is an observer that watches the :plugins list. When
> > a new entry is found, it loads this plugin. In fact, in Waterfront,
> > the startup function simply registers the plugin observer and loads
> > the list of plugins from a file.
> Suppose you have three observers: o1, o2, o3. run-observers evaluates
> them in this order.
> Let's assume we don't have run-observers-till-fixpoint. Thus, after
> the evaluation of a processor we will use run-observers to run these
> observers.
> Under this scenario, what will happen if o3 will return a new context?
> o1 and o2 were already evaluated so they will not be able to react to
> the updates in the context issued by o3.
> run-observers-till-fixpoint takes care of that by repeatedly looping
> thru all observers until the context stabilizes. This has the
> disadvantage of a possible infinite loop, but the advantage of making
> the context pattern indifferent of the order of the observers. My
> experience shows that observers ordering issues are more complicated
> to solve than infinite loops among observers.
> On Mar 4, 8:45 am, Glen Stampoultzis <gst...@gmail.com> wrote:
> > Hi Itay,
> > Thanks for posting this example. Being new to Clojure it's a nice
> example
> > to study since it solves a very realistic problem that many new to
> > functional programming will face.
> > I think I've unraveled most of how the code is working but there's one
> > function I'm not particularly clear about.
> > > Some of the reaction for Waterfront was related to the Application
> > > Context Pattern (ACP) - The pattern that allows most of Waterfront's
> > > code to be purely functional. I'll try to explain the basics in this
> > > post. Let me start with the motivation: the reason why FP is at odds
> > > with GUI code.
> > > (Pure) Functional code has no side effects, which implies immutability
> > > of state. There are no fields nor global variables that can be
> > > assigned to. Thus, one function can affect the computation carried out
> > > by another function only by the passing of parameters. Most GUI
> > > systems are built around the notion of event handlers which are
> > > invoked by a message processing loop. There is no chain of calls from
> > > one event handler to another.
> > > In particular, if handler "A" computed some new value it cannot pass
> > > it on to handler "B" because the system will call "B" only after "A"
> > > returns. That's the predicament.
> > > ACP overcomes this by capturing the applications current state in an
> > > immutable map. All event handlers receive a single parameter which is
> > > the "current" context and compute the "new" context. A typical handler
> > > (henceforth: "context processing function") will carry out these
> > > activities: (a) Examine the current context; (b) Perform some GUI
> > > operations (setSize, setText, etc.); (c) Compute a new context based
> > > on the current context and on information obtained from the GUI
> > > (getText, etc.). The caller (henceforth: "dispatcher") takes the
> > > returned context and will use it as the new current context, the next
> > > time a context processing function is invoked.
> > > This means that when you register event handler with a Swing widget
> > > the handler needs to to call the ACP dispatcher passing it a context
> > > processing function.
> > > The net effect of this approach is that only the dispatcher has to
> > > deal with mutable state. The context processors are functional: they
> > > merely compute the new state from the current.
> > > application-context-pattern.clj (http://groups.google.com/group/ > > > clojure/web/application-context-pattern.clj) shows a concrete example.
> > > It's about 140 LOC (ripped off from the real Waterfront codebase)
> > > structured as follows:
> > > Lines 1..40: General-purpose helpers.
> > > Lines 40..90: The ACP infrastructure
> > > Lines 90..140: A quick sample, built around ACP.
> > > The sample program opens a JFrame with two buttons: Input and Output.
> > > A click on the input button will pop-up an input dialog box. A click
> > > on the output button will pop-up a message box showing the last value
> > > entered into the input box. There's also a JLabel showing the length
> > > of the input, but let's ignore it for the moment.
> > > The entry point into the ACP world is the bootstrap function. It takes
> > > two parameters: a context processing function and an initial context.
> > > In the example, this is carried out at the bottom of the run-it
> > > function:
> > > invoke-later is a utility function that is mapped to SwingUtilities/
> > > invokeLater.
> > > Let's drill down into the build-ui function: It takes the current
> > > context (ctx parameter). Then it creates the frame and the buttons. It
> > > uses new-action-listener (another utility) to register an action
> > > listener with the buttons. The first listener looks like this:
> > > ((ctx :dispatch) get-input))))
> > > It uses (ctx :dispatch) to obtain the dispatcher from which ctx was
> > > obtained, and evaluates it passing get-input as the context processing
> > > function. The call to bootstrap initialized this dispatcher and added
> > > the :dispatch mapping to the initial context.
> > > It pops-up an input box, and returns a new context which is the same
> > > as the current context except that :user-input is now mapped to value
> > > returned from the input box.
> > > show-output is the context processing function for the output button:
> > > (defn- show-output [ctx]
> > > (javax.swing.JOptionPane/showMessageDialog nil (ctx :user-
> > > input)) )
> > > Note that show-output returns nil which the dispatcher interprets as
> > > "no change to ctx".
> > > What we have is that get-input communicates with show-output by
> > > returning a new context. There's no assignment into atoms or the
> > > likes. The mutable state is encapsulated within the dispatcher.
> > > It is now time for the JLabel to step into the plate. We now want to
> > > add a JLabel that will show the length of the user input. We want this
> > > label to be updated on the fly, with no explicit user request. This
> > > type of behavior is common in GUI applications. To this end, the
> > > dispatcher also supports the notion of observers. In ACP an observer
> > > is a function takes two contexts: old-ctx and new-ctx.
> > > An observer typically compares the contexts. If the mappings it is
> > > interested in were changed, it carries out these activities:
> > > (a) Updates the GUI (setText, setEnabled, etc.); (b) Computes a new
> > > context (that is: a context that is even newer than new-ctx). After a
> > > context processing function was invoked, the dispatcher will run all
> > > observers in a loop until a steady state was reached.
> > > Here's the observer that takes care of updating the label:
> > > (defn- input-observer [old-ctx new-ctx]
> > > (when-not (= (old-ctx :user-input) (new-ctx :user-input))
> > > (.setText (new-ctx :label) (str "Input length: " (count (new-
> > > ctx :user-input)))) ))
> > > One register an observer by adding it to (ctx :observers):
> > > (assoc ctx :frame f :label label :observers (cons input-
> > > observer (ctx :observers)))
> > > There all sort of variations on these theme and some subtleties, but I
> > > want to keep this post coherent so I'll skip these for now. Anyway,
> > > all the fundamentals are here. As you can see this pattern is quite
> > > powerful. Almost all functionality of Waterfront is built on top of
> > > this.
> > > For instance, the menu system is described as a DSL (a vector of maps)
> > > mapped to the :menu key of the context. An observer keeps an eye on
> > > the :menu mapping and rebuilds the JMenuBar whenever it changes. Also,
> > > the plugin-loader is an observer that watches the :plugins list. When
> > > a new entry is found, it loads this plugin. In fact, in Waterfront,
> > > the startup function simply registers the plugin observer and loads
> > > the list of plugins from a file.
> Does anyone have similar example using one of the cells
> implementations?
> What would be pros/cons between this and cells approach?
Sorry for a late reply but some of the code didn't exist when you
asked your question. This example can be done using neman cells and
swing wrapper like this: http://is.gd/mKRT
This shows that swing wrapper can 'erase' lexical scope so you can use
widgets before they are declared and 'bind' macro, part of cells lib,
that will sync text property of label with value of user-input cell.
Libs are very experimental, so this is more a feature preview for now,
not something you would use just yet.