Archive for the 'language design' Category

16
Oct
06

Suggested Type System

I think I am in general agreement with the viewpoint that typing should be optional not mandatory (see Is Static Typing a Form of Bad Coupling? and Plugabble Type Systems)

The one problem I have with this view is that most people who hold it seem to think typing should be a rarely used performance kludge. I’d also agree that typing should rarely be used as a performance kludge, but I don’t think that means typing should be rarely used. There are other reasons to use typing. Actually, there are quite a large number of them.

For me, the main justification has always been safety. Typing also has benefit as a form of documentation. Safety and documentation are things you want most of the time, not rarely, so why would you rarely want types?

I think one of the problems is that most people look at typing as a way that framework designers can restrict their users. But what if typing didn’t restrict users, but instead guided them? I think this would still meet the needs of framework designers. A framework change doesn’t need to have zero breaking changes. All it really needs to do is cover the reasonable points of view. If you provide a safety net, and your users tear it down, I don’t think any (reasonable) person would fault the framework designers for breaking those users.

A Metaphor

Consider programming like some kind of stunt in an Indiana Jones movie. In this stunt, you’re walking across a narrow beam, with a big pit of spikes down below. There’s also poison darts wizzing by from little holes in the side walls. Today’s type systems however fit into a few categories.

On type are harnesses that keep you from falling off, but they slow you down, and make you an easier target for the darts. These are your mandatory static type systems. Another type are safety nets, that let you fall, but at least keep you from hitting the spikes. These are the dynamically typed systems. You could go without a safety net at all, and save a bit of money and setup time. This would be assembler. Or you could go with a cheap and poorly designed harness. It would slow you down even more, and it might break and let you fall. This would be C or C++.

Although this metaphor isn’t perfect, I’d consider the darts to be complexity. You might be able to take one or two since the poison is somewhat weak, but there wizzing all over the place, and they add up. The spikes of course are the core dump that happens when runtime typing isn’t employed. Many static type systems employ both the harness and net, because the harness has a release latch. But this latch (reflection), is somewhat awkward and hard to use.

In the end we realize the best way through is a set of shields (unit tests) to protect us from the darts. But the only thing available to build these shields with is grass, and it takes a lot of grass to build a shield, since grass is kind of flimsy, our shields can only absorb so many darts. Plus we still have to worry about falling off and having to climb back up from our net.

I probably way over extended that metaphor, but it’s kind of catchy, and has a nice visual, so I’m keeping it.

Resolution

What I’d like to see is a type system that helps keep us from falling off, but doesn’t slow us down. I’m not sure how to represent this in my metaphor. Maybe as some kind of lightweight harness with a quick release/attach mechanism. When you have it on your sure you won’t fall, but if you need to do something that the cables are too restrictive for, you can detach. It’s important however that the choice to detach is not something that happens by accident. But it shouldn’t be hard to use either.

I think I’m asking for a lot here, but I do think it’s doable. If we could agree this was the way to go, we could start designing the syntax behind it. In a sense, we have all the basic ideas we need. The JVM and CLR provide just about all the necessary capabilities. The problem is that reflection is ugly and hard to use. We need a better designed latch. Even more important, we need one that’s designed for users who have a shield strapped to each forearm.

I’m afraid I may have blown the argument with that long drawn out metaphor, so I’ll probably write about this again, but it might be a fun to read anyhow, so I’ll publish.. really.. I’m doing it right .. now!

14
Oct
06

Testing Ruby

I spent a fair bit of time over the last two months with Ruby. I actually started studying it about six months before, but I hadn’t really got to the point of writing anything non-trivial with it. At that point, it was more “theoretical”, than real. So two months ago I started doing some more complex stuff. For example, I wrote this little distributed abstract syntax tree interpreter. It’s nothing to brag about, and it’s probably completely useless, so I won’t bother explaining it, but the point was to understand Ruby a bit better.

