Lots of confusion in the last JavaPosse ep (Roundup on static v. dynamic).

1 view
Skip to first unread message

Reinier Zwitserloot

unread,
Nov 5, 2009, 4:19:13 AM11/5/09
to The Java Posse
I wish I was there, because the vast majority of points raised have
absolutely zip squat to do with dynamic vs. static.

1. Growing existing types - can be done globally (so called dynamic
scoped), which is awful, because once your project grows beyond the
small, everyone overriding everything just makes it impossible to
figure out what is what, and you get conflicts. This is what rails
does - it redefines half the ruby core. An alternative is lexically
scoped extensions, which is really much nicer; an import statement
adds methods to, say, all strings, but *ONLY* inside the file that
contains the import statement. Both C# and Scala (both very much
statically typed languages!) can do this. Clearly, no relation
whatsoever to dynamic v. static typing.

2. Growing interfaces - nobody can do that without stuff breaking. If
you need to support pre-extension stuff while still extending, with
default behaviour, then you can do that. Scala can do this (traits),
and scala is statically typed. Clearly has not one iota to do with
dynamic v. static. For those who don't know scala: Imagine you could
give interface methods a method body, which will be used for all
classes that implement your interface but do not specify an
implementation. This method body can't use any of the fields of the
class, of course, so it's not as unique as being a superclass. Voila:
Extensible interfaces, in a fully static language.

3. duck typing the way it was explained in the roundup - in other
words, ad hoc interfaces, in other words: Give me any object that has
a 'doFoo();' method. Again, has nothing to do with typing systems. In
scala, you can make structural types which do exactly this: You can
create a type that says: Anything that has a doFoo() method. If you
then accept this type as, say, a method parameter, you can call doFoo
() on this parameter, and any object of a type that includes a doFoo()
method can be passed in.

4. Static typing is verbose. No, it isn't. This has nothing to do with
static typing.

Time for a rehash of the 4 dimensions of type systems.

Weak vs. Strong: This refers to whether or not the *OBJECT* knows its
own type. C and C++ are *WEAK* even though they are also static. You
can cast, say, a String to an Integer object, then get core dumps as
the methods that work on Integers work on String's data structure
which is nothing like Integers. Python, Ruby, Javascript, Java, C#,
and Scala are all Strong. Basically, if objects have a .getClass()
kind of method, you're looking at strong typing. Strong typing also
implies you get cast errors of a sort. So, in python, when you call a
non-existent method on an object, you don't get a core dump, or
unspecified behaviour. you get clear behaviour: A method missing
error.

Static vs. Dynamic: Whether *REFERENCES* have a type. So, in: "Object
x = "foo";", x is a reference, and its type is "Object". x, being a
reference, points at something. It points, in fact, at a string, which
has a String type. The fact that "foo" knows its a String means java
is strongly typed. The fact that 'x' also has a type, means java is
statically typed. In e.g. javascript, which is dynamically typed, you
just write "var x = 'foo';" and as a result 'x' itself has no type,
but the thing x is pointing at, does. *THIS*, and only this, is static
vs. dynamic typing. You can of course mix the two, which is just what
groovy and C# do, even though most people refer to these as 'static'
languages. They are actually hybrids. Java is very much static.
Sometimes, latent typing (where the compiler INFERS the type of a
reference, instead of just not caring about what it is) is confused
for dynamic typing. Don't do that.

Nominal vs. Structural: A type is simply an assertion about an
object's nature. Thus, this: [Anything with a getFoo() method],
qualifies as a type, technically. There are two ways to think about
types: You can think about what a type says about structure, or you
can think of types by themselves, generally identified by an
identifier (e.g. a name). This is nominal vs. structural typing. Java
is 99% nominally typed; if I have: class Foo { void shoot(); }, as
well as interfaces Gun and Camera, also both with shoot() methods,
then a Gun isn't a Camera, and a Foo is neither a Gun nor a Camera.
They ALL have the exact same structure (namely: I have a shoot()
method), and yet they aren't the same type, because they don't have
the same name: Thus, nominal typing. Java is structural in how it
handles main methods - a runnable class is one that has a static void
main(String[] args) method, regardless of its nominal types (the
interfaces and superclass). Scala is a hybrid; you can pick and mix.
Most dynamic languages are structural. Just interact with structure
(call methods, read or write fields), and if the object has that
structure, it works, and if not, it doesn't. An obvious downside to
structural typing is that you can shoot yourself in the foot due to
lack of namespacing - namespacing every method is rather tedious, so
you don't do it, and then you get to call shoot() on a Gun, thinking
it was a Camera.

