“Dynamics Trumps Semantics”: Why Java is Easy to Learn, but Difficult to be Good at.

Andrew Glynn
8 min readNov 14, 2017

--

And those are just the categories …

While in a sense the statement above is a truism in any system that contains discrepancies between its implementation and the dynamics of that implementation when running and its semantic definition, in another sense the statement tends to swallow itself in a peculiar manner, and doesn’t help concretely in guiding any analysis of those discrepancies or their implications. Worse, although in most cases the statement is neutral, since it only acts as a generic warning, in certain cases it’s detrimental to understanding the dynamics of the implementation, and thus the discrepancies themselves and their implications.

This peculiarity arises from a combination of things: the words being conjoined, as concepts, are themselves semantic, and thus don’t contribute to a dynamic understanding; the way in which they’re conjoined doesn’t make the inherent relation between them explicit, but leaves it implicit, and this hides the manner in which the statement has to be thought; finally, how terms are conjoined in any statement is taught in an overly simplistic manner, resulting in a lack of awareness of the dynamics inherent in any conjunctive statement, which is to say, virtually any simple statement whatsoever.

The first of these doesn’t require any significant explication other than noting that concepts, as semantic, are inherently static.

The second helps to understand how the first is relevant, insofar as the implicit inherent conjunction which makes it possible to immediately understand the specific conjunction of the terms, at least on the face of it, is that semantics are inherently static, and that dynamics are inherently not, so the terms are implicitly opposed in an inherent and coherent way, again at least on the face of it. Thus, the statement, in determining which has priority, makes an obvious sense, even though what that sense is hasn’t necessarily been thought through.

The last of the three can be most simply put that in any statement where terms are conjoined, which includes all but a tiny minority of statements, the terms are both brought together and simultaneously taken apart. Since even in the tiny minority of exceptions are usually interpreted as conjunctive, but with one of the conjoined terms being implicit, in usual analytic interpretation it can be taken as true of all statements. Although this understanding of the statement is far from new, it remains relatively unthought in rational, analytic interpretation, precisely due to the static way such interpretation proceeds, and that this appears self-contradictory is itself part of the problematic (how does a static analysis proceed at all?). Since merely accepting it to be the case is of no real help, denying it is actively detrimental, and demonstrating it, much less providing a means via which it can be applied, would require a doctoral thesis length argument, I’m going to simply state it as such and leave it there, in the event anyone wants to undertake the requisite path of thinking through it.

Instead, I’m going to focus on the effects of the statement’s peculiarity in a specific domain where such discrepancies exist, and where the statement’s obvious sense can be detrimental to pragmatically understanding the dynamic behavior of the implementation of a specific semantic specification.

That specification is known as the Java programming language. Having written Java code for 21 years, using nearly every known library and framework the language ever had, I would say that, more accurately, dynamics plays with semantics, rolls it into a ball, kicks it around the park for a while and then dumps it down the hole in the nearest port-a-potty.

It’s a relevant case to look at, since more code (at least the last time I checked) has been written in Java than in all other programming languages combined, despite its newness relative to languages such as C or Smalltalk and even further newness relative to languages such as Forth, Algol, COBOL, LISP, PL/1, or of course, Assembler. A reasonably usable version of Java was first released only ~ 22 years ago, while all the languages mentioned are more than double, and some more than triple that age. Aside from the sheer amount of code, that the language is relatively new implies that a significant percentage is likely still in use, even if it doesn’t imply that a higher percentage is still in use than code written in older languages.

As far as the latter goes, a higher percentage of code written in COBOL is still in production use than any other language with sufficient usage to be relevant. This might seem surprising given the inherent issues with COBOL, but it’s largely a result of the longevity and stability of the main target platforms, rather than anything to do with the language itself. Still, it remains the case that a lot of Java code is in production use, and while it may be (and may be fortunate in some though not in all systems) that most of the code in the most crucial systems was not written in Java, there is a vast amount of important code in production written in Java, if not a vast amount of critical code. The difference between the two depends on the use case: in terms whether it’s inherently necessary, and whether a failure simply involves redoing an operation, or is already catastrophic.

How the statement in the title of this article, used as a heuristic, can be actively detrimental in analyzing the behavior of systems written in Java is not immediately obvious. Since many of the discrepancies between the dynamics of Java at runtime and the semantics of the specification are relatively obvious, at least by comparison to similar discrepancies in other domains, it also seems a bit perverse that it would be worse than neutral in that specific problem domain, and to a greater degree than most areas of the field in general. Ironically, it is that obviousness that grounds the detrimentality of the heuristic.