As I expected, there were a number of areas where Ruby was beautifully brilliant and easy to work with. There were some disappointments however. That’s not really surprising either, since Ruby hasn’t been in the limelight all that long, and as a result many of the more expensive peripheral things like tools and libraries haven’t matured yet. And of course, like any language/platform, there are going to be room for improvement. There will be tradeoffs, things the community is working on, etc.

To be fair, I should probably start by listing all the great things about Ruby, but plenty of people who know a lot more about Ruby than me have already done that, so I doubt I’d really add much through that. Let’s just accept that I do believe there are great things.

That said I’m going to start taking potshots. Well, potshots is a bit harsh, but since I’m sure someone will feel like that’s what they are, we’ll just label them as such, and avoid all confusion.

IDEs

The first disappointment was IDEs. I started with Ruby in Steel, an add-in for Visual Studio .NET. The beginning was rocky as the first installers had some issues, but eventually I got it installed (and the newer installers are smoother). But I was immediately disappointed because I thought there was some rudimentary Intellisense support. There wasn’t however. The SapphireSteel guys are still working on that feature for their developer edition, and it sounds like it will be better than what I expected could be done, but we’ll have to wait and see. Oh, and the debugger was being troublesome.

The next up was RDT for Eclipse. Despite also advertising code completion, I found no signs of it. And once again the debugger was very troublesome.

I was about to give up on the idea of a debugger at all, when I found ArachnoRuby. It didn’t have or advertise Intellisense, but it did have a working debugger. Actually, it wasn’t just working, but designed for dynamic languages. For example, it allows you to attach a IRB console to a running program (after a breakpoint has occurred), and make modifications just like you had run your whole program in IRB.

Beyond that, another great thing was the Ruby Class Browser, which was far more useful than the standard documentation, or RDoc for finding classes and methods. Still however, there was no Intellisense, which I think would have been better because I got annoyed at having to swap windows to look something up.

So, it’s not all doom and gloom, but clearly, Ruby IDEs have a lot of room for improvement. And some features, like refactoring and Intellisense are much more difficult than they would be in a non-dynamic language. That’s one of those little tradeoffs. The smart dedicated Ruby people will probably just shrug their shoulders at those issues and move on about their work, accepting it for what it is, a tradeoff. The really brilliant and really dedicated Ruby people will find a way around 90% or 95% of the problem, although that will take time.

The idealists will probably respond that those features aren’t necessary, or are actually, despite common wisdom, are evil. Something along the lines of “Code Completion makes you stupid”, or “My Dual Core 3Ghz processor is too slow to run anything more complex than a text editor.” Oops, didn’t mean to start ranting like that… Moving on.

Unit Testing

Unit testing in Ruby is actually very good, despite the lack of any IDE support for it. Being dynamic is the biggest contributor to this. The array and hash primitives are also great for setting up test cases. But mostly it’s the dynamic nature of Ruby. Tests get virtually no benefits from static types.

Except the problem is, I found myself writing tests that would have been covered by a static type system. It’s not that the test cases would have been any easier to write with a static language, but there were plenty of them I wouldn’t have had to write at all. And even then, there were even more tests I didn’t write that would have been provided by default through a static language. And beyond this, in order to implement some tests, I had to put the equivalent of static typing into my code. It just ended up being a lot more verbose then simply declaring the type would have been.

For example, take this initializer:

def initialize(*exprList)
@exprList = exprList
@exprList = *@exprList if @exprList.is_a?(Array)
raise "Values must be interpretable" unless @exprList.all? {|value|
value.respond_to?(:interpret)}
end

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

(I know I probably screwed up a dozen things, so it’s worthless to nitpick my code quality. I’m an admitted Ruby Nuby) That last line is really the crux of the issue here. I expect some will feel it’s simply not necessary, but the truth is, it helped solve more than one bug when I combined it with unit testing. But that line is really just a really verbose form of List<Interpretable>.

