

Less is more: language features - neiesc
http://blog.ploeh.dk/2015/04/13/less-is-more-language-features/

======
picks_at_nits
The narrative in Why Functional Programming Matters (“WhyFP”) is that it is
not enough to simply remove features, but to take advantage of their removal
to add other, more powerful features.

The paper gives laziness as an example. It’s made possible by idempotency.
Which is made possible by the removal (or careful management) of side-effects.
Which then allows certain otherwise impossible ways to express infinite data
structures.

[http://www.cse.chalmers.se/~rjmh/Papers/whyfp.html](http://www.cse.chalmers.se/~rjmh/Papers/whyfp.html)

~~~
SatvikBeri
Which in turn allows more modular code. I translated an example from Why FP
using Python here: [http://satvikberi.com/2014/10/13/functional-programming-
usin...](http://satvikberi.com/2014/10/13/functional-programming-using-lazy-
evaluation-to-write-more-modular-code/)

------
kmicklas
I take issue with the single-number-type thing. This guy is conflating two
different reasons/ways to divide up number types - for transparency w.r.t.
machine (optimization) and by conceptual types (integer vs natural number vs
fractional, etc.). The first arguably doesn't belong in a high level language.
The second arguably is _exactly_ what a truly high level language should have.

~~~
sink
I agree. Just think of how often what you really want to express in your
program is a function that takes a Positive Integer, and any other type of
number is simply wrong.

~~~
dagw
I've often thought you could add that to your type system somehow? Sometimes
you want to express that this variable can only take numeric values between
0-1 or this variable can only take string values with exactly three uppercase
ASCII letters. Just like you don't need a special string type to handle three
letter strings, you don't need a special number type to handle positive
integers, you just need a way to represent subsets in your type systems.

On the other hand I'm decidedly old school and get very uncomfortable around
languages that at least don't separate floats from ints.

~~~
ygra
Pascal had subrange types, which were quite nice in that regard. Although I
don't remember using them _that_ much.

------
efaref
Yet another article that misses the point of "goto considered harmful."

The way "goto" is used in modern C (named breaks and defined function escape
routes) is perfectly acceptable. Newer languages include these features in the
language, in much the same way that C encoded structured "goto"s as "while"
loops and "if" statements.

Dijkstra was complaining about unstructured programming compared to structured
programming.

~~~
SideburnsOfDoom
Yep. Things like while loops, break statements and if-else statements
containing blocks are basically _structured constructs over goto_. Goto is the
basic building block, it is not "entirely redundant", it is entirely
foundational. It's just too low-level for everyday usage.

------
mden
To me this is a weird list. It's a bit like removing the personality of a
person and then claiming that made the person better.

Also a lot of the things he lists are not at all as clearly harmful as he
makes them out to be.

GOTO: Used in some of the cleanest code bases. For example the lua programming
language, redis db, and linux kernel OS. Yes the use is limited, but it's
there not because of mistakes but rather because it is sometimes the most
clean way.

Numbers: I don't understand this one. In the very least having different types
of numbers seems to be a necessity for anything performance oriented such as
graphics. JS not having them (they seem to have been added for WebGL as an
extension, no?) is not a good reason for all new languages to do the same.

Mutations: I am personally of the view that mutation often simplifies the
informal reasoning complexity of a program. There's a reason functional
programming hasn't caught on and it likely never will. A hybrid approach of
having both immutability and mutability like Rust seems most sensible.

Reference equality (Referential transparency?): If you have immutability do
you not get this for free? How can you be sure about getting rid of mutations
but not be sure you want objects to be equal based on data?

Each language has it's own quirks that make it special and that's part of the
reason programming is fun. There's never a single right way.

~~~
sink
GOTO, mutations, and side-effects break referential equality and equational
reasoning, making it much harder to prove things about your program. I guess
you can do that by endless permutations of tests or a stochastic testing
system, but that sounds like a horrible way to spend your time.

~~~
zak_mc_kracken
A program that's harder to prove correct is not necessarily incorrect.

I'd actually argue that most of the software that runs our lives today is
impossible to prove correct and yet seems to be doing quite alright overall.

You're setting up a similar false dichotomy as people who say that programs
that ship without automated tests are worthless and broken.

~~~
sink
Nah, I am saying two things:

1\. Mutability makes your program harder to reason about.

2\. There are better ways to spend your time than writing tests for conditions
that could not exist with pure functions.

You could totally extrapolate a ton of statements from those two statements
though. But I'm not doing that now, and not here.

~~~
mpweiher
> 1\. Mutability makes your program harder to reason about.

How so?

~~~
sink
I give an example in a sibling (cousin?) comment here:
[https://news.ycombinator.com/item?id=9372280](https://news.ycombinator.com/item?id=9372280)

------
ruricolist
I dispute the basic narrative here. If you have tail calls, you have GOTO. If
you have closures, you have pointers. The story (so far) is not one of
features _disappearing_ , but of features being subsumed by other more
general, abstract features.

~~~
derefr
No you don't. A GOTO allows you to GOTO a location that isn't valid code—for
example, the middle of a multibyte instruction. A tail call does not. That's
what the article meant by "disallowing invalid code": a GOTO is strictly
equivalent to a tail call, except for all the extra _invalid_ things a GOTO
also allows you to do.

~~~
ufo
Not really. Machine language jumps can do that sort of nasty stuff but usually
gotos are restricted to more well-behaved labels.

That said, gotos are still much less structured than tail calls. You can make
some much more spaghetti-like control flow patterns and you are also
encouraged to use shared mutable variables instead of passing parameters by
value.

------
hacknat
It's interesting to see that recent languages are making strides towards this
list.

* Golang does away with inheritance and exceptions.

* Rust does away with mutability, or at least makes it explicit.

I wonder if a convenient language with all of these taken out could exist. I'm
skeptical about a single number-type.

Also, wouldn't reference equality simply be gone if you take away pointers?

Also, his point about interfaces is pretty narrow, not every interface's
manipulation can be characterized by one or one set of manipulations. I think
Golang is a fantastic example of a language that forces a role-based interface
pattern.

~~~
aikah
> * Golang does away with inheritance and exceptions.

Meh,regarding exceptions you still have errors that you bubble up by returning
them. It's hardly different from exceptions.

On the other hand, Go went way too minimal with its type system so much that,
you can opt out with inteface{} because it's not expressive enough. I would
prefer no interface {} , no reflection ,but to allow types as parameters and
value.Which they are not in go.

finally, go has GOTO ... I would have gotten rid of that at first place.

~~~
rakoo
> you still have errors that you bubble up by returning them. It's hardly
> different from exceptions.

It's _completely_ different: you have to _manually_ bubble them if you want
them to bubble. You can perfectly not deal with them.

Yes, the initial reaction to seeing an err pop up is to return it. No, you
don't _have_ to return it.

------
thomasfoster96
I think it's worrying if language design ever becomes about having higher and
higher level features 'protecting' the programmer from making mistakes, at the
cost of losing access to lower level features that are considered harmful.
Higher level features are much better than a GOTO 99% of the time, but there's
always going to be 1% of the time when a GOTO might be better.

~~~
IndianAstronaut
Protecting the programmer is a good sell to corporations though. Less chance
of error prone production code, easier for people to develop with, etc.

~~~
alexchamberlain
The same corporations that use the Linux kernel, various database engines and
various language runtimes, all of which contain C or C++, many platform
specific byte manipulations and various other unsafe features?

~~~
tormeh
Have you used Linux lately? It's always close to falling apart, in my
experience. That the developers are doing a heroic job of keeping all the
unsafe parts under control does not mean this is a good idea for the rest of
us.

~~~
alexchamberlain
I use it daily, both personally, for development and in production. Performs
better than commercial _nix and Windows IMHO; cannot comment on_ BSDs or Mac.

------
StefanKarpinski
This article is nearly its own reductio ad absurdum. By this line of
reasoning, we would all be better off programming just with NAND gates – the
most minimal programming model of all.

~~~
byuu
It seemed really unfair the way the "valid programs" was slightly out of
bounds on C (meaning there are some things you can do in assembler, but not C,
to which I agree); but then as he starts removing more and more features, the
"valid programs" circle is wholly inside his imaginary language construct; and
no matter how much is taken away, we're to believe all possible valid programs
can still be represented? Nonsense.

------
sacado2
Nice post, although I think the author is cheating a little with his Venn
diagrams. Most of the time, his language subset contains almost all the valid
programs. I don't think it is true. I'd like to know how one can make hard
real-time embedded programming with the only language features he offers, for
instance, or anything that requires as much processing power as you can
(cryptography, constraint solving, data mining, optimization, simulation,
etc.) I am not talking about exotic niches here.

More generally, I'm still waiting for a massively successful piece of software
made in a language that would involve no mutation, no null pointers, only one
numeric type, etc. Even John Carmack has not yet delivered a revolutionary
game engine programmed in Haskell.

~~~
amelius
> More generally, I'm still waiting for a massively successful piece of
> software made in a language that would involve no mutation, no null
> pointers, only one numeric type, etc. Even John Carmack has not yet
> delivered a revolutionary game engine programmed in Haskell.

One can program with mutations in Haskell, and all the side-effects can be
kept "sandboxed" at the same time; also I believe the side-effectful parts of
a Haskell program can be compiled (in principle) in a way that results in very
efficient code, as fast as e.g. C++, though I'm not sure how well current
compilers are doing in this respect.

So in a way, Haskell (as a language) offers the best of both worlds.

------
Grue3
Unlambda [1] is completely functional and has only 2 built-in functions.
According to the author, it's the perfect programming language, even though
it's extremely inefficient and the code is almost impossible to understand.
Many of the features he lists enhance either efficiency (number types) or
readability. The languages that lack these things are bound to be slow and
hard to understand at least in some cases.

[1]
[https://en.wikipedia.org/wiki/Unlambda](https://en.wikipedia.org/wiki/Unlambda)

------
aikah
> F# projects have fewer and smaller cycles than C# projects.

Sure, but maybe the reason is the people that choose to use F# and Caml like
languages are a bit smarter that the regular coder. Or maybe F# projects have
a different nature and people don't write "enterprise applications" in F#.

------
andrept
Whilst it is true that the absolute number of possible wrong programs grows as
the number of features increases, I don't think that it implies that the
_proportion_ of wrong programs actually coded by people grows as well.

------
kragen
I do not like this article at all. I rebutted it extensively at
[https://lobste.rs/s/ipml3l/less_is_more_language_features/co...](https://lobste.rs/s/ipml3l/less_is_more_language_features/comments/fnomu1#c_fnomu1).

------
kephra
I don't understand the point in removing cyclic references, coming from FORTH,
Lisp, Smalltalk point of view those are required, if you split a big recursion
into smaller factors. The recursion automatically becomes a cyclic reference
between words, functions, methods or even classes, as classes are just
closures.

Does he want to forbid factoring? His feature looks more like a deficit of the
linker to me.

------
digitalzombie
I think Erlang hit everything?

Not sure about Cyclic dependencies and I don't think it have Reflection unless
I'm wrong.

------
ticking
If you install nightly builds on a production system you deserve to get
bitten.