You can mix structural and static if you want: In such a language, you
would be able to cast a Camera to a Gun without any issues, because
they have the same structure. If you want to create a method that
takes 'any object with a foo()' method, you just create an interface
with a foo() method, make it private if you want, even, use that as
method parameter type, and it'll work. This isn't how java works, of
course, because java is nominally typed. BGGA's (the closure proposal)
biggest downside is that it turns java into a structural/nominal
hybrid, as it introduces stuff like {String, String => Integer} as a
type, which is obviously structural.

Latent vs. Manifest: When you write: List<String> x = Arrays.of("a",
"b"); - you don't actually specify that Arrays.of() is going to build
a list of strings. It's not inferred from the left, it's inferred from
its arguments (which are both strings). Yet, it is very much static
typing: That is functionally equivalent to Arrays.<String>of("a",
"b");, but it's just that the compiler inferred the <String> part of
that. Contrast this to dynamic languages, where "String" (or List)
isn't needed. Java infers very little, but scala infers more, and
haskell infers a lot more. Inferring types means you have a latent
language, and if the language mostly forces you to be explicit, you
have a manifest language. Virtually all languages are hybrids, and
this term doesn't really apply to non-static languages. Boo is almost
exactly like python, but statically typed. It's very latent, so almost
all python code just runs as boo, and yet one is static, and the other
is dynamic. A lot of the 'it's all so verbose' complaints actually are
complaining about java's manifest tendencies. Has nothing to do with
static v. dynamic.

Example:

You could easily add the following rule to java, so that the following
is legal, and figures out that you must have meant that the method is
to return Numbers:

def foo() {
if (something) return 1;
else return 2.0;
}

because the most specific commonality between 1 and 2.0 is
java.lang.Number. This isn't legal java, but it could be. That would
be an example of latent typing - no need to specify types, the
compiler infers them.



Only after you understand all these distinctions, you can make
reasonable arguments about pros and cons, without the discussion being
all over the place, drawing all sorts of faulty conclusions, like the
roundup. For example, you can weigh the benefits of shorter code
(which isn't so much about not typing much, but more about reading
less, which can help), against the obvious conclusion that with a lot
of latent typing, the compiler has to do a lot of inference, and if
the compiler gets it wrong, the resulting error messages tend to be
cryptic. This is why haskell's cryptic errors if you make syntax
mistakes is something many people think is a fundamental, unfixable
aspect of that language. Most latent languages allow you to specify if
you want to, though.


The central argument between static v. dynamic usually boils down to:
Is it worth it. Types itself are easy, and between using latent typing
and the simple fact that you can't unit test comments, so moving
anything you'd stuff into a doc-comment in python into an explicit
typing system is always a good thing - seems to indicate Static is in
all ways superior to dynamic. However, to do it right, you need
generics, which have co- and contravariance, and this is where things
get rather complicated. Is it worth it to avoid cast bugs? Heck no, it
isn't. Cast bugs are rare, and the complexity introduced when you want
to do serious generics-fu is massive. That's the standard argument of
dynamic typing fans, and it shows that they don't understand what
static typing is for, because:

Static typing is all about documentation (which fortunately WAS raised
in the roundup!) - you're going to have to document the structure of
what your method returns anyway, might as well do so in a way that the
compiler can help you out, and in a way you can test. Static typing
make auto-complete work.

So the real question is: Is the considerable complexity of e.g. co/
contravariance a fair cost compared to the value of having tool-
readable documentation about how your program chunks behave, which
those tools can use to do e.g. much smarter refactoring? It's question
for the ages - and I'm happy the discussion did eventually get around
to this.

Luis Miranda

unread,
Nov 5, 2009, 4:05:43 PM11/5/09
to The Java Posse
I haven't listened to this episode yet, but... thank you for a most
informative post! I was ignorantly classing languages in only 2 of
those dimensions up until now. :)

Jan Van Besien

unread,
Nov 6, 2009, 2:16:44 AM11/6/09
to The Java Posse
I second that. I feel like I just learned a lot by reading your post.