So what we have here, is just another tradeoff right? Dynamic languages make unit tests much easier to write, but require more of them. But what if we started to treat static type information as a compact, concise and expressive form of unit tests, rather than as a straitjacket. Maybe if we did this, by making static languages that support dynamic capabilities, we could make unit testing easy, and avoid rolling our own static typing. Just an idea.

Summary

There is more to all of this then simply potshots at Ruby. The real point, is that we all have a lot to learn from each other. Here I’ve shown how the Ruby tool developers are learning from the Java/.NET tool developers, though obviously they have their own unique challenges AND opportunities as well. But likely, all of the hard work put into Ruby Intellisense will have some real value to static language tool designers as well, especially those working with type inference.

I’ve also shown how static language unit test tool developers can learn from Ruby unit testing. I’m going to assume there is probably a way to make the Ruby side of things more palatable as well, though I don’t know what it is yet. When you get criticism, look at it as an opportunity to grow, not an attack, even if it actually is an attack (this wasn’t).

24
Sep
06

Closures for C++?

This one surprised me. I totally saw the Java closure thing coming, but somehow I assumed that the C++ standards group would be too stubborn to even consider such a change. But according to an article, the ISO are considering lambda syntax for C++.

It’s not very well linked in the original article, but I believe the lambda proposal being considered is from Valentin Samko. What’s most surprising is that it appears to me as if they are considering full closure capability.

What’s next? C? or maybe COBOL. Or maybe LISP.. err hold on.

Anyhow, interesting stuff.

19
Sep
06

Java closures continues to chug along

The proposal to add closures to Java continue to move along. Neal Gafter has posted an update on the closure proposal.

04
Sep
06

Connections, Shared Space and Autonomy

Patterns

It’s interesting how simple patterns reappear in the most unexpected places, and how they often the similarity goes unnoticed. For example, take this simple pattern condensed from A Pattern Language.

179. Alcoves**

. . . many large rooms are not complete unless they have smaller rooms and alcoves opening off them. . .

* * *

No homogeneous room, of homogeneous height, can serve a group of people well. To give a group a chance to be together, as a group, a room must also give them the chance to be alone, in one’s and two’s in the same space.

This problem is felt most acutely in the common rooms of a house—the kitchen, the family room, the living room. In fact, it is so critical there, that the house can drive the family apart when it remains unsolved. . . .

In modern life, the main function of a family is emotional; it is a source of security and love. But these qualities will only come into existence if the members of the house are physically able to be together as a family.

This is often difficult. The various members of the family come and go at different times of day; even when they are in the house, each has his own private interests. . . . In many houses, these interests force people to go off to their own rooms, away from the family. This happens for two reasons. First, in a normal family room, one person can easily be disturbed by what the others are doing. . . .Second, the family room does not usually have any space where people can leave things and not have them disturbed. . . .

To solve the problem, there must be some way in which the members of the family can be together, even when they are doing different things.

Therefore:

Make small places at the edge of any common room, usually no more than 6 feet wide and 3 to 6 feet deep and possibly much smaller. These alcoves should be large enough for two people to sit, chat, or play and sometimes large enough to contain a desk or table.

Give the alcove a ceiling, which is markedly lower than the ceiling height in the main room. . . . (Alexander 1977b)

CLR – Multiple Languages

Reading this for a second time invoked a number of thoughts. The first was to relate this pattern to virtual machines, like the .NET CLR. In this first example, I was thinking of how they serve as a nexus for different programming languages. The CLR (or JVM) itself is much like the room at the center, and the languages the alcoves. Much like height of ceilings, no one language can meet the needs of all developers or development tasks. Different developers require languages of different complexity. More importantly, different development tasks require languages of different complexity (and possibly developers of different complexity too).

