[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]


> Other (potentially) interesting points regarding JVM vs. the CLR can
> gleaned from reading:
> pdf
> This is a comparison written up by Dr. Gout, and full of useful
> information.

Also have a look at a more recent paper I have done with John Gough

In my view, the main job of a language implementer is to bridge the
semantic gap between a language and the underlying execution
environment. One way to shorten the gap is to lift the level of
abstraction of the underlying execution environment. This is what
language folks like us have been doing for a long time when designing
abstract machines and runtime support (garbage collection,
continuations, closures) for our languages.

Even for execution environments and languages that are designed together
the match is often not perfect for various reasons. For example, the JVM
does not support inner classes and generics, so these concepts have to
be compiled into more primitive notions that the execution environment
does support. This is not necessarily a bad thing, as long as you are
able to do it (which is true whether you compile directly to machine
code, to C, to your own tailor made abstract machine, or whatever).

People often criticize the CLR because their favorite features are not
directly supported by the CLR. For certain of these features it makes
sense to have direct and cross-language support. For others it is OK if
they can be compiled away, so the concept exists within one language but
does not exists as a semantic concept in the underlying execution engine
(like inner classes in Java), or can be exposed in terms of underlying
primitives in a form that is useful for other languages (Eiffel for
example leverages on multiple inheritance on interfaces and the fact
that interfaces can have static methods to expose MI in Eiffel classes
to the outside world).

Another issue is that often features in different languages look similar
but are subtly different (e.g. multiple inheritance/mixins, closures
with or without side-effect and lazy evaluation) and there is no
agreement on what the best or universal solution is. Other features are
so language specific or obscure that they make little sense in other
languages (I will refrain from giving specific examples). A third
problem is that things that make sense in one language might cause
feature interaction when transferred to a more general setting, for
example, how do continuations interact with security mechanisms based on
stack walking or security passing style.

So whenever you think about multi-language execution environments, you
should very carefully partition the features into several categories,
ones that a multi-language runtime should support directly because they
are universal amongst many languages, ones that are only relevant for a
small set of languages, and for the ones that you do include you need to
know how they interact with all the others.

Many missing features that people mention can be implemented indirectly
on the CLR (John Gough's books has a chapter that describes how to
implement several things such as multiple inheritance, covariant
overriding, structural equivalence of procedure types etc). In fact,
David Hanson has implemented an MSIL backend for lcc and can compile
full ANSI-C (except for setjmp/longjmp). For others (for example
generics), we do believe that direct support would be useful, and we
work hard to get the design such that as many languages as possible can
benefit from them.

Note that the CLR introduces several new features that did not exist in
programming languages before, but solve real world problems. One
excellent example is versioning based on strong names. According to
Clemens Szyperski this is one of the major innovations in component
based programming in the last few years. By targeting the CLR and the
.NET frameworks, it becomes also easy for languages to be used to write
ASP.Net pages and XML Webservices, this is a great example of leveraging
on a common infrastructure. 

The other important thing that a language implementer/designer has to
make sure is that his/her language can interoperate with the rest of the
world so that users can access external libraries and use software
components written in other languages. 

Now if different languages share a common execution environment, it
becomes much easier for them to closely interoperate, even if these
languages each have their own different semantic notions. The fact that
you share one heap, have one threading system, that you can leverage on
a common infrastructure takes away a lot of the accidental complexity
that you face when you have binary interoperability or have to
synchronize two execution environments on a low level as anybody will
acknowledge that has done serious work on foreign function interfaces. 

Hope this helps,

Erik Meijer