Jan

Reinier Zwitserloot

unread,
Nov 6, 2009, 2:55:03 AM11/6/09
to The Java Posse
Thanks, guys ;)

Ben Schulz

unread,
Nov 6, 2009, 11:54:12 AM11/6/09
to The Java Posse
> [...] because java is nominally typed. BGGA's (the closure proposal)
> biggest downside is that it turns java into a structural/nominal
> hybrid, as it introduces stuff like {String, String => Integer} as a
> type, which is obviously structural.

You've asserted that before, prompting a response by none other than
Neal Gafter himself [1]:

"Well, not quite. List<String> is the same type as List<String> not
(only) because they have the same name, but because they have the same
type arguments. That's structural typing, and generics are indeed
structurally typed. As are arrays. Java is not strictly nominal. "

Really put's that "biggest downside"-thing into perspective. :P

With kind regards
Ben

[1] http://groups.google.com/group/javaposse/browse_thread/thread/702ae9036375d1bb/6897a83891cb3584#6897a83891cb3584

Reinier Zwitserloot

unread,
Nov 6, 2009, 4:14:23 PM11/6/09
to The Java Posse
It's also a ridiculous line of reasoning. Like most things Neal says
when you attack his proposals, it boils down to overly pedantic drivel
- technically correct, practically speaking irrelevant or misleading.

Take this very argument, for example: How else would nominal generics
even work? There's no such thing as nominal generics. You could, I
guess, create something like:

public typedef StringList extends List of String;

But, as you clearly can't create such a typedef for _every_ type in
existence out of the box in the java.util package (do we also create a
StringListListListListMapSetListListSetMap?), the rule would have to
be that any typedef matching 'List of String' matches any other.
Regardless of such things as package or name of the typedef. Which is
simply another take on structural typing.

If you think about it for more than 2 seconds, you realise that
generics fills a role that is all about the structure. Asserting type
bounds is a fundamentally structural activity. "Nominally typed
language" does not mean "language devoid of structures"! Thus,
generics has been implemented structurally, because there is no other
(practical) way.

Contrast this to BGGA, which tosses structural typing in there for no
good reason. Quite unlike generics, closures aren't specifically
filling a role that makes sense only with structurally typed
programming concepts, and doing closures with structural typing isn't
the only way. In other words, BGGA *chooses* to go the structural
typing route, whereas the concept of generics itself hardcodes
structural typing. Quite a big difference, as it has made BGGA far
more complex than my closure proposal, which still supports as much
power as BGGA (including full transparency). Possibly turning java
into a structural/nominal hybrid is a good idea, but I'd rather see 2
separate proposals for that instead of the big ball of scary, loosely
related changes that BGGA has become.

But, why not. I'll take back that BGGA's messing with the fundamental
nature of java's type system is its biggest downside. It's _a_
downside. It's got at least 2 more enormous whoppers, none of which
are relevant to this thread. I'd be glad to participate in a thread
about the pros and cons, and particularly the pragmatic realities of
what it takes to even have closures in java (including how some syntax
pain is unavoidable, something most BGGA dislikers don't really
understand, and how most of the extreme complexity of the BGGA
proposal, such as the 'throws E' generics, are not necessary), if you
and/or others feel that's an interesting discussion to have. Open a
thread and I'm sure I'll find it.
> [1]http://groups.google.com/group/javaposse/browse_thread/thread/702ae90...

Serge Boulay

unread,
Nov 6, 2009, 5:23:07 PM11/6/09
to java...@googlegroups.com
Reiner,

Could you please post a link to your closures proposal.

Thanks
--
Sent from my mobile device

B Smith-Mannschott

unread,
Nov 6, 2009, 6:19:05 PM11/6/09
to java...@googlegroups.com
On Nov 6, 5:54 pm, Ben Schulz <ya...@gmx.net> wrote:
> > [...] because java is nominally typed. BGGA's (the closure proposal)
> > biggest downside is that it turns java into a structural/nominal
> > hybrid, as it introduces stuff like {String, String => Integer} as a
> > type, which is obviously structural.
>
> You've asserted that before, prompting a response by none other than
> Neal Gafter himself [1]:
>
> "Well, not quite. List<String> is the same type as List<String> not
> (only) because they have the same name, but because they have the same
> type arguments. That's structural typing, and generics are indeed
> structurally typed. As are arrays. Java is not strictly nominal. "