But developers are best as a community to avoid solving the same problems more times than necessary. Without a central (intermediary) language, it’s not possible to share libraries seamlessly, and communication is more difficult. The CLR serves as a common center. IL establishes sets a level of maximum complexity, and each language retains some autonomy while enjoying the benefits of the CLR.

It’s worth noting that the CLR must be more complex than all other languages by nature of having to support the primitives of those languages. In principle, languages using the CLR could be more complex, but they would have to dumb down their behaviors and fake that complexity within IL. But by doing so, that complexity would become inaccessible to other language consumers. Ideally, you want the IL to support the same or greater level of complexity.

I use the CLR as the example here, because Microsoft was first to popularize the concept, but the JVM is just as capable of this. There are some minor differences, but I won’t waste time (today) trying to discover which is better. Instead, I’d prefer to move on to a similar example that the JVM popularized.

JVM – WORA

One goal of the JVM was to unite multiple operating systems. The concept was Write Once Run Anywhere (WORA). You can see the similarity, where the JVM acts as a center. Operating systems would share applications, not just libraries, and communication would be less difficult. However, like language interoperability you’d want the central room to have the highest ceiling, because otherwise you would be limited by the JVM.

But whereas writing a high level, nonuser language like Java bytecode or IL relaxed many constraints of design, due to not having to worry much about the user, multiple operating system support did not have the same advantage.

For simple actions that were already common to operating systems, the process worked well. For more complicated areas, like user interfaces, where different operating systems had significantly different implementations, it did not. Java has tried very hard to address this, but while Java 6 is set to address many outstanding UI issues, Microsoft is set to release a new UI framework. Beyond this, what about the unique UI features of the OS X, both today and tomorrow? In order to be the center for all operating systems, Java would need to be an operating system, bigger and more complex than all others.

Of course, a more practical approach is to rely upon WORA only in application domains that rely mostly upon simple common features. In these areas, the JVM serves portability needs very well.

It’s interesting to note that such portability is as possible with the CLR. Mono for example runs most .NET applications that don’t make use of user interface libraries tightly coupled to the Windows operating system. But cross platform user interface libraries are just as possible under the CLR as under the JVM. GTK# is an excellent example of one. But of course, the same difficulties with taking advantage of OS specific features arise, but if portability is more important than the latest features then this is an option.

It’s also interesting to note that other operating systems can have direct support of their own, like with Cocoa#. The Java community has generally discouraged this type of development, but it is just as possible under the JVM.

Integrated Development Environments – Language Workbenches

A few weeks ago, I was writing about the importance of metadata. Serendipitously, a reader of another post linked me into an essay of Martin Fowler’s that tuned into what I’ve been thinking about for a while, but haven’t quite so eloquently written. To sum it up, Martin talks about “language workbenches”. He defines a workbench as:

  • Users can freely define new languages, which are fully integrated with each other.
  • The primary source of information is a persistent abstract representation.
  • Language designers define a DSL in three main parts: schema, editor(s), and generator(s).
  • Language users manipulate a DSL through a projectional editor.
  • A language workbench can persist incomplete or contradictory information in its abstract representation.

Since that might not make too much sense out of context, I’ll sum it up. The idea is the workbench maintains a central abstract representation of code. Editors, instead of being simple text editors interact with this representation. The overall user experience could be similar or the same, but the underlying mechanics totally different.

This is however, another example of the alcove pattern, where the workbench is defining a high ceiling abstract representation, but you can use multiple editors and sublanguages to satisfy the needs of individual domains. This takes the inter project cross language compatibility established by the JVM/CLR and extends it to intra project activities.

There’s more to this topic however, which I will cover in another post.

Other examples

There are plenty of other examples of this pattern. Operating Systems themselves are a good example. Applications need their own process space, but they also need a standard system for communication with hardware, other software, and the user.

Pieces of networks are good examples as well. The network itself isn’t a very good example, but anywhere that users have both a private and shared space, such as sites like digg and reddit are good examples.

