Why hasn't Haskell taken over the world? And the curious case of Go
pchiusano.github.io
pchiusano.github.io
Eventually I arrived at Haskell after strongly considering Clojure, and I'm very happy about what I've learned and my new way of approaching programming complexity. Unfortunately there is nowhere else to go from here since I can't find employment as a Haskell programmer. OCaml, F#, Scala, Elixir, and Elm all feel like a step back. Now I'm a Java programmer and quite miserable. I feel hampered by the language nearly everyday in terms of how easily I can express my thoughts in code. Haskell isn't perfect but is the best fit for my mathematical mindset.
I am trying to lead by example. I help host my city's Haskell meetup and contribute to a Haskell reading group. The path is lonely, my co-workers poke fun at me, and don't care to put in any effort to understand what I have to say. I love teaching and explaining things but there is zero interest because the machine keeps moving. Not many at the office even enjoy Java but are resigned to do it for our large pool of enterprise clients. All in all each work day is a void I put 8 hours into, which is fine, compared to most working conditions; it causes no suffering beyond the lacuna.
I'm past the stage of trying to convince other programmers anything. I recognize many are happy with their tools. I yearn for that happiness and don't seek to spread my misery. I offer my time to those who are interested and want to learn more.
Anyway, this article nails it, Haskell has advantages but they aren't enough to change things without the infrastructure the author is building. I look forward to being an early adapter of Unision and continue to remain hopeful for the future despite the long odds.
- How does Haskell fare with large projects that use APIs that are inherently stateful, like OpenGL? Don't things get messy and ugly as the pure world of Haskell is being tainted?
- How do I optimise Haskell code without having studied the language for decades? I had a lecture where we were taught Haskell, so I know the language, but darn, even a simple hash map seems to be so very complex in the language. The fact that everything is a linked list (bad for caching & performance) and everything gets copied around really turns me off.
Haskell is excellent at handling state. "Pure" doesn't mean no state, it means all state is handled explicitly in a type-safe way.
> How do I optimise Haskell code without having studied the language for decades?
It requires learning some new things, but honestly when I worked at a company that used Haskell I didn't really have to worry about this much. You occasionally make some things strict and there are some good heuristics about when this is needed, but overall it just didn't come up often.
> I had a lecture where we were taught Haskell, so I know the language
No you don't. You can learn Python in a lecture if you know Ruby; you can't learn Haskell in a lecture unless you already knew SML or OCaml, and even then probably not.
> The fact that everything is a linked list (bad for caching & performance) and everything gets copied around really turns me off.
Everything isn't a linked list in production code. It's easy to swap lists out for vectors wherever it's correct to do so, because you can write almost all of your code generalized to the necessary type class rather than specific to one single container type. Real Haskell code performs well, poorly-performing code is just used for examples when teaching beginners because it's easier and introduces fewer things at once.
I just want to emphasize this part because it's really quite true, as someone who came to it from F#/OCaml.
The thing with learning "haskell", is that there's faar more to it than learning the basic language constructs. You can learn haskell the language in about a day, but you can't really learn haskell the paradigm/philosophy/mathematical discipline in a day, much less actually program haskell that quickly.
That being said, it's not as difficult as it's made out to be, the mountain you need to climb is much shorter than people realize. The issue is largely one of jargon, and getting used to using and thinking in all the new terms and concepts that really have few equivalents in other languages.
That would be a compelling reason as to why it's not popular. This basically means your programming experience is not transferable, leaving it as a weird side language that some people stumble into due to uncommon factors.
Saying the experience is "not transferable" is looking at it the wrong way. Learning the discipline behind haskell isn't even remotely the same kind of beast as learning how to use a niche legacy framework in COBOL for example.
It's a lot more akin to learning to read human language for the first time, because all it's doing is familiarizing you with patterns that exist in code and computation, independent of Haskell. The actual language part of the haskell equation is literally the least relevant/significant, because what it teaches you is to recognize mathematical/computational patterns that you can find in any language, despite the difficulty of expressing some of those concepts succinctly in some languages (e.g. java).
The biggest thing that's 'not transferable' is the ability to communicate the high level patterns to others who haven't gained literacy with that particular branch of mathematics/CS yet. But that's true of all sciences and mathematics. You can call it a "weird side language", but that's kinda doing it a disservice as a language that's intended to express complex relationships and computational patterns in the most general way possible.
The word "pure" gets thrown around with regards to haskell, but most of its draw and apparent purity, isn't the same as the technical definition of it being a "pure" functional functional language.
This "pure" functional aspect is more of a quirk that you get used to working with, rather than a noticeable feature.
I would argue that the seeming 'purity' most haskellers love about the language, has to do with it's ability to cleanly and flexibly model domains without fear of refactoring, or worrying about fitting them to strict opinionated frameworks. This is because the language is flexible enough to let you really model your thoughts however you want, regardless of what they'll be plugging into. Meaning that you can focus 'purely' on the business logic.
So no, the 'purity' shouldn't be affected very much in large projects.
> The fact that everything is a linked list (bad for caching & performance) and everything gets copied around really turns me off.
It sounds more like you're inadvertently looking to shoehorn your existing mental model of how "performant code should work" into Haskell, rather than looking into how "performance with haskell" is achieved. Haskell works on a fairly different paradigm, so you can't just make assumptions about it like that. For example, hashmaps are largely unnecessary in Haskell, and you find out why the more you use it. The linked lists in haskell are also not much of a concern, because they're already highly optimized, due to the fact that they have to handle being infinitely large, not to mention that there are other data structure readily available if needed. Overall, haskell is already quite performant,and you can turn it's laziness features off wherever you want if you really have a hard time reasoning about is lazy evaluation performance anyway.
I use Haskell quite a bit and I don't know what you would use instead of a strict hashmap for something like O(1) lookups in an application to correct spelling. Do you?
The pure world of Haskell does not get tainted. Monads help manage the inherent messiness better. As for your question, perhaps this glut example I quickly found and skimmed will help:
http://www.arcadianvisions.com/blog/2011/modern-opengl-with-...
- How do I optimise Haskell code without having studied the language for decades?
Optimizing is where I've been having trouble with lately. You need to understand laziness, inlining, and reading basic core imo.
For understanding laziness, read a few tutorials. Then use the ghci debugger as described in the ghc manual. Play around by adding BangPatterns based on educated guesses from any intuition you formed on how laziness works.
For understanding basic optimization (Space and time):
http://book.realworldhaskell.org/read/profiling-and-optimiza...
For understanding core, read/watch in this order:
http://www.haskellforall.com/2012/10/hello-core.html
http://blog.ezyang.com/2011/04/tracing-the-compilation-of-he...
https://skillsmatter.com/skillscasts/6495-keynote-from-simon... (go back over after watching above)
http://www.stephendiehl.com/posts/ghc_03.html
Try generating core for some Haskell code you want to know about. Tie what you've learned to core generated from Haskell code that solves one of your real world problems.
https://ghc.haskell.org/trac/ghc/ticket/11710
Another good tip is to use the ghc option `-ddump-rule-firings` to see if code gets inlined or otherwise optimized.
Keep fighting the good fight! I'm happy to hear that you took the initiative to host meetups and participate in reading groups.
I think for many folks Haskell is too big of a gulp all at once. It's not that it's too hard (I think that's a bit of a myth). It's that it's just too different. It's hard for an experienced Java programmer to go from being highly productive to seriously struggling to even solve FizzBuzz in Haskell, let alone dealing with lazy IO and various terms and concepts that don't show up anywhere else like monad transformer stacks.
In my experience, this is where languages like Elm come in and are extremely valuable. This is also why I think Elm is much more important than PureScript. I've recently had a lot of success advocating for and using Elm for an internal tool at work. We've now got several thousand lines of pure Elm code, and for the most part everybody has been incredibly impressed with the overall Elm development experience. I'll also say that the tool ended up being a lot more powerful and feature-rich than originally planned because Elm made it so easy to keep expanding and improving the application.
Java -> Elm -> Haskell/PureScript is a much more enjoyable path for many than Java -> Haskell. It also feels a lot more motivated. Use Elm for a while, and you see many of the strengths of Haskell, but you also see many of the weaknesses of Elm. Once you've seen those weaknesses, you'll be happy to find that Haskell solves most of them. Now you have a concrete reason to look at Haskell, and that can make a big difference.
noredink's json-decode-pipeline [1] seems better than what Elm offers out of the box, but it still feels like a lot of ceremony for something mundane. People building webapps [2] to help users generate boilerplate code seems like a hint something's wrong, isn't it?
A quick Googling suggests Data.Aeson [1] is the popular library in Haskell land. Does it rank better on what I was hoping for (succinctness, ease of use)? If yes, would it be reachable within Elm, or are some Haskell constructs missing in Elm to achieve a similar experience?
[1] https://hackage.haskell.org/package/aeson-0.6.1.0/docs/Data-...
https://github.com/codygman/concise-json-parsing-in-haskell/...
Please give feedback if you don't mind. I'd also like to wait a bit before officially sharing with wider audiences then this comment gets.
Keep in mind that part one is "haskell/lens as explicit as possible" which is why I suggested looking at part two to draw conclusions.
Edit: just looked at elm decoder pipeline and it looks very similar to aeson.
The key difference with Aeson is this line:
> deriving (Generic, Show)
Because of this, it's not necessary in that example to even write the JSON decoder manually. The compiler can generate it on its own. I don't personally know of any way to achieve this in Elm, as Elm doesn't have typeclasses and what's effectively happening here is that an instance of the FromJSON typeclass is being computed by the compiler.
A very similar trick is possible in PureScript:
Also checkout eta since it's Haskell but interops with the JVM:
https://github.com/typelead/eta
You are a haskeller writing Java, I'm a haskeller writing Go ;)
But it would also be a "step up". You would feel better than with Java and you would have an easier adoption than Haskell. And in a couple years, you would add a Haskell dependency to them. And then a bit more. And in a decade you will have help clean the mess we are in a bit.
Have a goal. Try to find a path to reach it. Look at how to improve things :)
I know for a fact that both the Elm and Elixir and Rust community would be really happy to help both the transition and evolving the tooling if needed. Come talk to us. Try to make your life a bit better :)
(I do not know well the OCaml community, but i bet you would also find helpful people there)
I tried learning about Haskell just so I could understand a flurry of articles I came across that were trying to explain monads. But mostly I found super abstract notation, something about bind and unit and >>=, and I came away just feeling dumb. I gave up pretty quickly, but to be fair I suppose I wasn't super motivated to work through it.
Later, I learned Rust just for fun. Options? Sounds like a good way to express uncertainty about a function call. Oh, and you can compose it with other Options using this function called and_then(), and things will short-circuit if they fail. Neat!
A year later I find out I had been productively using monads the whole time without even knowing it. It's so frustrating knowing how easy it could have been to learn this concept explicitly the first time around.
From a language theory perspective, there's a lot to criticize about Go. The "goroutine" and "channel" thing turned out to be less useful for general concurrency than expected. But it's good enough to service lots of network connections. The lack of generics means that functions like "sort" are painful. But you can still get sorting done; it's just clunky.
Haskell hasn't taken over the world because it's designed by theorists for theorists. It's too clever, or, "l33t", like LISP. Also, anything interactive is dominated by I/O, while the functional model is better matched to pure computation. The IT industry today is mostly I/O dominated interactive applications.
(It's not clear yet if Rust is "too clever" for widespread use. The jury is still out.)
Actually, seeing all those panic on null pointers was really my first surprise when i started coding in go ( and really first disappointment).
A null pointer indicates a memory problem only if it is the field of a memory object that was wrongly overwritten with zeros, giving rise to the null value in an invalid way. If the null value was correctly assigned, then it isn't a problem in the memory safety category.
If Go diagnoses all null pointers with panics, that makes it safer than C, whose implementations require on the virtual memory hardware to catch null pointers. An expression like ptr->member in C will onlyl segfault on a null ptr if the member displacement is within 4096 bytes, or whatever is the virtual memory page size. If memb is offset beyond the unmapped zero page, then some memory is silently accessed or overwritten. Not to mention that on systems without VM hardware, ptr->member just silently works when ptr is null, for any struct type.
I don't program in Go, but as far as I know, panic is a kind of high level exception handling feature in Go. So it sounds like you should be able to write Go code which expects that null pointers will occur in some object field or parameters, and just use panic/recover to trap it all in one place instead. In C, you would need platform-specific coding to do anything similar, like catching a SIGSEGV in POSIX or a structured exception in Win32. Yuck!
I am still working on explaining it well myself, but i am working on trying to write blogpost about it.
My main problem with CSP concurrency is simple : synchronisation. That does not work in face of possible errors. Period. The only way to make it work without breaking the laws of physics so much is to bet that there is no hardware errors and that everything stay single threaded.
Generally, look at the body of work against RPC and about consensus in distributed systems. Because your computer itself is a distributed system. It just happened that it hide it well for decades, but not anymore. We are even seeing timing problem between cores in some chip.
Without backing/promotion from Google, Go would be completely obscure and disused.
Google could promote a language that is a complete piece of crap and it would be instantly more successful than something well-engineered from some unknown hackers.
I would rather thank Google for letting their engineers use it and develop it than cast unproven aspersions.
Your counterexamples speak nothing to the validity of the proposition "if Google promotes something, it will likely be successful".
They are counterexamples to the ridiculous proposition "If, and only if, Google promotes something, it will be successful".
I wish Haskell "advocates" would give it a rest with banging the "purity" drum and tossing out gee-whiz examples of "elegant" code with no relevance to practical programming. I found it to be a very practical language, not without flaws, but also with powerful arguments in its favor for certain sorts of applications.
More focus needs to be on reasoning about real world tasks where is needed though.
That said there are a lot of tasks that I use Ruby for (text wrangling), Python (machine learning), and Java (huge number of useful libraries).
I use Haskell for NLP, some web apps, and coding algorithms that don't require libraries that aren't available for Haskell. If Haskell had a huge set of 3rd party libraries like Java, then I would probably use it for most of my development.
i.e. when browsing and trying to understand parts of the XMonad code, I came across the "Endo" type. I Hoogle'd it, and the only documentation for it is "newtype Endo a: The monoid of endomorphisms under composition." Thats.... not helpful for me.
-- Endo is…
newtype Endo a
= Endo { appEndo :: a -> a }
-- ^
-- |
-- …the monoid of endomorphisms…
-- |
-- V
instance Monoid (Endo a) where
mappend (Endo f) (Endo g)
= Endo (f . g)
-- ^
-- |
-- …under composition.
mempty = Endo id
But not so much about how to actually use it: -- If you’ve got a list of functions…
pipeline :: [Int -> Int]
pipeline = [(+ 3), (* 2), abs]
-- …you can compose them with the generic mconcat.
run :: [a -> a] -> a -> a
run = appEndo . mconcat . map Endo
run pipeline (-1) == 5---
Reply to montatonic here:
I'm neither a Haskeller nor do I want to see Haskell “take over” the world. Some of my comments even got lots of downvotes from Haskellers.
---
Reply to sidlls here:
Yes, FWIW, I don't really agree with the use of the term “endomorphism”. It isn't even precise: the type `Endo` is inhabited by endomorphisms is one specific category (Hask), not endomorphisms in arbitrary categories. So “endofunction” would be both more accurate and less pompous.
As for “codomain”, I can't agree, though. This is really high school stuff.
Neophyte: Hello, I do not understand this.
Haskeller: You should already know this.
twice :: Integer -> Integer
twice = (2*)
Here, `twice`'s range contains just the even integers.As a haskeller myself I wholeheartedly disagree. I knew nothing of function composition until Haskell. I only heard about functions in high school once my senior year and it was very basic. Admittedly I went to high school in rural Texas.
I also never heard about sets, the word I was never used in my high school math classes.
> this is stuff that every high school graduate should know.
You seem to have a lot of faith in the lowest common denominator of high school graduates and I think it does you a disservice in being able to help others learn these topics.
I sense my last paragraph might have sounded a bit... Aggressive? That's not my intention.
P.S. I still don't really know what domain and codomain mean :)
If the material is taught in high school, then educators must have decided that high schoolers can learn it, right?
> I still don't really know what domain and codomain mean :)
someFunction :: Domain -> CodomainDid you miss that I said it wasn't taught in my school?
https://www.youtube.com/watch?v=I8LbkfSSR58 https://bartoszmilewski.com/2014/10/28/category-theory-for-p...
For example, the poster you replied to can only see the advantage being that his question is answered. The tangible benefit for taking time out of the day to go and read those foundations needs to be made more clear I think.
y1 = f_1 x
y = f_2 y1
-- or,
y = f_2 (f_1 x)
But what if you have an arbitrary list of functions as your pipeline? y = f_n (f_{n-1} (... (f_2 (f_1 x)) ...))
Well, the solution is that functions of type `a -> a` are composeable, so you can 'add them up' as if they were real values (which they are). The Haskell function composition operator is `.`, so: y = f_2 (f_1 x)
-- is the same as,
y = (f_2 . f_1) x
Now, how do you go from 'composing two functions' to 'composing a list of functions'? Well, monoids are a typeclass (a statically-enforceable design pattern) that encode the idea of being able to combine two things into one, and 'for free' give you a function `mconcat` that combines a list of those things into one. So if you have a monoid instance for functions of type `a -> a`, you're now able to combine many of them together, get a single composed function, and apply your input value `x` to that.`Endo` is just a fancy name for 'function of type `a -> a`'. I agree to a large extent that all this category theory stuff can get distracting. I personally think type theory is a much more rewarding field of study. To 'endomorphism', a type theorist would say, 'Oh, you mean `a -> a`?'
(I know "+1" posts are a sin, but if a few more haskellers take heed of the documentation problem, it'll be worth me getting hellbanned.)
This module is inspired by the following paper: <DOI>If you aren't planning on submitting that to those docs I will. In fact I'm sure many would find you adding examples along those lines to the most popular but math inspired Haskell libraries.
-- Using Elm syntax
pipeline : List (Int -> Int)
pipeline = [(\x -> x + 3), (\x -> x * 2), abs]
applyPipeline : Int -> Int
applyPipeline value = List.foldr (\func val -> func val) value pipeline
-- or more succinctly
applyPipeline_ value = List.foldr (<|) value pipeline
Getting lost in the mathematical abstractions is really no help here. In niche circumstances I'm sure it's great, but like.... not here.
The language itself doesn't demand we write code this way, but the community, understandably, loves to be as expressive as possible in many cases.
But all of this comes with a cost of cognitive overhead that seems to rarely be worth paying.
Elm was an enlightening experience for me, having started with Haskell, because it helped me to realize just how little I ever took advantage of a lot of the very generic Haskell code, and how far the simplest of functions and types could take you in the overwhelming majority of cases.
To quote from Paul's post: "The trouble is that very often, the sorts of examples that are easy to discuss aren’t of sufficient scale to reveal any major differences between A and B."
---
Reply to sidlls here:
I didn't say that the term might sound scary to you. I said that the term might sound scary in general, to “average” people. Because, frankly, relative to its actual mathematical content, the term is perhaps a little bit too pompous.
Also, when it comes to mathematical definitions, I wouldn't say one really knows a definition unless one can use it in a calculational setting.
(terse && correct) != helpful
The art in writing documentation is in effective communication to a varied target audience, not in simply fulfilling the obligation to be technically correct.However:
(0) As for “endomorphism”, the definition “function with the same domain and codomain” should be pretty easy to understand to anyone. (Technically, in an arbitrary category, morphisms don't have to be functions, but Haskellers work in the category Hask, whose morphisms are terminating functions.)
(1) The right place to define what a monoid is is the definition of the class Monoid, not the definition of every type that happens to have a Monoid instance.
This is my favourite haskellism so far in this thread.
I learned the jargon as part of my academic background. That was part of the reason I made the comment. These terms are jargon, or terms-of-art. Knowing jargon isn't the same as knowing the things jargon describes or having the capacity to reason about them well. Similarly, not knowing the jargon does not imply ignorance, stupidity, fear, or any of a number of other negative things.
But you can have the bulk of these wins in a simpler language like Elm, or a language like Rust which tries to only add more type complexity in cases where it would significantly improve code in practice, rather than in theory.
Haskell is necessary; it pushes the state of the art forward because of how experimental it is (compiler extensions). But it's pretty clear that the community is skewed towards research rather than industry, and culture strongly influences what a language is practically capable of.
This does not at all mean that Haskell isn't fully capable of industrial applications; we have direct evidence suggesting otherwise. But with a philosophy to "avoid success at all costs", it should be fairly clear that moving out of the realm of obscurity isn't exactly a goal of the language.
Actually, jumping back into mathematics outside of work, I'm having to go through this period of uncomfortable 'badness' and resisting my urges for the greater good. It is emotionally taxing to be 'bad' at something. I think that's part of why we favour languages that allow us to be productive quickly, despite it probably paying off in the long run.
I wouldn't do it with everything (you need quick wins _somewhere_) but it's a good skill to have, to be disciplined and persevere despite what your heads saying.
I'm no psychologist though, so this is largely conjecture and anecdotes.
This is like saying that the history of architecture has been a series of advancements in reducing the cost of structures. This is the fallacy of narrow focus a single sexy metric. (See below.)
Stage 4: Composability is destroyed at program boundaries, therefore extend these boundaries outward, until all the computational resources of civilization are joined in a single planetary-scale computer
Smalltalk tried to do this with the Image, which contained everything within it, including compiler, source control, and development environment. (Smalltalk started out as an Operating System.) However, what this actually did was to sequester the community in the boundaries of the image while the rest of the programming field was busy building varied forms of infrastructure to allow interaction.
I think this hypothesis also offers an explanation for why Go is popular, even though the language is “boring” and could have been designed in the 1970s.
Java won over Smalltalk because the Java community understood much better how to win mindshare. Ruby took over Smalltalk's pure object schtick because it could play nice with the rest of the programming world, and it had a killer app in Rails.
Go wins because it is exceedingly well designed for convenience in a myriad of ways where other languages drop the ball. You can have very fast incremental compilation in C++, if you are careful. In Go, it just works. You can have a good deployment process with Java, if you are careful. In Go, it just works. You can have easy to use concurrency in other languages, like C#, provided you find and learn the correct libraries. In Go, it's baked into the language, and people can learn how to use it competently with about as much effort as learning how to implement FSM using switch{}, and very good documentation and tutorials are dead easy to find.
A Lamborghini is way faster than a Corolla. A Ford F300 can haul many times more groceries. A Corolla doesn't have remotely near the potential for track day fun as an Ariel Atom. The above 3 qualities are sexy metrics, but in terms of overall utility and convenience for the largest number of people, for the most favorable cost/benefit, the boring old Corolla kicks butt. (That said, I'm pretty sure that someone can figure out how to disrupt programming in the way that companies like Tesla are poised to disrupt the above story. It might well be a "boring" version of functional programming that has many "just works" qualities.)
As for the question of why Haskell isn't more popular, there are a lot of possible reasons. GC overhead and less control over memory layout makes it less performant than C/C++ and not really suitable for real time tasks. Some algorithms are hard to express without mutation. (The ST Monad is usually appropriate for those cases, but it's kind of cumbersome to use.) Laziness makes memory and CPU utilization hard to reason about. The learning curve is very steep. There aren't many books on "advanced" Haskell programming, or writing system software in Haskell.
One of the things I really like about Haskell is that you never stop learning new things, and new abstractions and libraries are being invented all the time. After 25 years or so, we're still figuring out new ways to program in Haskell. This has a couple of unfortunate side effects, though. One is that programmers can become distracted from the task at hand and direct all their effort at figuring out a better abstraction to solve their problem. A lot of great things come out of such work and sometimes it pays off it terms of productivity, but I think the common criticism that there isn't enough practical, "boring" software being written in Haskell is justified. Another problem is that it's hard for Haskell programmers to communicate with each other or understand each other's code when they haven't learned or are comfortable with exactly the same set of abstractions.
I do think Haskell is on to something good. I don't expect Haskell to take over the world, but I think in a hundred years people will look back and say that ideas from Haskell were a major influence on later languages that were more popular. Learning Haskell is probably a good way to prepare for languages of the future that haven't been invented yet, in the same way that learning Smalltalk would have been a good way to prepare for the object-oriented languages that came after.
I went down the Go path myself for a while, because it was familiar, but I found that it had many of the same issues I was trying to get away from in other languages. As the article points out, I also found Go "boring."
After searching for a while, I ended up wanting to use Haskell, but it was taking me too long to become proficient with Haskell's unfamiliar syntax and functionality; which is why, the author posits, Haskell hasn't taken over the world. I ended up using Elixir instead. It offered me the functional paradigm I was looking for in Haskell, and the ease of managing large-scale applications, while also offering a familiar and easy to learn syntax. I'm still studying Haskell, and may eventually employ it professionally, but for now, Elixir is working for me just fine.
I've tried learning them several times, but mostly stop after finishing the basic syntax :/
I am not using Haskell in my day to day job, yet, but I am using Elixir, and have been since last April.
For learning Elixir, I highly recommend Dave Thomas' book Programming Elixir, The Little Elixir & OTP Book, by Benjamin Tan Wei Hao, and the website https://www.learnelixir.tv. You may want to start with the website and jump into the books afterwards. I learned with the two books, but one of my co-workers started with the website, and said he found it quite helpful. I watched the tutorial videos after the fact, and they are quite good.
The website https://www.dailydrip.com also has a lot of Elixir tutorials (similar to Avdi Grimm's website, Ruby Tapas), but you have to be careful because the older videos contain descriptions of features that are deprecated. I wouldn't bother with Daily Drip until you have a solid foundation in Elixir, and can pick around the deprecated bits. The later videos on Daily Drip are up-to-date though.
Similar experience for me. You can't read Haskell without learning Haskell.
I don't write large complex applications though, thus far I've used it to write programs that fit into Go's sweet spot -- high performance network daemons that do a lot of I/O.
Go developers already have that "dumb-down" reputation.
Programming abstraction is elevating upwards. Language will play a less instrumental role. Apis are taking over. Ai is becoming a fundamental building block of any non trivial application. What you said could happen, but they are more possible to become even less relevant.
I hope Go does not "evolve" to support generics (whatever that is) because me and thousands of others have written Go code that produced value to many without it.
And that's one data structure. We've got thousands of developers, many of whom are now writing Go. This comes up all the time and is not a niche use case.
At this scale it's quite costly.
IMO slapping on even basic Java / C# style generics does not harm you and if anything lets you optionally choose to use higher order functions or novel data structures you can't implement today without reflection.
Edit: I have never written code that required me to copy and paste or generate code.
That's a date range based use case for this interval tree structure.
Another team is doing something similar query-wise, but with integer based ranges. I forget what their use case is, but our data structure is exactly what they need, but we either need to use reflection (unacceptable) or copy and paste (also garbage) or code generation (bad) to accept and return the right types.
This is trivial in Java, where I have a version of this written and used without complaint.
If so (probably not), would that mean that the lack of generics in Go influence good design from the beginning?
Edit: Thank you for the use case!
No.
Even if you represent date ranges as pairs of integer timestamps in UTC (for consistency) you now need to do annoying and possibly bug prone casting and converting. Remember, code not written is bug free. Those tests won't write themselves. Also think of N dimensions; those come up often and are hard to handle with this input type.
The moment someone needs a pair of GPS coordinates that are doubles you're SOL. Which incidentally is a common situation with interval trees: find things in a viewport, intersecting roads, etc.
Even the tree itself is actually generic. You could build on a red black tree and using generics use an internal data type to order the tree with some extra book keeping and thus use the same underlying tree implementation for, say, an ordered set structure, interval trees, etc.
You can do this all now with error prone casting. I don't know if you've done Java pre generics but it is a vastly more error prone world than post generics Java.
Incidentally the lack of these other structures leads to lots of OrderedXYZTypeSet type code in our codebase that are generated to avoid cssting.
With generics we'd not only have one implementation for all types, but we'd likely open source it since the broader Go community could use it and add additional useful types and functions.
Go has very few built in data structures which exacerbates this problem. It at least partially has so few because the generics support in maps and slices etc. is basically a special cased hack since it's not baked into the language and is thus likely hard to maintain.
A generic map, reduce, etc. are what I need. Across the thousands of developers in our organization I'll bet we've implemented "mapString" or "mapObjectThing" dozens of not hundreds of times.
At least we could make a package for non built-ins.
Also lots of interesting generic data types we need across our org that are a pain.
With Java we have some data structure packages that Just Work and don't require copying and pasting.
I dislike Go because it's resulting programs are not boring. So many things can and do panic.
Perhaps a more reasonable question is why F# isn't eating C#'s lunch.
Programmers have impure toughs.
I'm not the greatest Haskell programmer, but I love it. I recommend learning the basics of Haskell, if you haven't yet. Doing so improved my code in other languages quite a bit, so it was worth studying for that reason alone.
The big disconnect between fp/haskell devotees and other programmers is that everyone else doesn't consider it self evident that haskell or fp is better and the haskell and fp group does a terrible job explaining why they are better paradigms without making assumptions like 'all side effects are bad in all circumstances' or 'imperative programing is always worse way to impliment algorithms' without justifying them empirically and saying your paradim is better without justifying it empirically.
Functional languages have to be penetrable to newcomers and offer huge benefits to veterans to switch, or they're going to remain nearly esoteric. Is mathematically provable code nice? Sure is. Is that enough to win people over? Well, language adoption says otherwise.
How often do we need to do those things in a specific dependency order? How often do we need to simply get those things done, regardless of order?
And these days languages compete on a lot of things, on performance, reliability, security, productivity, on syntax, on tooling, on libraries, on deployment and supporting infrastructure, on interoperability. Syntax is not even that big of a deal, it's not hard to compete on it, you can just take Go's approach or you can do it thoroughly and properly, not forgetting about newcomers, their likely previous experience and designing the whole learning experience into the language too. You can't leave it as is though or it's going to be a huge disadvantage.
Extract code into a function, automatically suggested by duplicated code detection. Inline functions back into call sites.
Convert imperative for-loops to functional style streams and back again.
Change function prototypes globally by adding or removing parameters.
Extract classes and interfaces.
Common sub-expression elimination (i.e. select an expression, introduce a new variable, all uses of that expression can be replaced as well)
Detect dead code and automatically remove it.
Refactorings across languages.
Replace inheritance with delegation or vice-versa.
Automatically generify code. Extraction of type variables.
Obviously a whole suite of structural code changes like renamings and other smaller things.
That's without getting into all the other code intelligence features.
Regardless of how fancy you feel the type system of a language is, these sorts of keyboard-driven refactorings are tremendously helpful for getting code and APIs right.
Agreed, though hare is trying (has, but needs editor integrations?) to address this:
Mutability is definitely not less hassle or time if you account for the dramatic difference in error rates and maintenance costs.
These programs are almost wholly absent in Haskell. List all the programs used for something that isn't writing code that were written in haskell.
Xmonad Pandoc Git-annexe maybe ... And.
Please really do add to this list, its length is informative and tells us something about haskell's strength and weakness.
As smugly as possible and with a huge grin: Purescript
Some I can think of off the top:
- PostgREST
- hoodle
- Microsoft Bond
- git-annex
- darcs
Why? It's important not to just yell "rah rah" but actually analyse it as a problem and maybe, just maybe, you know, solve it.
Alternatively, why do you think there aren't small fun toys and/or commercial usage of Haskell?
https://github.com/dpiponi/Stellarator
(Work in progress but many games work well.)
http://fluffynukeit.com/software/fnistash/
If you're looking for not-necessarily-polished personal toy projects, probably just about everything in Hackage's "Game" category fits:
http://hackage.haskell.org/packages/#cat:Game
Monadius has an interesting look. Sadly a touch of bitrot since 2013 - you'd need to `cabal get monadius` and edit
--- Monadius-0.99/src/Monadius.hs 2013-05-28
+++ Monadius-fix/src/Monadius.hs 2017-01-25
@@ -18,5 +18,5 @@
import Data.List
import Data.Maybe
-import Graphics.UI.GLUT hiding (position)
+import Graphics.UI.GLUT hiding (position, DebugMessage)We can see this in a microcosm when we consider music. What makes music popular? To a large extent, it's music that's listened to by the right people at the right time—perhaps seeded intelligently with exposure and marketing. There are some minimal bars the music itself has to pass, of course: it can't be terrible and it has to be accessible, but those aren't high bars to clear.
There are thousands of bands as good or better than most of the ones you hear on the radio but they don't get anywhere. You just never hear them, or they never catch on among your friends and never make inroads into your social network. (Or, more importantly, into the social networks of the labels that push music in practice.) Unless they do, in which case you have some unknown band "going viral"—virality says a lot about how something spreads and little about the thing itself. It doesn't even matter what you mean by "better": some objective notion of quality, musical sophistication, aesthetics, "catchiness", pertinent lyrics... whatever. That's not the main driver of popularity.
Nautilus had a great article[1] about this a while back, based on some experiments run with music. They created several large groups of participants listening to the same set of 40 musical tracks over time. People in the control group listened to music independently; the other groups all had social feedback mechanisms within the groups. The results were all over the place: the popularity of songs was completely inconsistent across groups, and could be traced to early chance decisions that snowballed over time.
The article had a great analogy about how the whole process worked:
> …a single match is not the entire reason for a wildfire starting and spreading. But that’s exactly how we naturally think about social wildfires: that the match is the key. In fact, there are two requirements: a local requirement (a spark), and a global requirement (the ability of the fire to spread). And it’s the second component that is actually the bottleneck: If a forest is dangerously dry, any spark can start a fire. Sparks are easy to come by, and are not intrinsically special.
The programming language in question? Its qualities? Pragmatism? Purity? Elegance? All just sparks. Which one starts the biggest fire depends far more on its context than the spark itself.
[1]: http://nautil.us/issue/5/fame/homo-narrativus-and-the-troubl...
(The article was written by a researcher in the field and cites a few papers on the topic, if you want to read more.)
Reasoning about pure functional data structures in the absence of laziness isn't that difficult, any more than the many mutable data structures that have copying/rebuilding/rebalancing that takes place only sometimes. The benefit of pure functional data structures is most visible when there is some kind of sharing -- concurrency of some kind -- in which case, reasoning about side effects becomes more difficult.
There are good approaches to handling in-place array update and similar operations in pure functional programming (the ST monad, for example), it's just not the default. As long as the side effect is somehow accounted for in the return value, referential transparency is maintained; we might say the return value is there to help us reason about side effects.
Anecdotally, I can't think of a case of someone who's used Haskell with proficiency and feels another language is generally better. But I can think of many cases of people who, confronted with the learning curve, set it aside to learn something else more immediately applicable.
Just looking at some bullet points, Haskell offers:
* A nice interpreter,
* Default immutability,
* A flexible and clear type system.
Default immutability does make reasoning about your program much easier. I don't think anyone would argue that but if you disagree it'd be interesting to hear what you have to say.
The absence of an interpreter in Go is a major nuisance, and makes it about as difficult as Rust to try out something simple.
Whether you like the type system of Haskell or not, it is pretty good for the kind of type system it is. There is a joke that Haskell libraries don't have any documentation and when people complain the Haskellers say, the types are enough. At a certain point you realize this is true: you get a lot more out of a Haskell type signature than a Java or C type signature, so much so that your approach to a library is generally "I'm looking for a function A -> B..." and you fish around by type. This is the dream of types: meaningful, machine-checked metadata.