On Fri, Nov 6, 2009 at 22:14, Reinier Zwitserloot <rein...@gmail.com> wrote:
>
> It's also a ridiculous line of reasoning. Like most things Neal says
> when you attack his proposals, it boils down to overly pedantic drivel
> - technically correct, practically speaking irrelevant or misleading.
>
> Take this very argument, for example: How else would nominal generics
> even work? There's no such thing as nominal generics. You could, I
> guess, create something like:
>
> public typedef StringList extends List of String;
>
> But, as you clearly can't create such a typedef for _every_ type in
> existence out of the box in the java.util package (do we also create a
> StringListListListListMapSetListListSetMap?), the rule would have to
> be that any typedef matching 'List of String' matches any other.
> Regardless of such things as package or name of the typedef. Which is
> simply another take on structural typing.

I disagree with this characterization. I think Neal's line of
reasoning, above is sound and his example of arrays demonstrate this
even more clearly than generics.

With generics, it's at least possible by make a nominal type out of a
structural type by the simple expedient of:

class StringList extends List<String>;

With arrays in Java, we can't even do that. Classes and Interfaces are
the only kinds of types we are allowed to name in Java. This is not
nonsensical, it's merely impossible in *Java*. It's perfectly possible
in a language that's more nominally typed than Java:

TYPE Point3D = ARRAY 3 OF REAL; (* Oberon *)
TYPE String = ARRAY OF CHAR;

Oberon is generally very much nominally typed (as you may know,
judging by the breadth of knowledge demonstrated in your first post on
this tread) supports naming this type. But even in this language,
there is also structural typing. Tellingly, one such case are
procedure types.

TYPE Handler = PROCEDURE (VAR m: Message);
VAR h: Handler;

PROCEDURE DoSomething(VAR m: Message);
BEGIN
END DoSomething;

The variable h, which has a procedural type via the named type Handle
is assignment compatible with the procedure DoSomething (which is a
constant) despite the fact that there is no agreement in their nominal
types. (Procedures don't have nominal types). (How tedious would it be
if we had to name a type for every procedure we wanted to define?)

So, in summary:

(1) Java has always used a mixture of nominal and structural typing.
(2) I consider how procedural types are handled in other languages
I've seen and imagine these design choices were not made without
reason. I suspect that Closures, in particular, would tend to drive a
language designer to structural typing.

We can of course argue about whether adding another case of structural
typing to Java on top of what's already there is a good idea, but
there's no sense in pretending that Java doesn't already mix both
structural and nominal typing.

As to BGGA. I've never liked it. Far too much syntactic and conceptual
complexity for the power it promised. I would have preferred CICE or
better yet FCM, though that never gained much traction.

// (a different) Ben

Viktor Klang

unread,
Nov 6, 2009, 6:26:09 PM11/6/09
to java...@googlegroups.com
Ack!
Arrays are the Devil, especially in Java.
They are a shallow abstraction over contiguous memory and don't play nice with the type system.

I'd rather have them as performance optimizations done by the JVM instead of handcrafted by developers.

Also, I'd rather see a String as a Sequence of Chars



--
Viktor Klang
| "A complex system that works is invariably
| found to have evolved from a simple system
| that worked." - John Gall

Blog: klangism.blogspot.com
Twttr: twitter.com/viktorklang
Code: github.com/viktorklang

Reinier Zwitserloot

unread,
Nov 7, 2009, 1:49:22 PM11/7/09
to The Java Posse
We're smithing the syntax together on the lombok googlegroups, which
also contains plenty of background on how it works. A fully written
out proposal won't happen for quite a while; instead we are going to
focus on getting a working eclipse and javac plugin out via project
lombok, along with a 'how to' based primarily on examples.

On Nov 6, 11:23 pm, Serge Boulay <serge.bou...@gmail.com> wrote:
> Reiner,
>
> Could you please post a link to your closures proposal.
>
> Thanks
>

Reinier Zwitserloot

unread,
Nov 7, 2009, 2:05:04 PM11/7/09
to The Java Posse
In what way is your:

TYPE Point3D = ARRAY 3 OF REAL;

any different from my hypothetical:

typedef StringList extends List of String;