This is an important concept to remember in designing network applications because it’s important not only that you give users a place to communicate, but also that you allow them enough isolation that they don’t wander off to their own rooms, or feel constrained within the one room.

19
Aug
06

Java will support closures in JDK 7

It’s nice to be right. Not to toot my own horn too much, but today Sun announced a proposal for closures in Java, in the Dolphin (JDK 7) release. I uncovered the hints of this in the JSE6 documentation about a week ago.

I suppose that isn’t a really stunning scoop. The writing was on the wall anyhow. Almost every other modern language either always had support for functional programming, or already had plans to support it. Therefore it could only be a matter of time before smart people like Gilad convinced the pointy haired bosses to listen to reason. I have to agree with him when he say’s “what took so long”.

I haven’t had a chance to review the whole spec yet, but if you’d prefer it in non-HTML format, Neal Gafter, one of the other co-authors has a copy (Updates: 0.1, 0.2).

Anyhow, this is great news, even if you use other languages like Ruby. Making functional programming a standard language feature, as ubiquitous (or more so) as the class, can only make it easier for all developers to communicate.

The only downer is that JDK 7 is a long way off still, so there will be a bit of a wait.

18
Aug
06

A LISPie day. Data, Code, Context and Hints

I came to two conclusions today, about LISP. One the one hand I crystallized some thoughts on some of the flaws in LISP. On the other hand, I finally got the point one of rallying cries of the LISP community, namely “In Lisp, your code is data, and your data is code”.

Ever since I first heard that phrase, and had a basic knowledge of LISP, I understood the basic idea behind it, but until today, I didn’t have the right context. As a result, I think I focused more on the first half, which equates to the concept of functional programming, or at least first class functions. The second part seemed far less interesting, and somewhat of a tautology. But reading yet another post from Steve Yegge, showed me a context that made the statement far more interesting. The section that really did it for me was Beyond Logs.

What is curious is that earlier in the day I had a discussion that really solidified some vague thoughts I had about why I, and probably many others, have been so turned off by LISP, despite its capability. You often hear LISP lovers comment, that LISP has no (or very little) syntax. And it’s true. But that isn’t always a good thing. In fact, I think it’s a fundamental flaw for the majority of what programmers traditionally consider code. It’s nice to be able to think of code as data, and data as code, but it’s nice to be able to tell the difference too, or more specifically to be able to tell what is “more” code, or “more” data.

A truth, which goes rather unnoticed by too many language, tool, framework and library designers, is that we spend much more time reading code than writing it. Reading may seem like a very simple and straightforward activity, because it comes so naturally after years of practice, especially when compared to writing English prose. But what makes writing English so difficult is that you have to structure it so that someone else can understand it later.

There are many tricks to English, and it’s not just stupid syntax. The complex syntax rules of English may seem to make writing more difficult, but many of them, when followed contribute to readability. Without those rules, writing would actually be more difficult because not only would you need to devise a similar structure, but you’d have to somehow communicate that to readers. Having a set of rules, no matter how arcane is preferable to no rules.

And that is where LISPs lack of strong syntax becomes a problem. Sure good LISP coders will create a well thought out sublanguage, and avoid using cryptic shortcuts and other reading impediments. With high level elements this works well, because it is the sublanguage which eventually describes the solution. But at other levels, this can be a serious impediment because it is hard to separate out the layers. The syntax of most other languages gives the readers more “hints”. Some of these hints may seem unnecessary when writing code (brackets, parentheses), but they help you spot different sections of code. In the C derived families, when you see parentheses, you’ll think function call. When you see braces, you think loop/conditional. Brackets make you think “array”, etc.

Those same structures exist in LISP, but they all look nearly identical. That’s great for writing code, you don’t have to think about which character to use (has any LISP user remapped the keyboard so they don’t need to use shift for a parenthesis?). For reading, however, the homogeneity is very poor. It would almost be like removing tense from all verbs. Usually you can decipher tense from context, but it’s almost always difficult if done wrong. Programming languages are more deterministic than natural languages so it’s always possible to decipher LISP code from context, but it’s not always going to be easy.

