Switching a programming language is a major decision for a company. It requires changing the runtime library and possibly re-writing a lot of in-house infrastructure. While personal affinity to a language is important, making a decision for a company might have substantial business consequences.
For a company, overall language adoption, availability of libraries, tools and talent (and I'm not talking about training someone to be productive; sooner or later you need real experts and training an expert is expensive in any language) are extremely important. That's why when choosing between two languages, assuming both are well suited to the job at hand, a company should always pick the one with the wider adoption unless the other is so significantly "better" to trump lesser adoption. And the smaller the adoption, the "better" the language needs to be.
There's no doubt Haskell is an excellent programming language. I'm learning it and enjoying it. But because it has such low adoption rates, it needs to provide benefits that are orders of magnitude higher than other, more popular languages. I guess that for some very particular use cases it does, but I just can't see it in the vast majority of cases.
Hobbyists can afford playing with different languages, and can (and perhaps should) jump from one to the next. Companies can't (or most certainly shouldn't, unless for toy projects); they should pick (wisely) a popular language that's right for them, and just stick with it.
BTW, I think that if a company does want to try a good, though not-so-popular language, it should pick a JVM language, as the interoperability among JVM languages, and the availability of libraries that can be shared among the popular and less-so JVM languages, reduces much of the risk.
> That's why when choosing between two languages, assuming both are well suited to the job at hand, a company should always pick the one with the wider adoption unless the other is so significantly "better" to trump lesser adoption.
I think it plateaus at a certain point. You don't need the largest community, you just need critical mass so you don't have to build everything yourself. In fact, at some point being the biggest ends up diluting the talent pool because of the number of people getting into it for the money, and this is becoming a much bigger problem as the traditional job economy dries up and the demand for programmers increases.
As to being "significantly better", I think Haskell has that in spades. In fact the things that make Haskell better are probably difficult to appreciate by a lot of younger programmers who are using relatively new languages and frameworks like Node and Rails. When you start seeing the effects of code rot and programmer turnover on a codebase over time, the types of static checks that are a couple orders more powerful and simultaneously less verbose and restrictive than what most people think of when they hear "static language" (ie. Java), then Haskell really starts to shine.
Personally I think companies that invest in Haskell are going to start seeing major dividends in terms of productivity and agility over the lifetime of the company.
I don't disagree with your major point - "companies that invest in [[functional programming languages with a strong typing system]] are going to start seeing major dividends in terms of productivity and agility over the lifetime of the company." Haskell in particular I'm not sure of - the 'code rot' could easily manifest itself in performance. Haskell's laziness makes slow code and code that use up a lot of memory hard to locate, and at times require significant refactoring, to correct, though this is offset by haskell's type system somewhat...
You can see this manifesting in the project of the article's author under the "Not All Sunshine and Rainbows" section.
What I'm getting at - Bad code can be written in any language. IMO what's more important with regards to the language is how easy does it make to fix it.
I've only used haskell for my thesis project as well as having toyed with it in my free time now and then over the past two years, and have no commercial Haskell experience, so perhaps someone with more experience can chime in...
So far as code leaks go I think the professional story is divided. Everyone runs into them, some people learn to fix them, those that do tend to think of them as a little annoying but not significantly more so than profiling and tuning strict code.
Fortunately, outside of space leaks the Haskell runtime is actually really quite fast. At the end of the day most of your code which isn't leaky (buggy) will be really fast almost for free.
But that second step can be painful right now. The tools exist to profile and debug space leaks, but they're not cohesive and require a fair amount of black knowledge. It's also fairly murky as to how to escalate: if you've identified your problem area, strictified it a bunch, and it's still broken... well what then?
The answer seems to be growing that internal expertise. There are enough people out there writing enough stuff that a dedicated student of the runtime can learn all of the tricks needed to really attack failures like that—profiling, unboxing, reading core, figuring out the inliner, and a variety of style guide hints to making things all work smoothly (e.g. avoid the Writer monad).
Speaking as a part time GHC contributor (and alleged "core haskeller"), theres A LOT of exciting work going on right now for improving debuggability wrt correctness bugs and performance tuning.
GHC 7.10 is slated to (hopefully have):
1. exceptions will come with stack traces!
a. Some of this same work will also allow for sampling based based performance tools
b. mind you, a lots still in flux
2. theres a non zero chance you'll have in langauge facilities to create threads that get killed automatically if they exceed certain programaticaly set CPU/Memory/other resource limits!
3. Other things which are a combination of hard work and clever research grade engineering
I myself (as Tel knows) have plans for making GHC haskell AMAZING for numerical computing, and my wee ways of contributing to GHC are guided by that goal
Sure, or any strict language. There are a lot of benefits to laziness though even before you start to consider other Haskell/OCaml deltas. Laziness provides a much better platform for separation of concerns which has enabled Haskell programs to have an unheard of degree of decomposition and reuse.
As always, it's a tradeoff. At this point, I don't personally feel afraid of debugging core or profiling space leaks. I don't feel like I'm much more than 50% up to date on techniques to fix them... but when they arise I have little trouble eliminating them.
Lazy resource deallocation is a problem of lazy IO. The solution is to not use lazy IO and that's tremendously tenable today due to libraries like pipes and conduit.
On a quick glance I'd suggest (in addition to the responses already given) to (a) use a more efficient structure for holding events (Data.Sequence or maybe Pipes) since (++) has bad performance when the left argument is large (as is definitely your case), (b) for all of your "static" types, add a strictness annotation and possibly an {-# UNBOX #-}, (c) consider replacing System.Random with mwc-random, it's more efficient and has better randomness.
Diving into processOrder since it's the big time/space hog I think strictness annotations might help uniformly since there's a lot of packing/unpacking going on. To really dig in you can add some manual SCC annotations or dump core on it and see what's doing all the indirection.
These are all pretty general, high-level Haskell performance notes, by which I mean if you learn them once you'll be able to apply them much more quickly in the future whenever you find bottlenecks.
Yeah, a good starting point is that if something is running slow, and if there's lists involved, look at how you're using them. Also, as a special case, replace String with (strict or lazy) Text or Bytestring, as appropriate.
Unfortunately OCaml leaves a lot to be desired after working with Haskell for a significant amount of time. As far as I know GHC's extensions makes the type system much more powerful in non trivial ways. For example things like type families, data kinds, kind polymorphism, type level naturals, RankNTypes, and so on. That being said OCaml is still orders of magnitude better than most other options. As well, a least for me, it is a lot more work introduce explicit laziness all over the place into eager language, than it is to go the other way and apply selective strictness.
Well, most important is probably availability and stability of domain relevant libraries in either language. There are areas Haskell has well covered and areas where it's thinner...
It's also been the case historically that Haskell had much better concurrency support - I'm not sure whether that's still true - in which case a more parallel workload might push Haskell-wards (if it's not so parallel that simply spinning up N entirely separate processes make sense, in which case concurrency support doesn't matter).
I'm not familiar enough with the current state of OCaml, or with Haskell libraries outside the domains I've been playing in, to really give a thorough run-down of specific domains, sadly. I'd be interested to see it if someone else took a swing.
> What I'm getting at - Bad code can be written in any language. IMO what's more important with regards to the language is how easy does it make to fix it.
Hm, not sure I agree with this. As a professional rubyist I am aware of the wide variety of bugs that can and do happen because of things that Haskell simply wouldn't allow you to do (at least without pathological abuse of the language that would never pass a code review). The thing that makes Haskell special to me is just how much the commitment to functional purity and a powerful type system buy you in terms of decreasing the surface are of potential problems.
You've rightly pointed out that Haskell has its own memory and performance pitfalls that are very difficult to debug, but my instinct is that those things, while perhaps being showstoppers for embedded and realtime systems, are not intractable in a typical server environment, and that it's orders magnitude easier to improve the tooling around detecting and solving these problems than it is to ever make a language like Ruby code safer and require fewer tests.
Of course I'm an utter beginner at Haskell, so I don't have a real sense of its downsides at scale, so take my opinion with a teaspoon of salt.
I really wish Haskell weren't lazy by default. Laziness is great in places, allows you to write very declaratively, and even allows for some computations that would otherwise be impossible or less efficient. However, most of the time, you envision and write code as a series of steps (even though theoretically in a functional language, you're just writing an expression). In a practical matter, the cases in which lazy evaluation becomes desirable exist, but are limited. Most of the time, you want, and expect when you write it, your code to be evaluated strictly. Of course you can enforce strictness manually with `seq` and bang notations and the like, but this clutters up your code and might not even matter if you're using library code which doesn't do the same. IMO Haskell would greatly served by having laziness be opt-in, not opt-out. But its pretty ingrained at this point, and there doesn't seem to be too much of a push to change it (who knows how much extant code would be broken by such a change).
> However, most of the time, you envision and write code as a series of steps
Perhaps counterintuitively, I disagree here. I think using laziness enough will eventually change your mind that most code must be thought of in such discrete steps. Instead, I tend to keep in mind and think constantly about how a computation might be partially delivered and how it might be consumed. This is part of the declarative promise of laziness playing out, I feel.
Ultimately I don't think there's a "right" decision. I'm comfortable in strict and lazy languages. I do think everyone should experience both strict and lazy evaluations enough to no longer be worried about them and instead just see each as another, separate form of computation. At the very least it'll make you much more aware of what evaluation order and normalization feel like.
(I'd also highly recommend everyone plays with a total language for a while, too. That feel is quite distinct.)
At the end of the day, I feel like having laziness by default and picking around for places which ought to be strict is very dual to having strictness be default and picking around for places to make lazy. Both are pretty annoying.
I do think people are generally more sensitive to problems of excessive laziness than they are to problems of excessive strictness. Manually wiring around stopping criteria and consumption control through all of your (no longer decomposable) loops/recursion is the price of strictness. Both problems are helped by tooling, as well.
It certainly doesn't need to be thought of as a series of discrete steps, and laziness supports that view. Laziness is part of the Haskell philosophy of staying high-level: so high level that you're not even telling the machine in what order to perform its computations. However I stand by my contention that most of the time, when we as programmers write code to be executed, we're envisioning it as a series of steps: this is a major reason why monadic code is so popular and why Haskell's designers invented do-syntax to imitate imperative code. You say that it would be just as annoying to specify the individual places to make lazy, but I struggle to agree with that. After all, the vast majority of programming languages have strictness by default (even other functional languages like SML or Idris) and don't present any difficulty to the programmer. On the other hand, getting around Haskell's default laziness is a constant issue for performance-minded code (from what I've seen; certainly it's a constant topic of conversation), and makes stack traces and debugging hard.
To give a specific example, strictness is a pain in the butt when you try to write a combinator library. I have once tried to write a Parsec clone for Ocaml, and sometimes, I had to write functions in eta-expanded form, lest I enter an infinite recursion.
tel is right. We are desensitized to the problems of strict evaluation. It's the default, so we accept it as a fact of life. Lazy evaluation on the other hand, is "new", so whatever problems it have are magnified by our unwillingness to handle new problems. This is made even worse when we try to use lazy evaluation the same way we use strict evaluation: you get even more problems, and none of the advantages.
I think people are desensitized to the downsides of strict code. I don't think that either is "better", but I appreciate having spent a lot of time working in a lazy-by-default language.
I think the best option is total-by-default, honestly. I really like how Idris works, for instance. If you must work with some code which is non-normalizing then your choice of laziness/strictness can be a major architectural one—like organizing the entire pipeline of your program around a lazy consumer stream/set of coroutines.
Idris, one of the candidates to be "the next Haskell", is strict by default. It's not a mature language (+ecosystem) yet, but certainly usable and interesting. If you like exploring new cool concepts, you should have a look at it! (Plus it's very easy to learn when you know Haskell already.)
Idris also makes it trivial to annotate laziness, something most languages are lacking, also because it is total by default the evaluation strategy doesn't actually matter for all the total code.
Yeah, Idris really seems like a cool language. I wish it was better documented, at both the usage- and implementation-level. The source code for example is woefully under-commented; a quick grep of the source shows about one line of comments for 15 lines of code.
I think you are generalizing a bit too much. You may very well write code that way, but I don't. Having come to haskell from several years with ocaml, I found the switch to lazy by default to be entirely irrelevant and inconsequential. As long as I have the ability to be strict when I need to, and lazy when I need to (which both languages give me), I don't care which is the default.
In practice, I've found space leaks in Haskell code to be a bit analogous to extraneous copies in C++ code or NullPointerExceptions in Java: They're usually not a huge problem, but sometimes you get it wrong even if you're experienced and know what you're doing.
We've only had one really disasterous space leak and it was caught and reverted right away. The hardest ones are generally the slow ones where the servers take 3-7 days to run out of RAM and fall over. If you never go more than a few hours without a code push, you might not even notice it.
FWIW one reason the service mentioned in the blog was so fast was because of laziness. Because it didn't need most of the request, the work required to parse it was mostly avoided.
>Haskell's laziness makes slow code and code that use up a lot of memory hard to locate, and at times require significant refactoring, to correct
This is largely mythology built up around a misunderstanding of the grain of truth at the core of it. Lazyness can create pathological performance issues, just as strictness can. 90% of the time neither matters, 5% of the time you need strictness, 5% of the time you need laziness. Using strictness in haskell is very simple, and knowing when you need to is very easy to learn. The profiling tools make it incredibly obvious when laziness is causing a problem. And I can't even imagine a case where it requires significant refactoring. It is generally adding a couple of "!"s or importing Module.Strict instead of Module.Lazy.
> This is largely mythology built up around a misunderstanding of the grain of truth at the core of it.
I agree. I've encountered a few space leaks in my Haskell code, but it's always been pretty easy to track down: profile the memory usage and look for a conspicuously huge peak. It will usually be fixable by forcing an intermediate result, or using a different recursion strategy (eg. a fold instead of explicit recursion). Haskell functions tend to be so small and single-purpose that there are no knock-on effects of doing this refactoring.
I think there's a perception that 'Haskell is difficult to debug', whereas the reality is more like 'Haskell will reject all of the easy bugs'. In other words, the average difficulty of a Haskell bug may be higher, but the density is less.
Yes. Most recommendations for implementing recursion in Haskell that I've seen have amounted to writing it in terms of folds. You gain the benefit of experts vetting the code, and novices being able to understand what it is doing it a glance.
I'd like to add that for the 90% of the time it doesn't matter, 95% of that time you could use a total language. This is one of the joys of using a total language: seeing what's still easy!
I see no reason why strictness would have pathological performance problems. If you don't need to compute a value, dinner compute it. No performance problems.
I also see no reason you would need laziness for 5% of programming problems. That seems absurd given that the overwhelming majority of programmers have gotten along without it for 50 years.
Most programmers use laziness all over the place, they unfortunately have few tools for introducing it, and thus don't recognize it as such.
For example in C/C++ your only ability to introduce laziness is via manual thunking, if, or the logical conjunctive (&&), and disjunction (||) which have non-strict semantics. For example you often see the idiom of:
if (x != NULL && x.foo == bar) { ... }
Which relies on (&&) being lazy in its second argument. Lazy languages allow one to write things like && and if without needing them baked in. For example:
until :: Bool -> a -> a -> a
until b t f = case (not b)
True -> t
False -> f
>I see no reason why strictness would have pathological performance problems. If you don't need to compute a value, dinner compute it. No performance problems.
You just answered your own question. "If you don't need to compute a value, then don't compute it" is laziness.
>That seems absurd given that the overwhelming majority of programmers have gotten along without it for 50 years.
No they haven't. Most programmers don't understand it, but they tend to use it on a regular basis.
> You just answered your own question. "If you don't need to compute a value, then don't compute it" is laziness.
No -- "laziness" is saying "the programmer wants us to compute this value, but we don't need it just yet, so we're going to create a thunk that will sit on the heap until it's evaluated or garbage collected". Strictness is doing what the programmer says, which involves the programmer deciding what does and does not have to be computed while he's writing the program.
Perhaps I was not clear. You stated the very reason for laziness: to not compute things you don't need computed. There are many cases where it is very beneficial to be able to determine this at evaluation time, rather than when writing the code. Hence laziness, and hence it being used all the time in most mainstream languages.
I implement laziness "manually" all the time in Objective-C.
For example, imagine something happens which invalidates a property which must be calculated (say, a bounding box for a graphical object).
Instead of immediately recomputing it, just set it to nil, and only recompute it the next time the property is actually accessed.
This way you can harmlessly invalidate it multiple times without doing a potentially expensive calculation (which just gets thrown out by subsequent invalidations).
It's fair to say that it's laziness, in my view. That sort of thing is just a manual implementation of what laziness would do in a particular situation. The closure that the thunk would hold is essentially diffused into the object that contains the lazy property.
Every year I read on HN that this year is Haskell's year. What's so different about 2014? The whole 'only for REAL PROGRAMMERS ' argument still hasn't gotten any traction, my dudes, lisper's been trying it since the beginning of time.
Anyone got a link to a good todo-mvc implementation or something like that?
>Every year I read on HN that this year is Haskell's year
Really? Every year I read on haskell a bunch of uniformed dismissal of haskell from people who seem to have a deep seated hatred of learning. How do you get "this year is haskell's year" from his comment at all?
>Anyone got a link to a good todo-mvc implementation
A what? MVC is an object oriented pattern, I would certainly hope nobody tried to do it in haskell.
Every year, on a Haskell thread, someone calls me a noob for not knowing it. Then you get like the same 3 or 4 dudes talking about how it's only a matter of time until the enterprise picks it up and all the dumb people go away. The some stuff about static analysis, and I'm like, that's it?
I just want to see a working to-do list application, or anything resembling that size.
>Every year, on a Haskell thread, someone calls me a noob for not knowing it.
That sounds a lot like you putting words in other people's mouths. I've never seen anything like that.
>Then you get like the same 3 or 4 dudes talking about how it's only a matter of time until the enterprise picks it up and all the dumb people go away.
Or that.
>I just want to see a working to-do list application, or anything resembling that size before we start the name-calling.
Now I am even more confused. Why do you want to call people names, who do you want to call names, and why do you want to see some random toy app first?
"In fact the things that make Haskell better are probably difficult to appreciate by a lot of younger programmers who are using relatively new languages and frameworks like Node and Rails"
Are you reading this stuff? I'm not going to talk to you if you're just being a dick. I know you can read, so stop being obtuse. Come up with something, quick.
Yes, I am reading it. That does not say anything even remotely close to "ulisesrmzroche is a noob for not knowing haskell". I am not being a dick, I am being polite and trying to understand where you are coming from. I don't see how insulting me helps either of us, only how it makes your claims that "haskell people keep insulting me" rather hypocritical.
Ok, nevermind dude. You're either not being honest or really can't understand nuance in the English language, which, either or, doesn't give us a lot of room for arguing. Saying 'a lot of younger programmers probably don't appreciate the best parts of haskell' is clear Ageism. I think it's annoying.
Since I said that, let me be perfectly clear. It's not ageism, it's experience-ism. There are things that you learn when you work on a code base that is 10, 20, 30 years old. There are things you learn when you've been a programmer for 10, 20, 30 years. A lot of the pain Haskell solves isn't apparent in a lot of Node or Rails apps simply because they are so new. That's it, don't read too much into it.
Hey don't be the white person complaining about racism or the man complaining about sexism. If young people weren't getting a shot in the tech industry because of discrimination then I'd be a tad more sensitive to your protestations. But as it is, you're irrationally sticking your head in the sand. If you don't think that learn things and come to new insights that take years to develop then there's nothing I can say to convince you. But if you stick with it for a while, come back to me in 10 years and let's do a code review on what you wrote today and then we'll discuss if you still think I'm ageist for saying there are particular lessons learned through experience.
Hey, don't take it too bad. If you say anything negative about certain topics you will get downvotes. It's mostly because people not in love with certain topics have learned to just leave them alone and it's left a big echo chamber where anything positive will be upvoted and anything negative will be downvoted.
You'll notice this by seeing that in non-Haskell threads, critical remarks about Haskell are upvoted, while in 'Haskell threads' such as this one where an echo chamber forms, anything critical about Haskell will be downvoted. It's an interesting behavior, but it says a lot more about certain cliques than it does about HN.
Don't worry about it too much and get on with actually using practical tools to solve real problems and leave the echo chamber in their own peaceful world. Everyone is much better off.
Wow, that's smug. My comment had nothing to do with Haskell. He said I was being ageist when I said there are some things that only become apparent with experience.
Reverse-agism is part of an egalitarian mindset of many HN commenters, who believe experience is more important than individual variation in skill. For them, skill and ability increase with experience, so it is an objective fact that young developers as a group are going to be worse by most measures.
So when someone out of college makes more than someone with 10 years experience, they assume this must be due to agism, since the 10 years experience must, in their view, be more important than being one of the top graduates of that year.
On the original comment, I usually associate Haskell with younger programmers, since it is taught in many schools like MIT and UW. So I found the comment a bit grating since I thought Haskell was the one thing we could have.
"On the original comment, I usually associate Haskell with younger programmers"
Huh. FWIW, I associate it more with older programmers. Certainly, some of the more interesting and visible members of the Bay Area meetup are older than me and I'm not fresh out of school.
As a 21 year old this is not ageism in the slightest, it is reality that as you see more and more problems the applicability of some solutions becomes more apparent. For example, when talking to seasoned engineers who have written a lot of concurrent code they immediately understand the value of purity, and immutable data because they have been burned by the problems that crop up when you try to write threaded programs using shared mutable memory.
On the other hand if you take a 20 year old student still doing their undergraduate degree (like many of the people in the classes I TA), and try to explain the value of immutable data, many of them just don't get it. It has nothing to do with their intelligence or age, but the fact that they haven't written enough code to develop a sense of why such a thing would be useful.
If you want example apps their are plenty littering the internet all covering the various web frameworks, for example here is one covering Happstack: http://happstack.com/docs/crashcourse/index.html
Are you for real? It's like deja-vu. I guess I should know better by now. There's probably some guy in a lisp thread who has been on my end of this argument for the last 20 years or so.
I know what ageism is. You quoted something that is not an example of it. Given that this is entirely pointless, would you mind if we went back to the actual discussion?
The dude that said just said it was 'experience-ism', do I have to explain what nuance is? You've gotta help me out here, I can't do it all on my own.
All you keep saying is 'no its not'. And then trying to move on as if that's the final word. Not interested in talking to you bro, we're not going to come to any kind of understanding.
Hi pron, you're absolutely right. Transitioning to a new language and runtime is a huge undertaking. I work with Andy, the author, and he took exactly the right steps in getting Haskell adopted. Nothing was forced through, and he got both engineering and executive buy-in for the transition.
We do not use Haskell exclusively -- we also have a pile of PHP and some Python -- but it's awesome to have such a great tool at our disposal. I've been very impressed at how mature GHC's runtime system is. The Haskell services have run perfectly for months with no problems.
The Haskell type system even lets us say, for example, that you cannot talk to MySQL within a Redis transaction, preventing entire classes of bugs.
(I have many complaints about Haskell too, but net net, it's an enormous win over PHP and Python.)
The Haskell type system does not let you do that by default. What kind of monads do you use? Are they part of some library or did you develop them in house?
Do you have a policy around the use of I/O monads versus specialized monads for controlling access like that?
I can't remember the exact monad names, but Redis transactions are in a specific Monad that does not give you access to IO. I don't know whether that's functionality provided by Hedis or whether we wrote that.
We have a general Monad typeclass called "World" which gives access to MySQL, Redis, HTTP, Memcache, and so on. There are more specific Monad typeclasses if you have a function that, for example, should only ever talk to Memcache and nothing else. I think it's called SupportsMemcache.
My point about the Haskell type system is that the _ability_ to limit the operations in a context is a capability that few languages have.
We have a simple mechanism for controlling access to IO.
There are a series of type classes that provide access to all of the IO based services (mysql, redis, memcache, etc.). All of the request handlers are written to use these type classes not IO. There are two instances of the type classes, one pure for tests using fakes, and one real using IO.
Yes of course, but it's a next step to actually use that ability. Very cool that you do :) It takes some tough developer discipline to deprive yourself from the power or the IO monad.
It might take some developer discipline to set things up that way initially, but the great thing about Haskell is that once you have it set up, it does not require any developer discipline. If your system runs in the Foo monad, you can only do operations that have been made available to you. This is the big problem that other languages have...you simply cannot enforce some of these guarantees.
The type system allows you to say "you cannot use that here". It doesn't say it itself, and particular libraries may or may not exploit the ability. A classic example is STM, which relies on IO internally but doesn't let you use IO inside it (without resorting to aptly named and justly avoided unsafe... functions) because that would be dumb.
I see it the other way around. It's easier to find experts in esoteric languages because you don't have to sift through the same piles of dullards that use main stream languages. A person who has chosen to use a language like Haskell is probably not your average brogrammer.
Also, PHP in particular is so bad, to become an expert in it suggests a willing blindness to its problems, or a general attitude of "live with problems" that converges to a complete shit sandwich over time. When one could have just switched to something else so long ago, PHP is a point against any claim of being a software expert.
It is just like the traffic in DC: whatever your arguments against doing anything about a bad situation because "we don't have the time" or "we don't have the money", it ain't getting any better in the future. If you don't fix it now, it won't ever get fixed.
I about 50% agree with you here. The factor that you're forgetting is the filter effect: Haskell is not adopted, it's learned after a thankless (there are no jobs) process of deep study. This means that the quality of available talent is of a much higher standard. If you adopt a technology with these characteristics, and you're one of the few who are hiring in this pool then you have a significant advantage!
The only risk in my mind is when the big boys figure it out and start gobbling up Haskellers! :-)
We've trained a few people up on Haskell at IMVU now, and I'm starting to think that learning Haskell is at its very hardest when you're building tiny toy applications and all you have for answers are google searches and books.
It seems to help a lot to root the learning process in a specific concrete goal, in an existing codebase that already has idioms and patterns in-place, and to offer direct access to people who have answers to newbie questions.
I learned a lot about writing good Haskell code from working with Yesod. Since it's a framework it structures your code for you and exposes you to a lot of well-designed patterns. Yesod and Conduit are two of the best written packages I've come into contact with (though there are many many more) and simply following their guides has given me new insight into what high-quality Haskell code should look like.
I think this is a bad filter. I'd rather filter prospective employees based on an ambitious algorithm or a complex project they've done. Most of the people I know that like solving hard problems with novel data structures, algorithms, or mechanically-sympathetic implementations are averse to spending time on learning new programming languages. It seems to them (and to me) like focusing on bling rather than on substance.
Of course, this is a crude generalization, and obviously, anyone who's interested in learning any new, perhaps unappreciated, and difficult skill would make a good hire, and Haskellers are no exception. But I wouldn't filter based on programming language preference.
First, the implicit point you are making that ambitious algorithms or novel data structures are useful in industry. For the vast majority of applications this simply isn't the case.
Second, the argument that people interested in learning advanced computer science aren't interested in learning new programming languages. This goes against all of my experience; my friends who are most interested in advanced computer science concepts are exactly the ones who spend the most time exploring different programming paradigms as well.
I have one idea of what the distinction might be, though: learning different programming paradigms is very different from learning different programming languages. I certainly wouldn't want to waste time learning yet another Java or yet another Python, since as you say that would be focusing on bling rather than substance. If this is the distinction, Haskell comes out fine, as it is clearly a different paradigm from the mainstream languages.
Wow, completely the opposite experience here. The majority of the prog. lang. enthusiasts I know are deeply into other aspects of computer science. They love high level languages because they can express their ideas so cleanly. Lots of devs are drooling over Rust because it allows us clean code with C-level control over memory layout and allocation.
The majority of PL enthusiasts might well be interested in other aspects of computer science, but I was pointing out the the converse isn't true: the majority of people interested in the more challenging aspects of comp sci are not necessarily interested in programming languages, which is why this would be a bad filter.
I drool over Rust, too, but I wouldn't switch my company's (little) C++ code to Rust tomorrow. Also, Rust appeals mostly to C/C++ programmers, who don't usually need as many OSS/OTS libraries as application-level programmers (and those, usually specialized, libraries they do need, either work with Rust or don't; if they don't, that alone would be a deal breaker).
People who risk their company's investment (or make any important decision) based on stuff they drool over, might need to get their priorities straight.
As primarily a Scala programmer, Rust appeals greatly to me. It's basically the same language without cruft inherited from Java, and its much more amenable to optimisation that Java. For example, large heaps have unacceptable GC pauses, so projects like Cassandra invest a lot of effort into "off-heap" memory management (i.e. manual memory management). With Rust that just works a whole heap better (pun intended).
People tend to talk about "expressiveness" when discussing programming languages, which usually mean concision. I'm much more interested in what I call "expressive width", which means how high- and low-level can get in a language. PHP is little expressive width, because its runtime is so poor. Scala has good expressive width because I can play with threads, CAS operations, mem-mapped files, and even manual memory management (which is going quite far off-piste, but possible with sun.Unsafe). Rust has even more expressive width than Scala.
[Rust needs higher-kinded types before it will truly make me happy. I believe they'll arrive soonish.]
For your other points:
- P(interested in PL | interested in CS) != P(interested in CS | interested in PL).
Agreed, but I don't think it matters. Anyone interested in discrete maths (so, CS) will pick up Haskell quickly.
- "People who risk their company's investment (or make any important decision) based on stuff they drool over, might need to get their priorities straight."
Depends on your company's size and ethos. I'd rather have a few great people than a room full of monkeys, but I understand arguments that go the other way.
Many of the super programmers at google have PL research backgrounds! Like Jeff Dean. Most of the others are in systems, which are fairly aligned, and often both. Such PL enthusiasts are the last ones to try and switch the language being used.
> Such PL enthusiasts are the last ones to try and switch the language being used.
As a PL enthusiast myself, I agree with this.
By the time I had my first job out of college, I'd written programs in some twenty-odd different PLs, and felt comfortable with many of them. My employer needed me to learn SAS, so I did. They wanted some code written in Java, so I wrote it. Neither SAS nor Java is particularly fun to program in the way Haskell et al. are, but that's what they wanted, and I could do it.
There was a whole team of Java guys, and when asked to learn SAS they sat around bemoaning it and whining about how Java is so much better. "Why not just do it in Java?" they asked.
I've run into similar experiences at every company since. If people don't want Java, they want C++ or Perl. If someone writes a utility in Python, there are complainers. Some programmers will refuse to even use a particular program if it's not written in their language of choice.
In my experience, it's usually the people who aren't PL enthusiasts that are more opinionated about language choice, simply because they're less flexible.
That means it's a great filter, though, in one way—it has high precision but low recall. If you're building a smallish team on a lowish budget then precision is vital and recall isn't.
Assuming the remaining population is sufficient (which, for Haskell, for a smallish team, in a job that can be done remotely or is in a tech hot-spot, it probably is).
Haskell was inflicted on my whole CS class, and for that class P(avid problem solver|likes haskell) was mighty high.
As long as you can sample from a decent talent pool, I think it's a good filter. (Provided you only care about your outcome, not fairness to job seekers.)
learning another script language is totally different from learning Haskell. For the latter it requires much more than syntax translation, which is implied in your comment.
I ofter read that Python developers are of a higher quality than PHP developers.
So downvoters, how do you differentiate a good coder who chose the language based on features and reputation, over a poor coder, who chose it "because its easier than Perl"?
The point I am trying to make is that I think there are benefits to having a higher bar to entry.
If you hang out a lot on reddit, this particular site is written in python, so there might be a bias. Python is also often chosen for entry courses in CS, while PHP is probably more picked up by people who need to know it for work, so it is not necessarly well understood by many of its newly professional users.
My guess is that python is a more well rounded language, with a nicer syntax, which enforces indentation rules (that's a plus for teachers). PHP only shows its merits after a while, and through good practices.
However, looking at the charts over at langpop.com, I don't see how people would choose PHP over perl for its easiness, given the wide difference of popularity. They just don't sit in the same ballpark (except for maybe a few specific markets).
I think it is pretty safe to say that the average quality of PHP developers is lower than any other language except possibly javascript. There's a huge amount of very low knowledge people in both camps dragging the average down. Comparing python to ruby or perl things seem pretty similar.
I've been hiring both Python and PHP devs. It's absolutely night and day difference.
I don't think you need to worry... YET.
1. Python is simply a superior language.
2. There isn't currently a "WordPress" type of project that is bringing in droves of awful Python coders.
Will it eventually turn sour? If things continue with Python, I think so. But the language and community will do a good job keeping things slightly sane. I think the Python community understands the importance on both fronts. Sure, bad programmers are everywhere. But bad contributing programmers need to be kept at bay and out of our favorite GitHub repo's.
In my experiences as a solo founder needing junior-level programmers, Python is currently in the perfect sweet spot for adoption vs. quality. It's largely up to us in the community as to how long that will last.
Python tries to make it easier to avoid pitfalls. In contrast to PHP or Perl, Python believes that
> There should be one--and preferably only one--obvious way to do it.
Even something like significant whitespace forces everyone to format their code similarly.
That's not to say an incompetent programmer can't write horrible code in Python, just that Python tries to make it easier to do the right thing than the wrong thing.
BTW, I think that if a company does want to try a good, though not-so-popular language, it should pick a JVM language, as the interoperability among JVM languages, and the availability of libraries that can be shared among the popular and less-so JVM languages, reduces much of the risk.
I used to agree with that, but I think the interoperability of JVM languages is overstated. For instance, using a Scala library from Java or Kotlin is a painful experience, unless its developers invested time to export a Java-friendly API (e.g. to some extend Akka).
The situation for Haskell/C is comparable to that of Scala/Java. It is easy to use Java libraries from Scala, but hard to use Scala libraries from Java. In a similar fashion, it is easy to use C libraries from Haskell, but hard to use Haskell libraries from C.
My experience with large projects is that it is more important to avoid using a large mixture of languages without clearly defined boundaries. E.g. nowadays it's popular to use a mixture of C, C++, Python, Cython, and perhaps even a little Prolog on the side. And while it all links, it is very hard to maintain, debug or for a new developer to get into. Pick one language/ecosystem and stick to it.
For a given executable, you mean. One exec, one team, one language ecosystem ("C + Lua" would count as one ecosystem).
By the time two different team work on two different executables, the boundaries you speak of are pretty clear: the two pieces of program will communicate through inter-processes means: files, pipes, sockets… At that point, you don't really care about programming language mismatch: the only common logic will be parsing and generating the common formats. If those are simple enough, you simply won't care. And if they are not, you can always use a compiler generator that outputs C code, and use that in both programs (most ecosystems have a C FFI, so that shouldn't be too hard).
You still do care, you just care less. Two languages means two ecosystems. If you build a fancy business object library that could be beneficial in two execs... you'll only see that benefit if they happen to share languages or be FFI compatible.
I've worked on projects with single executable deliverables that needed two languages: Python for the interesting bits, and C for the fast bits. FFI is not always fun (I used ctypes) but there was really no other way.
This argument is called "Go home and use Java" in latin.
Infact, by this logic it is impossible for any language to be a competitor to java, because it is trumped in community, adoption and programmer replacability.
The diff any language can offer cannot be more than the advantage java has in these three aspects for a guy who wants to use java. Typically, you will find the good java advocate agreeing that the shiny new language is better - but common the net advantage is "trumped" by java's reach.
In case someone has ever convinced the seasoned java advocate, please share your angle of attack and the language of your choice. I don't think there are any.
Java was not the first popular programming language, and software has been around long enough to know how language adoption works. The secret is incrementalism. If you want to consider the Java world, then Java was an incremental change in syntax over C++ and a revolution in the runtime, while other JVM languages might be an incremental change in use of the runtime but a revolution in syntax/concepts.
So incrementalism is one way. The other is picking a niche. Some languages are widely used in certain industries because they are particularly well suited to some use cases. That kind of success is often enough, but sometimes a language might expand from its original niche after having achieved success there.
But in many cases, the sentiment you're reeling against is actually correct. The fact that language B is better than language A does not always (or even often) mean that the best course of action for the organization is to adopt language B. Switching a language has a very significant cost, and it's a change that a company should never consider more often than once every five years and optimally not more than once a decade, unless language B is so advantageous that it would be stupid not to switch. Well, that's assuming the choice of language A was wise in the first place.
Because the switching cost is high, there's a very high bar to competing successfully, as there well should be.
We write our own application code. We often write our own (specialized) library code. Sometimes, we even write our own little frameworks. But we very rarely write our own languages. There's a wall somehow between languages and everything else: the language is something you chose. Everything else is something you might build —or at least adapt to your ends.
Why? This is beyond silly.
Take one example: Closures in Java. Closures are very useful. If you put garbage collection in your language, but forget about closures, you fail at language design forever. Java made this mistake, and forced us to use anonymous inner classes, and other equally horrible workarounds instead. Java need closures and we knew that for years.
What did we do? We waited for the landlord.
We didn't have to. I'm sure some of you have heard, there's this nifty concept called "source to source transformation"[1]. Here's how it translate in Java: you take source code that is like Java, but is not quite Java. Feed that into your compiler, output Java code, and voilà, you have modified the language. In the specific case of closures, it would mean invent a syntax for closures, and compile that into those ugly anonymous inner classes.
Some people probably have done that, and I just don't know about them. Anyway, it seems the technique didn't get any traction. (The only example I know of is here[2]. Skip to page 6 and 7. Then read the whole thing.)
I don't know about you, but the next time someone forces me to use a language that sucks, I'm going to at least write a preprocessor, and modify the damn language. In other words, I can write LISP in any language, and I will. And when I'm done, even COBOL won't suck.
The gatekeepers at an organization refuse to allow devs to submit unreadable machine generated source code, and won't allow the fragmentation of multiple original source languages.
But 1000 ad-hoc human written micro a frameworks and spaghetti approximations or workarounds to closures etc? No problem. Massive LoC throughput, solving "complex problems" as demonstrated by code base size...
I often find that the people deriding the "gatekeepers" have never been gatekeepers themselves. Obviously, every developer should do whatever the hell they find most productive, because developers are known to have such incredible foresight, they never just pick tools they feel like using for no good reason, and they always consider the grand-scale effect of their decisions on the organization as a whole.
I have a feeling that the "grand-scale effect" you speak of is overrated. Any sufficiently big organization will be fragmented into groups that hardly talk to each other. At that point, if you want code that's usable by several groups, you must publish it as if it were Open Source (and have it compete with actual Open Source code). That is so much effort that most simply won't do it. It may not be even worth the trouble since the different groups are probably working on different software anyway.
While it makes sense to follow some unified standard as a team, it makes much less sense across teams. With few exceptions, there is no "grand scale".
Languages have a LOT of ecosystem demands that frameworks and libraries do not. DSLs (especially embedded ones) save on those demands and do get written all the time. That said, I'm of the belief that they should get written more, still.
I'm sure it's more my poor choice of word, sorry about that. I just meant that if you're starting from scratch on a language there's a fairly large amount of non-useful stuff that must be done before it becomes valuable.
I'm basically assuming that the only reason why you'd have enough business value in creating a new language comes from it either modeling something interesting or having a different semantic focus. Everything else is just wasted time then.
Some examples:
1. Documentation. How do people know what your language means? How do they know what happens when it breaks?
2. Shaking out edge cases (much harder to do than debugging a program!)
3. Syntax. Picking it. Parsing it. Ensuring it's complete and doesn't have weird edge cases.
All of these are fun in their own right, but most businesses would have a hard time arguing that they're worth working on unless they've already got a language tied into their core value proposition. It's also important to mention that there exist tools to make some of them easier (bnfc, llvm, &c).
But if you just start with an embedded DSL you can get right to the modeling or semantic issues by piggy-backing on the host language for (most of) 2, 3, 4, 5, 6, 7, and 8. If you prove that there's something valuable there and the host language is slowing you down then you can start to unembed it by building the "whole ecosystem".
We don't have to chose between embedded DSL (meaning, a kind of fancy library), and a fully fledged external DSL. I was advocating source-to-source transformation, which I think is a nice compromise between the two: it feels almost like an external DSL, but doesn't require much more work than an internal one.
The way I see it, source-to-source transformation helps a lot with 2, 4, 5, and 7: we can rely on the host language for these. Actually, 5 is even better than an embedded DSL: you can implement additional checks. As for 6 and 8, you will generally avoid the issue altogether by letting your host language deal with them.
An example of source-to-source transformation at work is OMeta. The thing compiles itself to JavaScript, and its source code takes about 400 lines of JavaScript and OMeta. I'm currently working on a clone for Lua, and my prototypes so far are under 300 lines.
I agree that picking up a syntax is hard (parsing it is trivial). But with enough feedback, you can make a decent one. Just make sure to tell everyone that the first 5-10 versions of your language are all pre-alpha, and totally incompatible with each other. And source to source transformation isn't about building a whole new language. Most of the time, it is about extending the host language. This means much less syntax to worry about in the first place.
Actually, if you're smart about it, you can often write most of the semantics of your language in a library, then add a light layer of syntax sugar on top of it. That's what I did when implementing a PEG syntax for Haskell[1]. It's just syntax sugar over Parsec.
Now back to my Java example: It was just a matter of adding a nice syntax sugar over anonymous inner classes, and maybe allow a syntax for calling objects as if they were functions, C++ `operator()` style (Even so, writing obj.app(args) isn't that cumbersome). Not much work, really.
Source-to-source is an interesting example. I think Coffeescript has proven it's pretty possible to get a long way there, though it's undoubtable that it still has some syntactic and semantic design issues.
I think another important example of source-to-source is Lisp macros. Super powerful and when used well and marketed well they can really transform what Lisp means. I also think that when marketed poorly they lead to an explosion of competing "languages".
My experience is that popularity does matter a lot, but that the value of popularity is logistic rather than e.g. linear. Not enough popularity does make using a language in production unrealistic, but there is a threshold of usability after which increased popularity seems to have rapidly diminishing returns. I would argue that Haskell passed this threshold a couple years ago for most use cases. There is still a lot of work to do on cabal and some of the core libraries, but we're happily using Haskell in production, and for us the benefits very clearly outweigh the occasional difficulties.
As the executive sponsor for this effort, I'd like to point out that we considered a wide variety of options. Java (and other JVM languages) as well as node.js, ruby, python, C++, and others. We also already had in-house Erlang experience, but chose not to go with that for web services for various reasons.
Haskell really is special, in that it represents a very strong point very far out on the "statically typed" spectrum. Among the options, it had the best bang for the buck. Erlang was also functional, but not statically typed. C++ was statically typed, but not functional. The others were largely not sufficiently better than our existing PHP solution, and/or were beat out by Haskell in the consideration.
It's also important to understand that Haskell is not replacing PHP; it's augmenting PHP. We have many millions of lines of PHP running, serving many millions of active users, and there would be very little value in porting the "long tail" of that business logic. Meanwhile, there is huge value to be un-locked in gaining tight error checking and great performance on the "important core."
I don't think you need orders of magnitude improvements to get a large adopted base [Note: I'm specifically not saying "rate"]. Just wait for new projects (or new companies) who're looking for which one is just marginally a better/funner/cooler choice over the rest. They'll accrue credibility for the language, and that'll add to it's case for usage in organizations that have already selected languages, and are looking for new ones. I think an organization that's been paying for a prior PHP choice would be quite happy to jump into Haskell, but is just waiting for a little validation of their choice with some more success stories.
And that's not really a function of metrics, it just needs a few well-spoken cheerleaders with a few good stories.
The largest issue Haskell has with adoption is its intentional avoidance of (at least, mainstream) cargo-cult programming culture. The "avoid success at all costs" attitude makes cheerleading Haskell as a proper, valid selection for betting your project less cool. The best you can do is "look, I got away with using Haskell!"
I think it's fine to cheerlead for Haskell, as we're all happy to increase the size of the community. What we don't want is for the community values to be diluted by that size increase, which is the sense in which "avoid success at all costs" seems to be meant. For example, I'm always happy to help someone understand monads, but I would be very unhappy if that involved renaming them to "workflows".
The notion that terms like "monad" are nothing but shibboleths is very common and completely misguided. Instead, these terms are used for a couple of very good reasons.
First, the simple reason that they are accurate terms for which there are no good replacements. The English language does not have the necessary scope to cover the relevant abstractions. This leaves you with two options: use the accurate term from mathematics, or make up some new term. Either way the term will be something new to most people, and making something up does not provide any value.
This leads me to the second reason: the mathematical terms actually do provide additional value. If you are working with monoids and do some Googling, you can discover a wealth of information that is directly applicable in practice, for example you can learn that List is the initial monoid [1]. You don't have to learn any of this, it's completely optional, but if you do choose to explore it you can get real benefits. Renaming monads "workflows" would prevent you from gaining those benefits, and would not get you anything in exchange other than making the people who are scared of math more comfortable.
I think you're right, and I think Haskell delivers on your definition of "better." I chose to try Haskell because I've been through a lot of agonizing maintenance work and I'd much rather do it in an environment that has mandatory, pervasive static analysis and tests that are by default isolated from all possible nondeterminism.
We don't have any 8-year-old Haskell yet, so I can't fairly compare it to refactoring our PHP codebase, but I have made a few sweeping changes to our 60k lines of Haskell over the past year and a half. It tends to be mostly effortless, no matter how much code needs adjusting.
> It requires changing the runtime library and possibly re-writing a lot of in-house infrastructure.
I think this is a bit overstated. (See below.)
> BTW, I think that if a company does want to try a good, though not-so-popular language, it should pick a JVM language, as the interoperability among JVM languages, and the availability of libraries that can be shared among the popular and less-so JVM languages, reduces much of the risk.
For the most part I think this advantage of JVM languages goes away if you design your system as loosely coupled services. Amazon is the canonical example of this, but I've worked in places where C++ and Java were used together easily because of this kind of design.
Also, I'll echo some of the others and say that Haskell has pretty good adoption and a fantastically helpful community. Three or four years ago I would have agreed with you, but the community has come a long way in the past few years.
I'm profoundly puzzled by your statement. Most of the really high quality developers I've seen have at least poked Haskell - the really lousy developers don't even touch it.
One way to phrase my objection is that popularity is a really good measure of how many really bad developers exist for a language. I am not interested in being replacable by a really bad developer, working with them, or hiring them.
Haskell (and a few other languages) occupy a very nice space where entire classes of errors are wiped out by language design (null pointers); other classes are strongly limited by the type inferencing. This bonus in particular scales non-linearly with the LoC of your codebase due to the interaction of multiple components of the codebase all having to be type safe.
Always use the best tool for the job. As long as it is compiled, statically typed, runs on the JVM, and blends functional and OO programming seamlessly.
I think you are vastly overstating things. You need an acceptable level of adoption. One where there are high quality libraries for common needs. One where you can hire experts. Beyond that level, increasing popularity doesn't really get you anything. Haskell is already beyond that level.
Haskell could still benefit from a larger community. We have had to write some key low level tech, but we have been able to do so at an incredible pace.
The /dev/urandom bug mentioned was caught by us because we are one of the few places using Haskell that also has a fairly large user base. If the community was larger we would have run into less issues.
That said, we have run into a surprisingly few amount of issues :)
>If the community was larger we would have run into less issues.
I'm not so sure that's the case. For example, debian's user base is vastly larger than openbsd's, yet debian has bugs like "all the keys you ever generated are garbage" and openbsd doesn't. The "many eyes" hypothesis is bogus. Bugs are found and fixed more through deliberate effort than just large numbers of people.
For a company, overall language adoption, availability of libraries, tools and talent (and I'm not talking about training someone to be productive; sooner or later you need real experts and training an expert is expensive in any language) are extremely important. That's why when choosing between two languages, assuming both are well suited to the job at hand, a company should always pick the one with the wider adoption unless the other is so significantly "better" to trump lesser adoption. And the smaller the adoption, the "better" the language needs to be.
There's no doubt Haskell is an excellent programming language. I'm learning it and enjoying it. But because it has such low adoption rates, it needs to provide benefits that are orders of magnitude higher than other, more popular languages. I guess that for some very particular use cases it does, but I just can't see it in the vast majority of cases.
Hobbyists can afford playing with different languages, and can (and perhaps should) jump from one to the next. Companies can't (or most certainly shouldn't, unless for toy projects); they should pick (wisely) a popular language that's right for them, and just stick with it.
BTW, I think that if a company does want to try a good, though not-so-popular language, it should pick a JVM language, as the interoperability among JVM languages, and the availability of libraries that can be shared among the popular and less-so JVM languages, reduces much of the risk.