- because, as I mentioned, either of those are either (A) still
structural typing, if "TYPE Floobargle = ARRAY 3 of REAL;" is
interchangible with a Point3D, or (B) a fundamentally different
concept, if they aren't interchangible, and roughly equivalent to:
public class StringList extends List, with some minor syntax sugar
thrown in to tighten type limits.

As my original post hints at, almost all languages are hybrid if only
you theorize long enough. So, instead of getting boiled down in a
purely pedantic waste of time, I'll rephrase to:

Java with BGGA would move java quite a ways towards the left on the
Structural <-> Nominal typing axis. This is a major change, which has
a great impact on the feel of java, and it is not necessary for a
closure proposal, even one that supports full transparency.


Can we all agree on that, instead?


NB: A closure proposal with transparency and without structural types
works roughly like this:

(1) Closure literals are legal only in an environment with an obvious
and unambiguous target type. In other words, you can only pass them
verbatim as a method parameter, or as the expression of an assignment
operation. This is in practice not a big loss; hierarchical closure
types don't have much use (e.g. where I implement, say, a
"FooBarListenerChild" instead of a "FooBarListener" - it's kind of
hard to even imagine what that means, and doing direct operations on a
closure that you just defined is similarly pointless (what are you
going to do, call the apply function on it? Then you could have just
written the code verbatim. You create closures so you can hand them
off or store them).

(2) Via target typing, you always know exactly what kind of nominal
type the closure was meant to become. Thus, something like:

Collections.sort(someStringList, #(String a, String b) {
inner.return Integer.valueOf(a.length()).compareTo(b.length());
});

would be legal, would be transparent*, and is fully nominally typed;
the compiler will immediately attempt to infer a type on this closure,
which in this scenario is possible, as there's only one matching
method named 'sort' in java.util.Collections that fits the obvious
aspects of this method call (2 arguments, first is a List<String>,
second is a closure taking in parameters 'String, String'). In other
words, this is static, nominal, latent typing, not static, structural
typing. It's also latent/manifest hybrid:

Collections.sort(someStringList, Comparator#(String a, String b) {
inner.return Integer.valueOf(a.length()).compareTo(b.length());
});

which is not just nice for those who dislike latent typing, but also
serves as an easy mechanism in the face of inference confusion, for
example due to method overloading.

*) "inner.return" is not syntactically transparent, at least not
according to TCP. However, the 'return', 'break', 'continue', and
'throw' statements change meaning depending on their lexical context,
but TCP-style transparency dictates that code can be moved around in
the lexical context without affecting its meaning. Therefore,
discussing transparency on the *syntax* of return, break, continue,
and throw is a meaningless proposition. The only important aspect is
whether the *operations* of returning both from the closure itself and
from the enclosing method, for example, are supported. If you don't
agree with this view on TCP vs. break/continue/return/throw, assume
this pseudo-proposal uses the BGGA_style 'last expression in the
block' for local returns instead; works just as well.

On Nov 7, 12:19 am, B Smith-Mannschott <bsmith.o...@gmail.com> wrote:
> On Nov 6, 5:54 pm, Ben Schulz <ya...@gmx.net> wrote:
>
>
>
>
>
> > > [...] because java is nominally typed. BGGA's (the closure proposal)
> > > biggest downside is that it turns java into a structural/nominal
> > > hybrid, as it introduces stuff like {String, String => Integer} as a
> > > type, which is obviously structural.
>
> > You've asserted that before, prompting a response by none other than
> > Neal Gafter himself [1]:
>
> > "Well, not quite.  List<String> is the same type as List<String> not
> > (only) because they have the same name, but because they have the same
> > type arguments.  That's structural typing, and generics are indeed
> > structurally typed.  As are arrays.  Java is not strictly nominal. "

B Smith-Mannschott

unread,
Nov 7, 2009, 3:06:18 PM11/7/09
to java...@googlegroups.com
On Sat, Nov 7, 2009 at 20:05, Reinier Zwitserloot <rein...@gmail.com> wrote:
[...snip...]

> Java with BGGA would move java quite a ways towards the left on the
> Structural <-> Nominal typing axis. This is a major change, which has
> a great impact on the feel of java, and it is not necessary for a
> closure proposal, even one that supports full transparency.
>
> Can we all agree on that, instead?

Yes

// Ben

Reply all
Reply to author
Forward
0 new messages