The most interesting part of all this is how it ties into the data is code concept. If you’re putting code into your data, you really should have a way to make it stand out. The complexity of code, is higher than data and deserves special attention, when inside a mixed environment. It’s best to separate data from core code, so that it can change independently. I see the value of one unified model for both code and data, and the ability to work in the blurry areas in between. But as a reader, I’d prefer there to be some syntactical differentiation regardless of what’s under the covers.

It’s possible a super-intelligent IDE might be able to fix these problems, but I haven’t put enough thought into it to know if that’s truly feasible with the existing LISP syntax, and from my current review of the state of the LISP IDEs, there is a very long way to go before that type of capability starts to show up.

13
Aug
06

Functional Programming in Java, C#, VB.NET? Soon.

I’ve been doing a lot of reading lately, and lot of it has been about functional programming. There are many well known writers out there with quite a bit to say on the topic. For example, Steve Yegge and Paul Graham talk about the virtues of functional languages a lot. What I don’t understand is why neither has even once mentioned C# 3.0. They and many others talk a lot about Ruby, and how important functional programming is there, and they are right. But I can’t understand how someone who values functional programming so much can ignore the functional features in C# 3.0.

Sure, they might like Ruby more, but when Microsoft releases C# 3.0, functional programming will finally be mainstream in a way that Ruby would take years to reach. And for an advocate of functional programming, that should be an event of astounding importance. But not one single mention of it is made on either site, or many others that advocate LISP, ML, Hakell, OCaml, etc. Will C# 3.0 do everything these languages do? No, but I think it hits the most important points, at least as in the area of functional programming.

Why do these writers completely fail to mention such a momentous turn of events for one of their most dear principles? I can only conclude it is a hatred of Microsoft, which causes them to think irrationally when it comes to anything from Microsoft. If it was just a few writers, this wouldn’t trouble me so much, but it’s not just a few writers. I see so many people who will simply pretend as all Microsoft’s products with less than 90% market share are insignificant.

I expect the reaction will be different when Sun announces that they intend to support functional programming in Java as well. I’m not sure when it will happen but I am sure they are thinking about it. If you look at the specs for the pluggable annotation API closely, it talks “new language features”, and visitor classes. This can only mean one thing, lambda functions with closures. Of course, unless Sun is being very tight lipped about this support, Microsoft will beat them to the punch. It’s unlikely that Sun is hiding anything because the JCP process does not really allow for that.

It’s nice to see that Sun has woken up, and isn’t ignoring what .NET is doing, as they once did. J2SE5 was a litany of copycat features, for which I applaud Sun, rather than deride them. And of course, functional programming in general is a copycat of what LISP did long ago. But that’s ok, because with programming languages it’s not all about which language was first. We programmers are more practical than that.. Aren’t we?

12
Aug
06

IDEs, Dynamic Languages and the Importance of Metadata

I realized something curious today. As programmers, we use the same tools for two very different tasks, reading code and writing code. Our IDE’s (or text editors), display only one view of a program’s source code, and it is universal. But the requirements for reading code and writing code are very different.

When writing code, programmers universally appreciate succinctness. It’s often over emphasized because for most programmers writing code is the fun part, so making it as fun as possible is top priority. But we know eventually, we will expend a great deal more effort and time reading that code. Succinctness has value for readability, but it has limits, which don’t necessarily apply to writing code. It may be easier to type “i”, then “index”, but is it more readable?

So our parameters for reading code are different. When reading code, we should have more information available then we are required to write. Intellisense is a partial example of this vision. Not only does it reduce what you need to type, but insures later when reading, you will have more information available.

