9 ms·
Less is more: language features
- ruricolist 12y agoI 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.
- timbit42 12y agoAs long as they are not directly available to the programmer.
- jerf 12y agoEverything ultimately manifests as a restriction on the set of assembler instruction series you are permitted to execute. A GOTO that is restricted from leaping outside of its local scope is a limitation. A language with no GOTO is even more limited. A language that only permits tail calls is yet more limited. You don't have GOTO unless you have a full, unrestricted JMP, and if all your language has are tail calls, you do not have a full unrestricted JMP. If you have "pointers" but can't perform arithmetic on them, you don't really have "pointers". All of these things are restrictions, as well as features. The idea that "features == more power" is not accurate... features and power have a complicated relationship, the moreso since there's more than one useful definition of power, but they are almost no matter what neither strictly in opposition, nor strictly working together. But pretty much every useful high level feature is some restriction on what code sequences you can cause to run with the language, together with something built on top of that restriction that would be either harder or less safe without the restriction.
- derefr 12y agoNo 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 12y agoNot 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.
- bandrami 12y agoIf you have closures, you have pointers. And if you have closures you generally have a garbage collector of some sort or another. But anyways, I don't think closures are more "general" than pointers. Closures form a proper subset of memory access vectors (to use a fairly simple definition of "pointer"), so there are things pointers can do that closures can't. But for that matter pointers themselves are an abstraction for memory addresses that the Real Programmers of Yore used before compilers came in and made us all wimps...
- hacknat 12y agoIt'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.
- brandonbloom 12y ago> Also, wouldn't reference equality simply be gone if you take away pointers? You don't need to expose a pointer datatype to offer a reference equality check operation. Java, JavaScript, C#, etc, all do reference equality by default for user-defined classes.
- pcwalton 12y agoRust also lacks goto, "raw" pointers, null pointers, inheritance, general reflection, and cyclic dependencies. It also has very limited exceptions (as panic can only be caught at thread boundaries) and prefers structural equality syntactically to reference equality (although reference equality is possible). Also, Go has exceptions as described here. Panic and recover allow the same semantic power, since every exception-based program can be easily transformed into the equivalent program with Go panic/recover, and vice-versa.
- aikah 12y ago> * 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.
- 12y ago
- thomasfoster96 12y agoI 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.
- derefr 12y agoThere's no need for the two things to be at odds. Look at Rust's unsafe blocks, for example—or C's asm blocks, for that matter. A language can let you have your cake (or safety), and eat it (or skirt it) too.
- IndianAstronaut 12y agoProtecting the programmer is a good sell to corporations though. Less chance of error prone production code, easier for people to develop with, etc.
- alexchamberlain 12y agoThe 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 12y agoHave 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 12y agoI use it daily, both personally, for development and in production. Performs better than commercial nix and Windows IMHO; cannot comment on BSDs or Mac.
- picks_at_nits 12y agoThe 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 12y agoWhich in turn allows more modular code. I translated an example from Why FP using Python here: http://satvikberi.com/2014/10/13/functional-programming-using-lazy-evaluation-to-write-more-modular-code/ http://satvikberi.com/2014/10/13/functional-programming-usin...
- mden 12y agoTo 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 12y agoGOTO, 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.
- mden 12y ago"[They] break referential equality and equational reasoning" Please explain? Mutations can cause side-effects but don't have to. You seem to think that limiting program behavior _necessarily_ alleviates informal reasoning, but that's not the case. Your argument is also highly hyperbolic in terms of testing. A properly designed system with immutability needs to be tested no less than properly designed system with mutability.
- 12y ago
- kmicklas 12y agoI 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 12y agoI 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.
- kephra 12y agoe.g. money is badly represented as float. just my $0.0200000000001 on that topic.
- mmphosis 12y agoand what about "floating" currency? just my 0.00603698 KWD Kuwaiti Dinars on that topic
- dagw 12y agoI'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 12y agoPascal had subrange types, which were quite nice in that regard. Although I don't remember using them that much.
- StefanKarpinski 12y agoThis 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.
- zvrba 12y agoI agree. I stopped taking him seriously when he wrote that Java didn't have pointers. (Java's references are pointers w/o arithmetic. But they're still pointers: they're occupying space and are allocated separately from the pointee.) Also, in the summary list of features to be removed: in that case the diagrams of "Languages that disallow X" should all be mutually intersected, and I suspect that the resulting intersection would exclude too many valid programs (i.e., you'd end up with the graph "languages taking away the wrong features).
- byuu 12y agoIt 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.
- aikah 12y ago> 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#.
- digitalzombie 12y agoI think Erlang hit everything? Not sure about Cyclic dependencies and I don't think it have Reflection unless I'm wrong.
- sacado2 12y agoNice 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 12y ago> 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.
- efaref 12y agoYet 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 12y agoYep. 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.
- ticking 12y agoIf you install nightly builds on a production system you deserve to get bitten.
- andrept 12y agoWhilst 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.
- kephra 12y agoI 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.
- Grue3 12y agoUnlambda [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
- kragen 11y agoI do not like this article at all. I rebutted it extensively at https://lobste.rs/s/ipml3l/less_is_more_language_features/comments/fnomu1#c_fnomu1 https://lobste.rs/s/ipml3l/less_is_more_language_features/co....