I agree with this. I also know plenty of full stack engineers who are superior in both domains to the so-called specialists.
HN user
rjberry
Software Engineer (Scala, JavaScript) based in London
Hubris, much?
Clojure seems too high level for SICP. Scheme was a perfect fit because of how simple it is and how few language features or data structures it contained. You had to build everything yourself, which was kind of the point.
Primarily because the people who decide salaries are themselves managers.
They pretty much are as without sequencing you can only do the most trivial thing (a single IO action). Most interesting programmes are going to need at least two IO actions.
Premature optimisation ...
Getting a good idea is usually harder than getting the tech right. Being able to quickly make prototypes is the most important thing in a start up.
FYI I'm not a "brogrammer". I work at a large established company and one of my main jobs is making systems work at large scale. But that's not what you do when starting a company. If you make it you can sort out those issues later.
Not sure how that's relevant.
Anyway it's a logical fallacy "appeal to popularity".
I worry that we're gradually moving towards a society that sees privacy as neither a right nor something desirable to an innocent individual.
PHP is pretty popular ...
Or you could use a library explicitly designed with these considerations in mind, that offers the same powerful, familiar combinators, with resource safety and speed. e.g.,
Machines in Haskell: https://hackage.haskell.org/package/machines
Scalaz Stream in Scala: https://github.com/scalaz/scalaz-stream
I've no doubt an equivalent exists for Clojure, too, although I'm not familiar enough with the language to point you in the right direction.
One of the most amazing things about writing the IO parts of your program using libraries like these is how easy they become to test. You don't have to do any dependency injection nonsense, as your combinators work the same regardless of whether they're actually connected to a network socket, a file, or whether they're just consuming a data structure you've constructed in your program. So writing unit tests is basically the same as testing any pure function - you just feed a fixture in and test that what comes out is what you expect.
I found this really useful when writing a football goal push notifications service for a newspaper I work for. I realised that what the service was essentially doing was consuming an event stream from one service, converting it into a different kind of stream, and then sinking it into our service that actually sent the notification to APNS & Google. The program and its tests ended up being much more succinct than what I would normally write, and the whole thing was fun, and took hardly any time.
I would say that is the right tool for the job.
If we assume the operations we're talking about take time linear in proportion to the list, you've just gone from a * n time to b * n time, where b > a. Both of these are still O(n) in Big O notation, which drops constants, because constants unless they're very large or n is extremely large, tend to have relatively little effect on the running time of an algorithm.
Choosing to write more verbose, difficult to decipher, difficult to maintain code, under the claim that it will perform better, is not a good thing: "premature optimisation is the root of all evil".
In practice, if this becomes an issue (which you find out through benchmarking once you know there is a perf issue), most modern languages offer an easy way to swap out your data-structure for a lazily evaluated one, which would then perform the operations in one pass. Languages like Haskell or Clojure are lazy to begin with, so do this by default.
Not that it particularly matters whether we're talking about C++, as it's rather tangential, the original essay by Richard P. Gabriel that coined the term "Worse is Better" talks about C++ (http://www.jwz.org/doc/worse-is-better.html):
The good news is that in 1995 we will have a good operating system and programming language; the bad news is that they will be Unix and C++.
In his later essay on the same topic (http://dreamsongs.com/Files/IsWorseReallyBetter.pdf) he again talks about C++: In the computer world, the example of interest to JOOP readers is C++. Many would concede that languages like Smalltalk, Eiffel, and CLOS are vastly “better" in some sense than C++, but, because of its worse-is-better characteristics, the fortunes of object-oriented programming probably lie with C++.
Simplicity is more often simplicity of implementation than interface. Anyone who's worked with the C standard library knows that it's anything but simple. There's a lot of incidental complexity that is due to design inconsistencies. The complex parts of a language like Haskell are the bits where the ideas themselves are complicated, but the power you get for overcoming that initial learning curve is very great.Sure ... but if a loop is effectively doing a map, a filter, and a bunch of other operations all at once? It's a lot quicker to figure out what's going on if it's been written with combinators (once you're familiar with them) than if it's the vanilla loop.
Paul explains fairly well why he thinks C++ is a good example of "worse is better", including quotes from the language designer to that effect.
I think when you get to more complicated (or at least alien) abstractions like the I/O Monad, it's not about whether it's practical to build software with it. It's perfectly practical - I know people working at big investment banks writing large scale software in Haskell, and they don't have any issue with it. What it comes down to is whether the people you work with will understand or want to invest the amount of time required to learn those abstractions. If you're working with very talented programmers, that might be yes. With the majority of programmers it is probably no.
But we're not talking about the I/O Monad. We're talking about generics, which are not difficult to understand, and that Go lacks them is a shame, as it means that whole layers of abstraction are not available to the programmer, so they end up having to spend more time writing boilerplate.
It's not really a different approach. You had to write a lot of vanilla loops in C, too.
All loops encode simple logic? I don't think that's true.
Either way, a very good reason for not using loops is to make code more readable. If I see a loop I have to run it inside my head to figure out what the intent is, and if there's multiple things going on, that can take time. If I see a 'map' I think "right, this is tranforming every element in this list in this way"; if I see a filter, I think "right, this is removing elements in this list that don't satisfy this property", if I see a groupBy, I think "right, this is grouping elements in this list by this property", etc., etc.
Code isn't only more succinct, it's more human friendly.
I dislike it when people say languages are either 'practical' or 'well designed'. As if a language is only useful if it's not built on sound mathematical ideas ...
Generics are not a particularly new idea and nobody who uses language that implement them yearn for the days when they weren't around.
See Paul's post on the idea that 'worse is better': http://pchiusano.github.io/2014-10-13/worseisworse.html
I think it depends on what you're working on. If the software is completely designed and the desired outcomes known from the beginning then up front planning and processes are probably helpful. But this is such a small percentage of actual software. In 99% of cases the business itself doesn't know exactly what it wants (even though it might initially think it does) and a lot of the seemingly chaotic ways of approaching software engineering are really just reflecting that.
It's a nature vs nurture argument. I think Paul believes certain programmers have some innate quality that means they're magnitudes better than others. As it's innate it's not related to culture. As in you could give certain people as many opportunities and as much education as you like but they will only ever achieve mediocrity.
How can you access Python and Perl libraries?
However you feel about the JVM, it has led to adoption of Clojure, meaning if you're in a big city you have a chance of finding a job doing it. Not many jobs exist where you can use Scheme as your primary language.
But the main reason I wouldn't go back to Scheme is that it doesn't have a rich array of immutable data structures built in. Too quickly the implementations I've seen capitulate to mutation, which makes code harder to reason about, harder to parallelise, and just less fun to write IMO.
I'm looking forward to Emacs implementing Guile, as Elisp is yucky. But realistically that's not going to happen very soon.
Racket is so different it's basically a different language, so I'm not really talking about that.
I do know Chicken and Guile have more advanced library support than the Scheme I worked with on SICP, but neither has as advanced concurrency features or advanced immutable collections, which is why Clojure seems to me a big advance on Scheme. Plus that you can leverage existing libraries on the JVM, plus its excellent compile-to-JavaScript support, etc., etc.
Consider Scala or Clojure. You'll be able to leverage the same libraries you've been using in Java (pretty trivially), while having access to a completely new way of thinking (functional).
I personally prefer Scala as I like strongly, statically typed languages, but I've played about with Clojure and it's very nice, too. A vast number of benefits come from having a rich immutable collections library.
It's definitely possible to learn things from those languages. But languages that are trendy today (at least from my experience in the London job market) - Clojure and Scala - are really amazing, too, and have a lot of interesting ideas about concurrency, immutable data structures, and functional programming. Scheme, when compared to Clojure, seems a bit outdated now IMHO, although its extremely simple, small core makes it great for teaching.