Intellisense is not enough however, we should have IDE’s that show us, or allow us to see, implied characteristics that compilers can compute. For example, I really appreciate OCaml’s ability to infer types, signatures and such. When using a prompt, you get to see results of your declarations. But once you go into an IDE, a lot of that information is missing, which is silly because that information is even more important when reading in an editor than writing at a prompt.

I’d like to see that information, and more displayed in my edit/read window without having to hover, click, etc. I’d be ok with a switch to go from read to edit mode if that’s what the interface requires, but I think it’s critical that we stop thinking only about the writing, and begin putting more thought into reading.

One of the things you’ll hear the most from Ruby fans is they like how readable the language is. In many ways, I’d certainly agree, there is a lot other languages can learn from Ruby about readability. But there are some chinks in the armor. I’ve been picking up Ruby lately, and I’ve been frustrated by the lack of support for things I take for granted in Java or C#, such as Intellisense/AutoComplete.

Because it is a dynamic language, which doesn’t really know the type of variables until runtime, tools often don’t know what the author intended when he wrote a set of code. In the long run, this will be a limiting factor for most dynamic languages. One could design a dynamic language with a full type inference system, but that is quite difficult and requires compromises to the dynamic language ethos, so they aren’t. Without this information, IDE tools will be less functional.

But, dynamic languages are all about the language. Everything evolves from the language, and it’s been this way ever since LISP. But today, to make a great development environment you need to look at more than just the language.

First, you should consider your API as well. This was one of Java’s early strengths. It had an excellent well-crafted API designed alongside the language. This was contrary to the C++ development model, which created multitudes of APIs separated from the language, without any strong common base.

As well, you should consider the IDE. This is a recurring strength of C#. The design of C# took into account Visual Studio and the .NET framework and vice versa. C# lends itself to preparsing extremely well, and as a result, features like Intellisense, Refactoring and Class Browsing are highly supported. .NETs assemblies and metadata likewise help support these features extremely well.

I recently read Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, and the most interesting part of the book were the annotated comments. Besides being open about mistakes made in the design of the .NET Framework API, it also makes clear the amount of thought that went into optimizing the API for use with the IDE. By considering features like Intellisense in the design, Microsoft could design the framework to make it easier for developers to find information fast.

It’s amazing how easily and how commonly developers dismiss the IDE from the equation, even when talking about usability. Take this article, “Are programmers people?” as an example. But the IDE is becoming more important with every passing year. Intellisense, Refactoring and Unit Testing are just the tip of the iceberg. Smalltalk was ahead of its time in some of these areas, and coincidentally was the last widely known language with an integrated IDE before .NET.

But automation of these capabilities requires information, and this is the Achilles heel of dynamic languages. By their very nature, they have less information available when the IDE needs it because they don’t intend to figure these things out until runtime. Static types are metadata, and that metadata is incredibly valuable to pre-runtime tools.

I respect dynamic languages. Dynamic languages have done some great things, but most of them are doable in static languages as well. The recent surge in interest in dynamic languages, especially Ruby, has demonstrated the value of features neglected by mainstream language developers for far too long.

Even after this, dynamic languages will have their place and many unique advantages, but I disagree strongly with anyone who thinks static languages are a dying breed. Metadata will become more important in so many ways. What we will be searching for in the future are ways to give our tools more information, with less work. Settling for just providing less information not only cheats tool developers, but also cheats ourselves later on when we want to read our code.

I imagine a day when give us succinct methods for indicating intent, validate that information, and suggest safety while still allowing freedom. In addition to helping write safer code, or allowing secure systems to validate it, I also imagine this data helping us to maintain our code by providing more information and insight to the maintainer than the original author explicitly typed out.

06
Aug
06

Collection of Dynamic Features for Static Languages

Dynamic languages have been gaining support and interest over the last few years. Static languages however still dominate usage, and have benefits, like performance and verifiability, that some dynamic languages would like to replicate. At the same time, some static languages are realizing the value of dynamic languages and are attempting to replicate some of those benefits, while retaining the static capabilities.