The way in which the statement, as a heuristic, tends to prevent more than assist in thinking through discrepancies between the Java language spec and the dynamics of a given system written in it is that since certain discrepancies are so obvious, they tend to be integrated into a developer’s semantic understanding rather than treated as discrepancies from it, and this integration, to a point, increases with experience rather than decreases. As a result, it tends to be forgotten that the discrepancies are themselves dynamic. Not only are specifics dependent on the specific case, but base discrepancies dynamically create further discrepancies, which are dependent on both the specific case and the state of the system at any given time.

While Java is by no means unique in this regard, it is towards the extreme of the spectrum, due to the percentage of specific code affected by such discrepancies, the degree of variation between versions of the base implementation (without even including variations of the base implementation such as OpenJDK), the degree to which specifics of the implemented system dynamically create further discrepancies, and the degree to which the current state of that system modifies whether and how such discrepancies affect the functioning of the system at any given moment.

The result of this is that the experience level of a Java developer is highly relative to his or her understanding of these discrepancies and their potential effects. A beginner simply doesn’t understand them, though they may have heard and accepted that they exist, and even looked at specific examples of their effects.

Understanding them in a real sense requires understanding, in at least a relatively determinate manner, at minimum:

· how the written code is dynamically affected by translation to bytecode

· how the bytecode is affected by an interpreter / JIT compiler / compiler not itself written in Java

· the differences in implication that arise from interpreted versus JIT compiled versus compiled Java and what versions, platforms and frameworks imply each

· the ways in which the statically compiled VM can affect dynamically interpreted, JIT compiled or compiled Java

· the ways in which the underlying hardware and software platform can affect the behavior of the VM

· the ways in which all the above can dynamically create further discrepancies and how such discrepancies will or may manifest

· how the state of the system affects the ways in which they will manifest, and the ways in which they may manifest

Understanding to that degree is the most general difference between a competent Java developer and a beginner. There are plenty of ‘expert beginners’ in Java development. An expert beginner in Java is someone who has attained a measure of capability in writing Java code that for the most part, works, and has reached the level that can be attained without the insights and consequent understanding that would make them a competent Java developer.

Part of the reason ‘expert beginners’ don’t get beyond that point, and that due to their ability to get most things to work most of the time have even more difficulty getting beyond where they are, arises from a semblance of such understanding having been introduced into their understanding, and then assumed that they in fact possess that understanding.

A competent Java developer may not understand much more in an immediate sense, but has become aware of how much they don’t yet understand. In that awareness, they become capable of recognizing their current limitations and progressing beyond them.

Unfortunately, this is precisely where a very specific halt in developer progress results from a tendency to integrate those discrepancies into one’s semantic understanding.

Beyond a certain point, of course, experience which is not simply a quantity but includes experience writing more complex and more diverse systems, will result in that integration breaking down, due to the necessity of solving problems that arise from it that can’t be solved at all without putting the discrepancies back into the realm where they manifest (though they often do originate in poor semantic abstractions).

In terms of a developer’s own development, having crossed that point is largely what differentiates a good Java developer from a competent one.

An expert Java developer must integrate a deep understanding of those variable discrepancies with an ability to apply them to the most complex factically existing systems, and the ability to project them from the initially static perspective of system conception and design. They must also be able to do this while maintaining the ability to use frameworks, tools and techniques that assist in implementation but make imaginative projection more difficult, including tooling and techniques such as object model to code generation, data model to model transformation generation, and other similarly complex tooling that assists with specific aspects of implementation, without which implementing a complex system becomes unreasonably difficult and expensive.

Software begins with a telos, which is both initial bounds and goal, this in turn creates aspects, vague at first, of what the goal would look like, which in the original Greek were eidos. Eventually those aspects form a coherent idea, what it would look like if you could somehow view every aspect simultaneously, and thus something realizable.

At the point of merely having a few aspects in mind, many developers in nearly every language start coding, without a full idea, never mind the imaginative projection, necessary to develop an imaginary real, which it must become in order to be actualizable.

To some degree the projection does improve as code develops and changes, but without the projection, code develops and changes too haphazardly to produce anything like the initial telos. As the goal, the final telos should resemble the initial telos in most cases. As a result it’s necessary to project far enough for that to happen, while retaining the flexibility to change individual aspects that simply don’t work in practice, which you only discover during development.

A follow on article will provide a real-world example of the types of problems that a developer needs to be at least good if not an expert, in terms of the above definitions, in order to solve.

--

--

Andrew Glynn
Andrew Glynn

Written by Andrew Glynn

A thinker / developer / soccer fan. Wanted to be Aristotle when I grew up. With a PhD. (Doctor of Philosophy) in Philosophy, could be a meta-physician.

No responses yet