Search |
||
Thinking Declaratively in JavaFXPosted by javakiddy on May 26, 2009 at 11:23 AM PDT
JavaOne is coming up, and with it no doubt a slew of enhancements to JavaFX. Many of you reading will have no doubt dipped your toe into the waters of Sun's new platform, but how well do you really understand the power of its Domain-specific language, JavaFX Script? I've had a pretty good excuse to write lots of JavaFX code of late, and to be exposed to some of the difficulties programmers first have when approaching the language. One of the common stumbling blocks seems to involve the shift in thinking from purely procedural source code (where the function/method is our chief currency) to the declarative source code supported by JavaFX Script (where code may be nested into a tree structure). So, what I'm going to do in this post is take a problem ripe for declarative exploitation, and show how JavaFX Script might change the way we tackle the solution. For newbies struggling to understand the power of JavaFX, hopefully this will be an eye opener. For the uninitiated, perhaps it might demonstrate how JavaFX Script differs from Java and other languages. The example I've chosen is the parsing of an XML file — a simple enough task one might think, but a task which highlights the stark contrasts between the structure of the document being processed, and the structure of the source code processing it. (Hopefully that last sentence will make a bit more sense by the time you've finished reading this posting!) To parse XML in JavaFX we would typically reach for the Consider the (slightly contrived) XML below:
Both If the event is a 'text' event, and our immediate parent is A bit of a mouthful, just to ensure we know which bit of text in the XML we're processing. And remember, the only way of knowing the lineage of an event is to keep track of where we are in the document ourselves. Things get even messier when we want to populate objects as we process the XML. Suppose we created a couple of classes, one to hold customer details, the other to hold product details, and we intend to populate and store objects of these classes as we walk over the XML.
Because each XML element is delivered as separate, discrete, events we can't use a local For any non-trivial XML document the event handling code quickly grows in complexity and acquires a burgeoning collection of private variables for maintaining state. The code starts to become a tad spaghetti-like, and inflexible — even a simple change to the XML format may require a lot of code hacking. The problem is, of course, we're trying to apply a very linear way of working to a non-linear data structure. If only we could write our event handling code as a tree, so it would mirror the XML. Expert JavaFX Script programmers will be able to guess what's coming next.
Note how we now handle the creation of those
The The above is only an example, and far from perfect. For large XML documents the The beauty of JavaFX Script lies in its ability to shape code around the data structures we work with. By utilising JavaFX Script's declarative syntax we were able to transform a (potentially troublesome) linear event handler into an event tree who's source code mirrored the document being parsed. The result was more readable, flexible, and easier to maintain. So, bottom line: don't just think about code to manipulate your data, start thinking about code which mimics (models) your data... :) [Source code] (3k) »
Related Topics >>
J2SE Comments
Comments are listed in date ascending order (oldest first)
Submitted by opinali on Tue, 2009-05-26 12:31.
This is very good, but I think JavaFX has potential do do better. I don't like that you have to declare variables and assign values to properties; this is not purely declarative. We could have a more advanced parser library that allows to, somehow, bind object properties to parts of the XML (perhaps with XPath?), so the "parser" would be mostly an object initialization tree, something like this:
Customer {
name: xmlBind("customer/name")
address: xmlBind("customer/address")
}
This is just a very simple and raw example of what I mean. The most important principle is: having no explicit assignments (so we can parse stuff into public-init properties; public [writeable] properties suck in a language with good potential for the functional style).
A decent proposal would show how to use generators to initialize a sequence from a XPath that returns multiple elements; allowing either full XPath expressions or "local" qnames considering scope; perhaps even type inference (with help of a XSD available at compile time) so you don't have to convert data to non-String properties...
Submitted by mikeazzi on Tue, 2009-05-26 14:33.
I think one of the most fundamental, and often overlooked feature of JavaFX Script is its declarative programming style. It is this declarative style that gives it such an expressive power, and enables developers to so easily use higher level abstractions to represent, and manipulate low level primitive 2D constructs in their applications. As this nice example has shown, libraries/utility packages developers have yet to fully appreciate, and take advantage of the expressive power of JavaFX's declarative style of programming. It takes a little bit of an effort, and clear thinking to not allow ourselves be influenced by such a long heritage of purely procedural style of programming.
This declarative style is also a perfect fit with the functional programming aspect of the language and the combination gives such a tremendous expressive power. I believe this declarative style is what really sets apart from other functional programming languages like Scala, and Clojure. JavaFX in a way makes functional programming more approachable, more mainstream, more blue collar so to speak. There are some important things though that are still missing from the language which could potentially make JavaFX the main programming language of the next decade, such as actor based concurrency model, and software transactional memory, and other things. If, and when it gets those it will be unmatchable.
Submitted by javakiddy on Wed, 2009-05-27 07:37.
@opinali: I like the idea, but I can't see how one could effectively bind directly against elements of the XML, as a lot of XML has repeating blocks.
Given that a lot of XML tends to consist of the same sub-structures repeating over and over inside a container (like the above were [customer] is the sub-structure and [customers] is the container) I wonder if it's possible to use a FOR loop to iterate over the container, and the Element tree to translate each sub-structure into an object?
What we need is a sequence which wraps a given part of an XML DOM. Hmm...
Submitted by meldahl on Mon, 2009-08-10 09:09.
I greatly admired your declarative effort but it is simply not a solution to the challenge of XML. We need to exploit the powers that a new language gives us but there are limits to these powers and XML reveals the limits of JavaFX's declarative power.
To elaborate on the observation of the previous comment, XML is a hierarchical model and requires a recursion as a general solution. There is no limit to the possible depths of nodes in an XML document and only recursion can guarantee that those depths will be reached. Recursion requires, by definition, a procedural solution rather than a declarative one (this procedural solution does not rule out an object-oriented model where objects have procedural behavior).
For example, take an XML schema that allows for only two kinds of elements: "Leaf" elements that cannot have children and "Branching" elements that can have children of both the Leaf variety and more Branching elements (Branching elements can recursively contain more Branching elements). Branching elements simply cannot be resolved in a declarative manner. The solution to the Branching elements requires a procedural call from the function that resolves Branching elements to the same function that will resolve the Branching elements at the next lower layer.
I have taken your idea however, and have come up with a solution that exploits the declarative power of JavaFX with recursion. I will provide it in detail in the coming weeks on my blog site http://xmlbi.blogspot.com/.
Submitted by meldahl on Mon, 2009-08-10 14:34.
A method of using the declarative power of JavaFX in a solution that requires recursion (such as XML) would look something like the following pseudocode:
------------------------------------------
function processNode( n: Node) : CompositePatternObject {
......NodeDeclaration d = declarationHashMap.get( n.nodeType() );
......CompositePatternObject {
............parentObject : d.createUserObject( n )
............childrenObjects: recursively call processNode() for each childNode
......}
}
------------------------------------
The CompositePatternObject is simply a wrapper for the user object created from the current node attached to the children user objects created from child nodes.
The primary thing is that for each XML schema, the user would have to load the HashMap with declarative object factories that would basically use "object literal" syntax as found in your solution above.
Submitted by meldahl on Mon, 2009-08-10 19:49.
Actually, in the pseudocode submitted in the previous comment, the hashMap should be keyed on "n.nodeName()" rather than "n.nodeType()".
Submitted by javakiddy on Tue, 2009-08-11 09:00.
@meldahl : "To elaborate on the observation of the previous comment, XML is a hierarchical model and requires a recursion as a general solution. There is no limit to the possible depths of nodes in an XML document and only recursion can guarantee that those depths will be reached. "
The idea is to create a specific declarative structure which matches a specific XML schema. Although, in theory, an XML documents can have element types nestable within themselves (like your "Branching" element, creating unlimited depth), in practice most schemas don't permit this (or only do so in a few locations within the document). This is particularly true when dealing with web services -- remote functions which return tree structures of unlimited depth are the exception, not the norm.
Although, admittedly, my solution doesn't work 100% of the time, it makes life a lot easier for the 99% of cases it does work for -- meaning you only have to fall back to long-hand procedural/recursive coding for the other 1%. ;)
Submitted by meldahl on Tue, 2009-08-11 09:50.
Thanks for your reply. XML itself is recursively defined as a tree of nodes of unlimited depth. There is much evidenced that the mind itself organized concepts as encompassing each other in a hierarchical manner. My "branching" example was provided as a simple example of a nesting paradigm that is ubiquitous in data organization (a hierarchical tree has even been used as a symbol of computer science).
My small algorithm works for all XML and limits the schema dependency to the definitions of the individual elements only rather than the fragile structure of the whole schema definition.
In any case, I appreciate your example of the power of declarative programming. It was a worthy experiment for me and has influenced my design. Thanks.
Submitted by javakiddy on Tue, 2009-08-11 10:48.
@meldahl : "Thanks for your reply. XML itself is recursively defined as a tree of nodes of unlimited depth. There is much evidenced that the mind itself organized concepts as encompassing each other in a hierarchical manner. "
That's why I was trying to create a hierarchy from the event handlers that process the document. ;)
When your processNode() gets called for an element called [name], how does it know whether it's [name] inside [customer], or [name] inside [product] ...?
In my solution the event handling code for [name] never needs to ask whether it's a child of [customer] or [product], because the hierarchy implies the context. Procedural (even recursive) XML processors spend far too much time keeping track of their context (if only for error checking). That was the problem I wanted to fix.
Actually it shouldn't be that difficult to adapt my solution to work for infinite hierarchies. Say we had a branch/leaf XML, the event handler for branch->branch (a branch elenent within a branch element) simply invokes a new XML processor using the same node tree. In this way, we can process any number of nested branch elements (and each leaf) ;)
|
||
|
|