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.


-Itay


> 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:
>
> >   (defn run-it []
> >     (let [build-ui (fn [ctx]
> >       (let [f (javax.swing.JFrame. "Frame")
> >             b-in (javax.swing.JButton. "Input")
> >             b-out (javax.swing.JButton. "Output")]
>
> >         (.addActionListener b-in (new-action-listener (fn [event]
> >           ((ctx :dispatch) get-input))))
>
> >         (.addActionListener b-out (new-action-listener (fn [event]
> >           ((ctx :dispatch) show-output))))
>
> >         (.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.
>
> > (file:http://groups.google.com/group/clojure/web/application-context-patter...)
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to