Friday, November 17, 2006

Eric Lippert, C# 3.0 Type Inference, Charlie Calvert & Turbo Pascal

Some great details in a video linked from Eric Lippert's blog.

There's a lot of detail on the new algorithm for type inference in C#. It all looks increasingly hard to parse without build a whole tree without typing for the expression containing the LINQ query, and because lambdas can contain statements, that implies a whole tree for statements too. In other words, a compiler implementing this functionality wouldn't always be able to annotate expression types as it parses - it'll need another descent / transform through the parse tree.

Also interesting, for me at least, is the Turbo Pascal box in the background on Charlie's shelf, about 25 minutes in - given that I'm now helping maintain the Delphi compiler, the descendant of that product.

Thursday, September 21, 2006

Considering CLR & JVM as Manycore Paradigms

Larry O'Brien asks " Are CLR and JVM suited to manycore?" My first instinct is to say no, because they're built on sequential execution and mutable data structures. That said, RPN-stack-based code like IL and JVM opcodes (modulo control flow instructions) is basically an expression graph serialization format, so there's a shadow of hope in there, also considering Expression<T> and friends in LINQ. However, I definitely don't think either of them are in the shape of the end of the road, a suitable UNCOL.

I'm sure you're familiar with Wesner Moise's blog, where he's made some entries relating to the idea of moving away from structures requiring mutability, and towards more functional structures. One recent post that's still in my RSS reader is http://wesnerm.blogs.com/net_undocumented/2006/09/modelview.html, where he hints at some interesting ways of using immutable structures in document / view architectures.

For better or worse, I don't fully share Larry's pessimism (privately related) about functional / dataflow and other "different" ideas. I think we'll see more stratification in the software industry, as people who need the extra performance of parallelism seek out new local development optima, while people who only need current-day levels of performance for tasks that are relatively trivially parallelized (such as today's request/response server code) use other, more procedural languages, like Ruby, C#, Delphi, etc.

IMO, one of the worst things historically to have ever happened in programming was the fashion for C as the "real" programming language. With its primitive strings and its unsafe standard library (i.e. including functions which are literally unsafe no matter which way you call them, consider gets() for example), I think it's wrought unnecessary and disproportionate harm. I hope that any coming stratification doesn't fall into similar social trip-ups that blight us for another few decades.

Thursday, September 07, 2006

Growth of the Internet through the Lens of the Social Sector

Facinating post by Stirling Newberry at TPM: "... let me boil down one of the simplest and most important ideas of the next half century into a short space."

Tuesday, August 29, 2006

Interfaces in Delphi / Win32

Craig Stuntz comments on my remarks about interfaces in Delphi / Win32:
Regarding interfaces, I'd argue that Delphi Win32 interfaces are more similar to .NET interfaces than COM interfaces are to COM+ 2.0 (a.k.a. .NET) interfaces insofar as they don't completely dictate lifetime management. In Delphi Win32 I can (and frequently do) override reference counting and use interfaces to provide multiple interface inheritance of classes without reference-counted lifetime management of instances.
Sure. Delphi/Win32's interfaces are a superset of COM interfaces; they include support for native Delphi types (such as AnsiString), and when one is working in single-language / single-vendor (taking BCB into account) environment, it can be useful to discard the language-neutral COM conventions. That doesn't stop the Delphi compiler from performing all the refcounting operations anyway, though.
I can't cast from an interface to an object, but in other respects it's pretty similar to what I do with interfaces in .NET. The underlying mechanisms are very different, yes (e.g., GUIDs as IIDs), but in the end I find myself doing similar things in code.

When you say interfaces are different in Delphi Win32 and .NET you seem to be talking about lifetime management.

