As a language designer, I’m glad to hear my goals might resonate with somebody. :)
Dynamic typing is easy to implement, and that’s about as far as its value goes, as far as I’m concerned. If your static type system is getting in your way, you need a better static type system, not a dynamic one. The same goes for dynamic scope—much as I like having it available in Perl and Emacs Lisp, it’s best used sparingly.
Memory safety is also kind of a no-brainer. I enjoy working at all levels of abstraction, and having control over memory layout is great, but I don’t miss it when working in a higher-level language. Compilers and runtimes for statically typed languages can usually be trusted on matters of value representation.
Efficient storage reclamation is something I’m particularly concerned with. In the absence of mutability and laziness, refcounting is an excellent GC replacement because you can’t get cycles. :)
I came to that choice from working with Haskell—it’s wonderful, but it suffers from performance issues due to the GC–laziness combination. Both immensely useful features, to be sure, but they come at a cost. OCaml hits a certain sweet spot: it uses GC, but also eager evaluation by default; has convenient access to mutable values; and the runtime uses unboxed primitives.
Windows support is…well, an unfortunate truth. The state of programming on Windows isn’t great outside the Microsoft stack—MinGW and Cygwin notwithstanding, as “using Windows to emulate Unix” isn’t the same as “using Windows”. Language designers have a sad tendency to neglect Windows as a platform but I can’t quite tell why. Can it be just because they work primarily with Linux, Unix, or OS X?
Refcounting is an extremely poor choice for memory management on modern machines. Even putting aside the issue that it can't handle cycles: constantly writing to memory to modify reference counts can be a huge performance hit (you often have to do it even when you are accessing objects in a read-only fashion). Also it requires extra memory on a per-object basis. Also it doesn't play nicely with concurrent threads. Also it's not cache friendly.
I'd expect any halfway-decent modern GC implementation to be significantly more efficient than reference counting.
About the only valid justification I can see nowadays for not using a GC is a hard-realtime latency requirement. Yep, GC pauses suck. But for efficient memory management in nontrivial systems, GCs are definitely the state of the art.
> Refcounting is an extremely poor choice for memory management on modern machines.
Tell that to Linus:
Data structures that have visibility outside the single-threaded
environment they are created and destroyed in should always have
reference counts. In the kernel, garbage collection doesn't exist (and
outside the kernel garbage collection is slow and inefficient), which
means that you absolutely _have_ to reference count all your uses."
Just to balance your downsides of refcounting, some downsides of GC: requires halting the program periodically and trashing all the caches, which is inconvenient and sometimes impossible. And performance-wise it's unpredictable and can require lots of tuning, as Java programmers who work on allocation-heavy workloads can attest.
Reference counting is surely not state of the art, and very definitely un-fancy, but what it has going for it is that it strikes a good balance: reasonably efficient, predictable and bounded memory usage, plays nicely with others (no worrying about scanning other heaps / object pinning / etc). Where reference counting shines is that its memory usage has a lower high-water mark than any GC algorithm.
Garbage collection has a lot going for it too, but any garbage collecting algorithm will suffer from at least one of the problems you mention, in varying degrees. Reads and writes should not require memory management code, like updating a refcount? That rules out a large class of GC concurrent/generational algorithms that depend on read and write barriers. No extra memory on a per-object basis? This rules out many incremental mark-and-sweep algorithms that store the object color. (True, the color may be implemented with a side table, but so also may reference counts.) Play nicely with concurrent threads? This rules out a large class of stop-the-world collectors. Cache friendly? This rules out semispace copying collectors, which may even page in memory from disk during a collection.
I'd say the evidence shows that GC performs best when there's lots of memory available and low interactivity, so it's a good choice for server-type applications. For interactive applications with limited memory, especially mobile apps/games, GC is more problematic, and it's not so clear. I don't know if the GCs in XNA or Android are "halfway-decent", but they both have significant overhead that causes real performance issues.
You’re right in the general case. My language does not have mutation, so refcounting is[1] just an optimisation to avoid copying and allow in-place mutation when the refcount is 1; it only applies to boxed values. I should have said “copy on write”.
And I’m well aware of modern GC implementations, but one of my goals is deterministic memory behaviour, a feature lacking in other functional languages. I swear I know what I’m doing. :)
[1]: Read: will be; the current implementation is interpreted but I’m working with a friend on an x86_64 VM target.
Amazing ... you just restated the article in half as many words, and avoided sounding like a prima donna. This is a useful skill if you actually want people to listen to you.
Dynamic typing is easy to implement, and that’s about as far as its value goes, as far as I’m concerned. If your static type system is getting in your way, you need a better static type system, not a dynamic one. The same goes for dynamic scope—much as I like having it available in Perl and Emacs Lisp, it’s best used sparingly.
Memory safety is also kind of a no-brainer. I enjoy working at all levels of abstraction, and having control over memory layout is great, but I don’t miss it when working in a higher-level language. Compilers and runtimes for statically typed languages can usually be trusted on matters of value representation.
Efficient storage reclamation is something I’m particularly concerned with. In the absence of mutability and laziness, refcounting is an excellent GC replacement because you can’t get cycles. :)
I came to that choice from working with Haskell—it’s wonderful, but it suffers from performance issues due to the GC–laziness combination. Both immensely useful features, to be sure, but they come at a cost. OCaml hits a certain sweet spot: it uses GC, but also eager evaluation by default; has convenient access to mutable values; and the runtime uses unboxed primitives.
Windows support is…well, an unfortunate truth. The state of programming on Windows isn’t great outside the Microsoft stack—MinGW and Cygwin notwithstanding, as “using Windows to emulate Unix” isn’t the same as “using Windows”. Language designers have a sad tendency to neglect Windows as a platform but I can’t quite tell why. Can it be just because they work primarily with Linux, Unix, or OS X?