I have attempted to create a list of the dynamic features that static languages are picking up on, and how they are addressing them. I am sure this list is not yet complete as most there are a great deal of interesting languages out there these days, and I have not had a chance to review them all. If you know of a few more features or implementations, let me know.

If your interested in static/dynamic crossover, you might be interested in three earlier posts. Inform, Suggest Safety and Allow Freedom, Static/Dynamic? Why not both? and Principles of Language Design – Semantic Capture

Safe Features

Safe features do not require late binding and retain all static type checking ability, while shortening syntactical definition. Sometimes this results in a clearer syntax as well. Safe features stretch from syntactic translations, like type inference, to functional extensions like lambda expressions.

Type Inference

Found in: C# 3.0, Visual Basic 9, F#, Fortress, ML, OCaml, Haskell, Boo, Nemerle, Groovy?, Clean

Type inference is the simulation of dynamic syntax while maintaining static typing. A variables type is not explicit, the compiler infers it from the context. The depth of inference can vary, but usually takes into account at least constructors for local variables.

Note: I could not really tell whether the def statement in Groovy was a dynamic variable or a type inference mechanism.

Structural Subtyping

Found in: Haskell, ML, OCaml

Also know as: ad hoc interfaces

Structural subtyping can be thought of type inference, where the name of the type is not taken into account, but only the structure of the type. The ultimate result is very similar to duck typing, while remaining completely statically typed.


Anonymous types

Found in: C# 3.0, Visual Basic 9, haXe, OCaml

Also know as: immediate objects

Anonymous types allow using dynamic syntax for temporary types or data structures. Under the covers, these are just another type with an implicit name.

Tuples

Found in: OCaml, Nemerle

Tuples are very similar to anonymous types, but tuple members do not have names. Technically I believe the term tuple could be used to refer to anonymous types as well, but in practice where the term tuple was used it was with unnamed members.

Lambda Expressions

Found in: C# 3.0, Nemerle, ML, OCaml, Groovy, Boo, Nice

Generics

Found in: C# 2.0 Visual Basic.NET, Java 5, Managed C++, Nemerle, ML, OCaml, etc.

It is somewhat debatable if generics are a dynamic feature. They fulfill a very specific piece of the self-generating program capability available from many dynamic languages, but do it in a very different way.

Unsafe Features

Unsafe features shift evaluation to runtime through techniques such as reflection, lazy evaluation, or runtime interpretation.

Runtime Casts

Found in: C#, Visual Basic, Java, C++, Nemerle, Boo, etc.

Not commonly considered a dynamic feature, casts that evaluate type coercion to at runtime are a dynamic feature. In statically typed languages, runtime casts usually only result from explicit casting, yet the two terms are not synonymous.

Dynamic variables (a.k.a. Duck Typing)

Found in: Visual Basic.NET, haXe, Nemerle, Boo

A dynamic variable is quite simply a variable that not only can hold an object of any type, but allows any member name or signature to be valid at compile time. Some languages like Visual Basic enable/disable this globally or per file. Other languages have a specific dynamic type.

Dynamic interfaces

Found in: Visual Basic 9, Nice

A dynamic interface allows for after the fact interface definition. A dynamic interface is similar to a dynamic variable, but defines some restrictions. Like a dynamic variable, a dynamic interface at compile time accepts assignment from variables of any type.

Dynamic blocks

Found in: haXe

A dynamic block allows evaluation of a series of statements to either partially or fully at runtime.

Known implementation techniques

Generate reflection code. Miniature interpreter.

Dynamic identifiers

Found in: Visual Basic 9

A dynamic identifier allows evaluation of individual identifiers at runtime. In many ways, dynamic identifiers are reflection short hand, but their syntax is similar to that of dynamic languages.




Pages

Top Clicks

  • None

a


Follow

Get every new post delivered to your Inbox.