I'm talking about slightly more than that. I'm talking about the basic services that IUnknown provides: lifetime management (AddRef and Release) and interface negotiation (QueryInterface). All Delphi/Win32 interfaces derive from an interface that supports these three operations (whether it's IInterface or IUnknown depending on version). Delphi/Win32 implements interface casting (in so far as it does at all, excluding Supports() etc.) through calls to QueryInterface, via 'as'. Delphi/Win32 doesn't support 'as' casting unless the interface has a GUID, a COM-ism. Delphi/Win32 classes can dynamically change which interfaces they support (within or without COM rules) by explicitly implementing QueryInterface() - that won't work in .NET (I use IServiceProvider for this). All COM interfaces consumed in Delphi in the orthodox way (ignoring the old Delphi 2 stuff) are also Delphi/Win32 interfaces. Delphi/Win32 interfaces and COM interfaces are basically made of the same "stuff", with the same binary format.

.NET interfaces are entirely different; they're a runtime feature, and they aren't a binary standard. They're not ref-counted. They don't have the same layout. Casting is defined by the implementing class, and can't be dynamically changed (RealProxy etc. aside). They don't require GUIDs for full support. COM interfaces get wrapped in runtime callable wrapper (RCW), rather than being actual .NET interfaces.

But the lifetime management features in Delphi Win32 are something I generally try to work around because (1) I mostly don't use them for COM and (2) I find having two wildly different lifetime management schemes coexisting inconvenient. When I work in .NET I use the GC; I would find doing a mixture of GC and explicit allocation / disposal messy. Similarly, I only want my object instances reference counted in Win32 when I can't avoid it, like when I must use COM. Otherwise I want to do things explicitly, for the sake of consistency.
Sure - but you have to do this yourself! Delphi's TInterfacedObject, the normal base for a class supporting interfaces, implements COM rules. I think it can even be useful to work within the COM rules too, when restricted to Win32-only code, since the automated ref-counted GC supports shared ownership fairly well.

Delphi Native Closures, Part II

A few days back, I made a post on the difficulties of implementing closures in a non-GC language.

Craig Stuntz made a comment:

Barry, I don't see how this problem is any different than in operator overloading, which is already supported in Win32, albeit in very limited conditions. So there's a fourth possibility not mentioned in your list, which is to only support it for functions with argument data types such as strings and value types which can be simply cleaned up. It's far from ideal, but given the OO example not unprecedented.
I agree with Craig re possibilities, there are more ways to slice it - but some slices are bigger than others! In my previous post, I cut things into two basic slices: how to free the delegate, and which captured variables to free. Of the two, the first one is about 90% (or more, FinalizeRecord in the Delphi RTL can do the second job) of the problem.

I don't agree with the comparison to operator overloading. Implementing closures is far more complex than operator overloading, because closures aren't limited to traditional Standard Pascal downward funargs; they can live long after the creating frame goes away. That isn't the case for temporaries created through support for operator overloading. The bulk of the work would be in putting the stack frame on the heap and refcounting it, and in implementing reference counting on every Delphi Win32 method pointer. I could then see people (ab)using COM interfaces to get deterministic destruction, effectively the same as the notional 'finalization' section I described.

Lest one think that one could get away without refcounting by limiting closure support to downward funargs, well, that would be a problem: closures gain a lot of power through composability. By composability in this context, I mean the ability to write a function that takes a function and returns a function, where the returned function is a function of the argument function, and thus relies on closure or some other way of tracking the passed in argument.

I should make that concrete. Consider this predicate, in C#:

public static Predicate<T> Not<T>(Predicate<T> pred)
{
    return delegate(T value) { return !pred(value); };
}
This snippet can't be implemented with just downward funargs because the stack frame of Not disappears when Not returns.

This is the heart of the problem: not only a transformation of stack into heap (of the lexically closed & captured variables etc.), but implementing refcounting everywhere a method pointer exists. Whether or not simple types like string and record and interface (and thus 'simple types' is open ended) are supported or not, is relatively trivial.

Another thing this brings up: the (ab)use of COM interfaces to implement simplify lifetime management of plain old Delphi objects, something which effectively would add the power of a 'finalization' section, without being explicit about it. It gets at another issue in my earlier post: the divergence of .NET and Win32 forms of Delphi. How important is it that the two are the same? Because interfaces don't work in a similar way at all in .NET, they're completely different. They're so different they probably deserve different names.