
Language Design Deal Breakers - modeless
http://sebastiansylvan.wordpress.com/2013/05/25/language-design-deal-breakers/
======
evincarofautumn
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?

~~~
Mikera
Refcounting?? I thought that idea died years ago.

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.

~~~
haberman
> 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."
    

<https://www.kernel.org/doc/Documentation/CodingStyle>

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.

------
wglb
> but really people who think dynamic typing is suitable for large scale
> software development are just nuts

I would argue that ITAs software
([http://www.itasoftware.com/solutions/travelers/flightsearch....](http://www.itasoftware.com/solutions/travelers/flightsearch.html))
is larger than most software efforts, and it is written in LISP. Not
statically typed.

~~~
lowmagnet
Because everything is a list.

~~~
wglb
No, not really.

------
quanticle
I don't get the following requirement:

    
    
        Great Windows support
    

That doesn't have _anything_ to do with the programming language. That's all
about the libraries and toolchains that the language ships with. Sure, when I
evaluate a language, I also take into account the maturity of the libraries
and tools that the language includes. But I don't think lack of library
support (especially for a single platform) should be a criterion in judging
the _design_ of the language.

~~~
hnriot
Yeah, this point is just plain dumb. And although he says windows is an
extremely popular o/s, it might be, in general, but not for software
engineering. A unix like environment is far superior for software dev, unless
you stay in visual studio all day (or eclipse)

The other points are largely flame bait too. A competent developer can manage
just fine in java, python, c++ or a host of other languages that fail his
tests.

A classic case or the bad worker who blames his tools. My response to this
kind of blog post is grow up, deal with the reality of the languages that are
in every day use and stop whining about things like null pointers!

~~~
evincarofautumn
That’s not what the post is about. He’s saying that if you’re designing a
_new_ language, you should meet these criteria because they are deficiencies
in existing languages.

We live with the flaws of old tools because they have other, more important
value to us—a new language has no value without adoption, so it had better
improve on its predecessors in order to convince people to adopt it.

------
munin
I love this post but I don't wholly agree with the null-purity system. I think
that the existence of the 'null pointer exception' is a sign that global,
total, error handling in anything but trivial applications is really hard.

So let's remove null pointer exceptions and lets require that anything that
could return failure be wrapped in an option type. That way you can determine
if there is Some x or None inside of the possibly-None value. Well, then you
need to deal with how to fail in the case of possibly-None.

Even the best functional programmers I've seen fall back to some kind of exit-
or-exception semantic in the None case. So we've kind of re-invented the null
pointer exception, by having cases where the easiest thing to do in our
program in response to some failure is to just abort the program.

I think that this is fine from a memory safety perspective. The terrible thing
about the 'NULL' pointer is that it is a sentinel that is overlaid into the
domain of valid addresses to the machine. When we add a layer that checks if
the address is valid without relying on its literal value, then we gain a lot
of safety in terms of not generating a hardware exception (that is difficult
to reason about) or scribbling on some other object (which could be impossible
to reason about). Terminating the program, to me, is a fine tradeoff to avoid
corrupting and modifying the program in an unknowable way...

~~~
chongli
>Even the best functional programmers I've seen fall back to some kind of
exit-or-exception semantic in the None case. So we've kind of re-invented the
null pointer exception, by having cases where the easiest thing to do in our
program in response to some failure is to just abort the program.

The difference here is that the programmer is making a conscious choice to
violate the option type by writing a partial function which escapes it. It
also makes very plain and clear (via type annotations) which functions are
allowed to fail and which are not (pure functions). With NULL pointers you're
basically making every pointer in the entire program an option type with the
potential to crash if you haven't written the boilerplate to handle it.

Oh and by the way, Haskell's Maybe and Either types are monads which can be
handled very elegantly via do notation, simple combinators or monad
transformers (MaybeT and ErrorT).

~~~
munin
Yeah, I just think that at every point in your program where you will have a
Maybe the failure case could be as inelegant to program execution as raising
an uncaught NullPointerException, and escaping that involves a lot more work
than using a language without a NullPointerException.

Maybe I should write more Haskell code, but this has definitely been my
experience with OCaml...

~~~
gizmo686
I've never used OCaml, but Haskell isn't as bad as you describe. When you use
Maybe, you do not have to deal with the Nothing (ie. Null) case until you want
to escape the monad (even then, you could still ignore it, and just crash if
you do have Nothing).

If an intermediate calculation results in Nothing, then the final value will
be Nothing. Handling this is as simple as:

foo :: Maybe Int -> Int

foo Nothing = 0

foo (Just x) = x

------
6ren
Ocaml? Static types, garbage collected, not sure about windows support, and it
still has null (IIRC).
[http://programmers.stackexchange.com/questions/62685/why-
isn...](http://programmers.stackexchange.com/questions/62685/why-isnt-ocaml-
more-popular)

It's C-fast.

~~~
evincarofautumn
Approximately C-fast, anyway. You still have a GC, tagged integers, and boxed
floats by default. Also not sure about Windows support, as I haven’t been
using it long, and then only on Linux and OS X.

------
angersock
I think the trick is that any programming language that lets me operate on
bytes (and hence makes me care about things like memory addressing) shouldn't
prevent me from having nulls.

A NULL (usually defined as 0) is a valid memory location, especially if you
are doing certain types of systems work--the fact that we've used it as a
sentinel value in C/C++ is a separate problem entirely.

Maybe we need to use a taxonomy where languages that do not express anything
about layout in memory (as bytes, quibits, or whatever) are kept separate from
other languages.

The issue, of course, is that eventually you need to move out from algorithm
space and start dealing with real hardware, which speaks bytes. Then again, a
model where you only interface over JSON blobs might obviate the need for
that, provided you don't talk to anything that speaks in something other than
numbers and human-readable strings.

~~~
gizmo686
I think you are talking about a separate (and identical issue). "NULL" is not
a valid memory location. 0 is a valid memory location. Because we have to
express NULL as the same primitive type as valid memory locations, we have no
way to distinguish between NULL and 0. In a language with a type system that
prevents you from dereferencing NULL, this problem goes away. Now that the
type system can distinguish between NULL and a pointer, we can once again use
0 as a valid pointer.

~~~
JadeNB
> I think you are talking about a separate (and identical issue).

Somehow, this reads very strangely to me. Are you saying something like "I
think you are talking about the same issue from a different point of view"?

------
MaxGabriel
Save for the Windows requirement, I think the author would actually really
like Objective-C. The compiler checks types. Messages to `nil` resolve to
`nil` (0), so no null-pointer exceptions. Automatic Reference Counting gives
you memory safety.

~~~
Strilanc
I don't think objective-C satisfies any of those constraints. It _kinda gets
close_ , but I don't think it does.

The compiler checks types... but there's no generics. Given that the author is
coming from C++, they probably want a type system that can represent 'vector
of ints' vs 'vector of strings'.

Nil doesn't cause null pointer exceptions... but it causes silent bugs, where
you thought something was happening but it wasn't. The real problem (all types
have an extra misbehaved value) is still present.

Automatic reference counting gives you memory safety... but it can't handle
cycles automatically, and obviously it doesn't apply to the C subset.

~~~
protomyth
> but it causes silent bugs, where you thought something was happening but it
> wasn't.

In Objective-C, it could be a silent bug or just intended behavior to send
messages to a nil object. It is explained here:
[http://developer.apple.com/library/ios/#documentation/cocoa/...](http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html)

~~~
Strilanc
Of course, the behavior may be intended. I've certainly taken advantage of nil
doing nothing.

I've also _accidentally_ had a nil where it wasn't expected, and lost time
figuring out why particular effects weren't occurring. More time than I would
have lost if the mistake had caused an immediate exception.

The same type of mistake is still possible (unexpected value), though the
consequences are different, so I don't think it makes sense to say nil solves
the null problem.

------
t1m
I find it fascinating that programmers working in C++ seem to spend a lot of
time thinking about language design.

There is a lot of responsibility on the shoulders of language designers. There
is the notion of technical debt that we all deal with as programmers, but the
meta-debt the language designer accumulates is astounding.

Let's meditate a while. As we read this, some young, well meaning C++
programmer is writing another String class.

I don't even know if talking about Tony Hoare's NULL story is really that
relevant. After all, that's a single feature of a programming language. In the
context of C++, talking about NULLs at the language level is like arguing
about the colour of your Edsel.

------
metaphorm
had to take the whole article with a massive grain of salt. clearly the author
has alot of experience writing Windows desktop applications in C++. Equally
clearly the author has very limited experience with other domains.

------
nahname
I used C# for a long time and consider it to be one of the best examples of a
statically typed language. The generics are very well done. Implicit typing
from 'var' makes half of your statements almost dymanic.

    
    
      var list = new List<int>();
    

Many of the base types are pass by value (int, bool, dates, etc...).

There is terrific functional support through LINQ.

It is a great language. I just don't want to work on windows and it only has
full support there. The library support and community are also pretty bad.

------
protomyth
> Finally, performance.

Wouldn't Strongtalk and the research around it be the counter example to this
claim?

------
michaelochurch
_but really people who think dynamic typing is suitable for large scale
software development are just nuts_

Flame-bait. I happen to like static typing-- and I'd be excited to see more
progress with optional static typing in Clojure-- but I disagree.

First of all, you can pretty easily get principled strong typing with a
language like Clojure. One of the first things I write on a large Clojure
project is defn-typed. If types were a major part of what I was doing (e.g.
theorem proving) that wouldn't be acceptable, but for web programming it's
generally enough. It's pretty easy using a few macros to make it so that you
get almost all of the guarantees (from unit testing) that you'd get from
compile-time type checking.

Remember: dynamic typing doesn't mean you _don't have_ types. You can include
types in your contract system. It means that type sanity isn't checked at
compile-time. For certain problems that require extremely high levels of
correctness, you need static error-checking/analysis because runtime unit
testing isn't good enough. For _every_ large system? No, not the case. What
you need, in general, are good programmers and sane development practices;
those matter a lot more than static vs. dynamic.

My experience is that static typing performs best when you're trying to solve
a known problem extremely well, and that's not an uncommon thing in software.
Dynamic typing is better-suited to a more chaotic world where you don't know
the problem yet, because it makes it easier to "poke" at things in the REPL
and build prototypes.

Also, improperly used, static and dynamic typing both fall down disastrously
on large or distributed systems. For example, if you have circular
dependencies in a large Scala project ("don't do that", you might say; but
we're talking multi-developer now, with the full spectrum of skill) you're
going to see gargantuan compile times because incremental compiles will fail
you, and while Scala's compiler is an extremely powerful machine, it's not
fast.

All this said, Haskell is still a very cool language and I think everyone who
wants to be a serious computer scientist needs to get some exposure to what it
has to offer. _Real World Haskell_ is a great book, for starters. I also think
Scala is great if you have strong developers who won't be tempted to OOP
things up.

~~~
munin
I've found that static types help with prototyping because you can make
changes to types used in your program and the compiler lets you know when you
missed a spot, those locations could live for months in your code in a
dynamically typed project.

static typing systems work with good programmers and sane development
practices as a force multiplier. dynamic typing systems become something that
requires you to have good programmers.

~~~
michaelfeathers
I've found that automated testing helps with prototyping because you can make
changes to tests for your program and the tests let you know when you missed a
spot, those locations could live for months in your code in a project without
tests.

~~~
TheEzEzz
Yes, but this requires the additional steps (and work) of making the changes
to your tests. Tests give a good approximation of what static analysis gives
you, but it is more cumbersome. (Of course tests can also do things static
analysis can't)

~~~
michaelochurch
I usually write a macro (defn-typed) that generates pre- and post-conditions
(contracts, essentially) for crucial APIs when I'm working in Clojure.

It's not as good as static analysis at catching _all_ type errors-- if that's
what you need, I wouldn't recommend any dynamic language-- but it does mean
that most type errors will be caught in unit testing, with minimal recurring
work.

