A founder's perspective on 4 years with Haskell
baatz.io
baatz.io
Also there are occasionally posts to the /r/haskell subreddit with job listings.
Good luck!
Eg getting into Jane Street requires a firm handle on functional programming, not just a desire to learn.
In general, decent advice. If you don't get in now, try again in a year. There's almost no downside.
I remember reading Functional Programming (FP) leads to more readable code, fewer lines and financial companies use them for some reason. I assume it must be because FP programs guarantee the certainty of their results, but I am not sure. Are there side effects in FP languages?
Of course, the problem with Haskell being what it is, you'll probably end up with very few bugs to squash, so you may end up programming yourself out of a job :). Hopefully the area that you work in isn't quite that static such that requirements will change from time to time...
1. IRC. Join the #haskell channel on Freenode. Lurk for awhile and follow some of the conversations. Try to participate in discussions when topics come up that interest you. Don't be afraid to ask what might seem to be stupid questions. In my experience the people in #haskell are massively patient and willing to help anyone who is genuinely trying to learn.
2. Local meetups. Check meetup.com to see if there is a Haskell meetup in a city near you. I had trouble finding a local meetup when I was first learning Haskell, but there are a lot more of them now. Don't just go to listen to the talks. Talk to people, make friends. See if there's any way you can collaborate with some of the people there.
3. Larger non-local Haskell events. Find larger weekend gatherings of Haskell developers and go to them. Here are a few upcoming events that I know of off the top of my head:
Hac Boston - http://www.meetup.com/Boston-Haskell/events/231606922/
Budapest Hackathon - https://wiki.haskell.org/Budapest_Hackathon_2016
Compose Melbourne - http://www.composeconference.org/2016-melbourne/unconference...
MuniHac - http://munihac.de/
Hac Phi - https://wiki.haskell.org/Hac_%CF%86 (2016 is coming but hasn't been announced yet)
The first event like this that I went to was Hac Phi a few years back. Going there majorly upped my game because I got to be around brilliant people, pair program with some of them, and ultimately ended up starting the Snap Web Framework with someone I met there. You might not have a local meetup that you can go to, but you can definitely travel to go to one of these bigger weekend events. I lived a few hours away from Hac Phi, but I know a number of people who travel further to come. If you're really interested in improving your Haskell, it is well worth the time and money. I cannot emphasize this enough.
4. Start contributing to an open source Haskell project. Find a project that interests you and dive in. Don't ask permission, just decide that you're going to learn enough to contribute to this thing no matter what. Join their project-specific IRC channel if they have one and ask questions. Find out how you can contribute. Submit pull requests. This is by far the best way to get feedback on the code that you're writing. I have actually seen multiple people (including some who didn't strike me as unusually talented at first) start Haskell and work their way up to a full-time Haskell job this way. It takes time and dedication, but it works.
5. Try to get a non-haskell job at a place where lots of Haskell people are known to work. Standard Chartered uses is Haskell but is big enough to have non-Haskell jobs that you might be able to fit. S&P Capital IQ doesn't use Haskell but has a significant number of Haskell people who are coding in Scala.
[1] Propeller [2] git-annex
[1] twenty years of free software -- part 12 propellor
http://joeyh.name/blog/entry/twenty_years_of_free_software_-...
[2] twenty years of free software -- part 7 git-annex
http://joeyh.name/blog/entry/twenty_years_of_free_software_-...
I've just interviewed for a bunch of FP jobs. The going is getting better and better. (I went with an OCaml job this time and declined two Haskell offers, but that's because of other factors, not the languages themselves.)
The tooling is amazing though.
> You lose some guarantees, but gain a lot, and the quality
> of a codebase will always be up to its programmers.
I started using Haskell because I thought it was cool how it could spread out work over multiple cores. I continue using it because of its guarantees, enabling me to do large refactors and be reasonably confident that the compiler has caught all errors (if I construct my types properly).I would argue that the only true value a compiler has to offer are guarantees. A compiler that is able to do something "most of the time" isn't worth much. So, losing some guarantees may be both crucial and irrelevant, depending on which guarantees we're talking about.
I think the answer that to that question appears to be yes [1], but I stopped experimenting. I am considering resuming my experiment now that Frege appears to be crystallizing a bit more.
The java interop is nice, albeit it imposes discipline (as it should). Since java doesn't care about side effects, most of the values returned from a java function call will be in a Mutable monad. Here [2] is a good walkthrough for how it is done in case you want to try it out.
[1] https://groups.google.com/d/topic/frege-programming-language...
[2] http://mmhelloworld.github.io/blog/2013/07/10/frege-hello-ja...
Do you have some examples for "compiler bug"?
1. (Search for "asInstanceOf"): http://typelevel.org/cats/tut/freemonad.html
2. (Finally fixed and will hopefully be released soon - yay!): https://issues.scala-lang.org/browse/SI-2712
3. https://twitter.com/extempore2/status/430456002392391681
4. http://pchiusano.blogspot.com/2011/05/making-most-of-scalas-...
5. (Anecdotal): Various pattern matching peculiarities.
Compiler bugs: As long as I write code on the happy path and steer clear of scalaz/shapeless/cats, the compiler is pretty well-behaved and I can go months without getting a compiler crash. However, when I add those in and start writing pure functional code with complicated types that hits the intersections of various features (e.g., GADTs, package objects, implicits), I get a few crashes per week related to known bugs.
It works pretty well, and I am not trying to complain too much about all the mileage I get out of Scala and the excellent tooling! Just trying to get the most out of it.
Interesting! Can you link to the issue?
My current code base is not open-source just yet, so reporting self-contained reports may not be possible. (Currently I'm writing an interpreter for a fairly big declarative language using FreeApplicative from cats as the substrate, and I think the sheer size of it along with shapeless and scalaz usage is causing some undesirable feature interactions.
Where would be the best venue for reporting (known) bugs? Is there any value if I can't provide the actual code? Or is just reporting a "I experienced it too" message valuable?
Even if you can't publish your code, it might help to say "I'm seeing the same error, but I'm using X unlike the exmaple code that uses Y".
This will make sure that people who fix a bug will cover both causes with a test.
edit: The extension to [1] is actually pretty benign and the compiler does well with it. The part that puts a big load on the compiler is that it is used as an interpreter for a popular, yet complicated, visualization DSL. The compiler, especially the typechecker, has to do a huge amount of work.
[1] https://github.com/jdegoes/scalaworld-2015/tree/master/src/m...
SI-1503 should warn in 2.12, additional progress might happen in https://github.com/scala/scala/pull/5310.
http://www.kurilin.net/post/117369543198/haskell-at-front-ro...
Ultimately, the functional solution was verbose, harder to understand and still slower than the more imperative solutions in Clojure (also very hard to get good performance in BTW, but at least you can easily implement performance critical stuff in Java) and Ruby.
That experience really turned me off Haskell.
I'm guessing people use your learning software during weekdays and working hours (5 days and 8 hours/day). That's 3.4QPS. How many machines are you running on?
Also, I'm really shocked to hear you've not encountered any bugs. How many humans are using your systems? A scale is good enough (100s, 1000s, 10000s?).
Who said a "learning action" is equivalent to a query?
>Also, I'm really shocked to hear you've not encountered any bugs.
What's so shocking about it?
It's a different style of development.
A lot of people have never worked on projects without lots of bugs.
My current F# project for example has almost 100 outstanding issues.
They would be really surprised to know that lots of people have worked on large c projects that are virtually bug free.
Examples of (a) are not always malicious, mind. For a small shop, if your program messes up because during a process the machine it was on got unplugged, it is easy to see why that can be called not a bug in the system. When your shop gets large enough, though, scenarios like that become far too common.
You make it sound like there are numerous studies supporting your conclusion, can you link a few of them?
Each week the number of individual users was in the thousands; that was usually a rolling window since people tend to not do more than a few e-learning courses per year. I'm certainly not claiming that there are no bugs -- in all likelihood there are -- only that no bugs have yet crashed the system or were obvious and serious enough for users to tell us about.
Also check out F#, especially if you already have a code base involving .NET in any way. The CLR makes it super easy to write some parts in a functional language and other parts in more traditional OO - after all, the right approach often varies even within projects.
As a personal anecdote, taking the time to learn Haskell or any other functional language makes you a better programmer. The concepts often apply to less'pure' languages and certainly stretch you to think in new ways.
Which string to use? String, ByteString or Text or something else? How to convert between zillions of String types, which are, it seems, completely incompatible even though providing similar functionality? So much for the support of abstraction.
Which library is good for doing X? It seems, like String, the Haskell provides too many experimental and/or incompatible and/or immature libraries to do many trivial things.
Then there are issues of laziness combined with IO and you get a `seq` shenanigan.
Then there are issues of laziness combined with inability to profile/debug and you get a `analysis` shenanigan.
Then there are issues of laziness combined with inability to handle exceptions without great grand-daddy-catch-all-bad-things IO monad and you get a `IO monad exceptions` shenanigan.
So, there are a lot of Haskell shenanigans, too. Take your pick.
In fact, I have learned a lot many good programming practices/principles by spending time to learn Haskell. I do apply some of those in my projects in other languages.
A lot of the problems I encountered probably boiled down to inexperience on my part, and perhaps not understanding idiomatic ways of doing things in the language. But another part of it is that I think the community has not settled on a set of common idioms.
There are things about Haskell that I absolutely love. The expressiveness is amazing. Chaining a monadic sequence can feel magical at times. Treating errors as just values is nice. But what do you do when the libraries that you depend on throw exceptions instead of using Either?
And those awful compiler errors can be so, so frustrating. But I will return to you again one day, dear Haskell.
The issue is when this monad is actually a monad transformer tower, in which case there is actually a bunch of "magic" happening at each line.
File a bug on them
tome's response is amusing, but assuming you need to keep getting work done and aren't interested in forking the library...
You wrap the library in a shim that forces the return values, catches anything that's thrown, and returns Either. If it's a library that you only use in one or two places, that shim might be inlined.
There's strict and lazy ByteString, which you should use whenever you're working with buffers of bytes that don't have any associated encoding and aren't text.
There's strict and lazy Text, which you use for human-readable text that's decoded from some specific encoding, like ASCII or Unicode.
There's String, which you use when interacting with an API that requires use of String, like anything in the language standard.
It's certainly some amount of complexity, but it's not anywhere near as complex as I keep seeing claimed, unless I'm missing something. It's certainly ugly that we still have to deal with String, and that's a notable wart on the language, but I rarely even think about it.
One guess I've had about part of the confusion is that ByteString has the word "String" in it, which might make people assume that it's for text. A better name would be "Buffer" or "Bytes". I've also speculated that maybe part of the complexity is having to explicitly encode and decode to get Text, instead of the implicit coercion you get in other languages that freely mix bytes and text.
If you want a greatly simplified way to unify string conversion, you can use string-conv. https://hackage.haskell.org/package/string-conv-0.1/docs/Dat...
Edited to add: it's been pointed out to me that "zillions" might not have actually been a claim that there's enough string types actually keep track of them, but is probably just hyperbole to express some frustration. If that's the case, my apologies for my failure at reading comprehension.
Python got to two, and the community decided that was enough of a PITA to merit a breaking change in the language in order to get back down to one. C++ also has two, but we put up with it because it's C++ so really we're just thankful it's not three or four.
Five is so far beyond the pale that it is absolutely reasonable to hyperbolize it as "zillions".
Treating all bytes as if they happen to accidentally represent utf-8 encoded unicode text would be a big mistake; there's significant advantages to representing text and byte buffers separately. They are very different things.
Choosing to support only strict or only lazy handling of byte buffers or text would be quite unfortunate; there are significant advantages to both for different algorithms and use cases, and encoding the difference in the type system seems entirely reasonable to me.
I'm curious which of these you disagree with. Would you prefer that Text and ByteString be merged into one data type, so that the compiler doesn't consider it a mistake to treat arbitrary bytes as if it were text without specifying any encoding? Would you prefer that Text and/or ByteString discard support for lazy representation, or for strict representation?
http://www.suspectsemantics.com/blog/2016/03/27/string-types...
I would say it's actually justified in that.
If one considers those to be string types, then Vec<u8>, Vec<16>, and Vec<32> would be considered string types as well (in addition to both fixed-size arrays and unsafe pointers to the same).
All the other types provide specific functionality that you won't get otherwise. (While String is there only because it is easy to use.) If you really don't care about that extra functionality, just keep with strict Text, and be done with that.
Now, if the perfect number of string types is 1, why couldn't you name any language with that number. By the way, Java also has two string types, as does .Net. Python still has 2 types, they just don't coerce into each other anymore, and I've probably seen a dozen C++ string types already.
Why not help bring some of Haskell's concepts to a more relevant language instead of bemoaning the entirely rational decisions made by millions of junior and senior developers?
Edit: the rabid insularity of the Haskell community definitely doesn't help. The Python community was dissatisfied with JS and created Coffeescript, and as of ES6 now everyone can enjoy the fat arrow syntax. Less pleasantly, the Java community got class syntax added to ES6, but at least they're trying to contribute. I'm sure Haskell has plenty of really cool things to bring to the table, but I've never heard of any of them, because all the Haskell community wants do is talk about how JavaScript sucks instead of embracing the functional side of it.
> instead of bemoaning the entirely rational decisions made by millions of junior and senior developers
I think people are bemoaning the stupid shit and not the entirely rational decisions. JS makes the former a bit easy.
> Python community was dissatisfied with JS and created Coffeescript
Nitpicking, but the first CoffeeScript compiler was written in Ruby and the language was largely inspired by Ruby.
> the Java community got class syntax added to ES6
Huh that's a new one
pretty sure that's not just the Java community, given that JS developers have been independently inventing class libraries (starting with Prototype, if not earlier) for ages.
> I'm sure Haskell has plenty of really cool things to bring to the table, but I've never heard of any of them
Underscore, lazy.js, other popular JavaScript libraries have some Haskellisms; LiveScript is a fork of CoffeeScript that used to be moderately popular and very Haskelly; React takes a lot of ideas from Haskell; immutable.js is quite Haskellian….
I think you're just not looking.
> all the Haskell community wants do is talk about how JavaScript sucks
You can drive a go cart on the highway, and you can keep modding your go cart, but at some point you might want to not be driving a fucking go cart on the highway.
Not quite coming from the Haskell community, but heavily inspired by parts of Haskell is Elm, which you might have heard about. It has the best compiler errors ever, it takes immutability seriously, and is far nicer IMO than either Purescript or Javascript for web development.
It's really an issue of types. Languages like Java, Python and Javascript don't have quite the same approach to types, but their worldviews there are not that different. Anything coming from the ML family just isn't going to translate, and that is going to happen regardless of how insular the communities might be. Existing Javascript features actively make most of the things a Haskell programmer would want just not work at all. This is why you hear them talk about how Javascript sucks: Everything they'd want involves taking things out first, and that is never going to happen.
So don't blame in on the community here, bad as some elements might be: The differences just cannot be negotiated away. You might as well ask people to open their mind and breathe carbon dioxide and sulfur so they can go visit you in Venus: It's a barrier that is too hard to be worth crossing in either direction. Trying to add Javascript or Python features to Haskell would get you in a similar boat.
It's written in Haskell. How much more "coming from the Haskell community" could it be?
Since you mentioned CoffeeScript, Elm seems like a particularly relevant example of something that came out of the Haskell community and which compiles to JavaScript.
I think you have a negative impression of both Haskell and its community which isn't necessarily justified. It's a little insular, but not to the extent that you're suggesting here. PureScript is another language that Haskell has almost directly spawned. It's a lot more complex than Elm, like Haskell itself, but has quite a few brilliant insights of its own.
Let's not forget Purescript[0], which is looking to be the spiritual successor to Haskell, and runs on node.
The carpenter blew us away. They don't allow publication of a preprint, but it was just accepted into the Journal of Patio Support Design!! Which, I'm told, is the third most-prestigious journal in the business...
No I meant like can we have a barbecue?
If the lenses for records thing in Haskell is amongst the simplest things for you, I beg to differ. I spent considerable time trying to get this working but could not succeed beyond toyish examples. Combine lenses/records with exceptions, applicatives, monads and monad-transformers as they become necessary once you try to expand your toy-example code and the Haskell thing becomes anything but simplest.
Yet, other languages support records flawlessly and almost out of the box.
The heavy and unnecessary usage of symbols, like, >>, <<, >>=, >=>, <=<, and so on just adds cognitive overload and gives no inherent benefit but most of the Haskell library code is littered with it and due to almost arbitrary overloading of these symbols to mean different things in different contexts (libraries) just adds more cognitive overhead without any benefit. The almost cult-like insistence on the excessive usage of meaningless-to-humans symbols reminds me of the great practical programming language known as brainfuck [1]. Sorry but no sarcasm intended. Some Haskellers harp on succinctness, when talked about this religious fetish for symbolism in Haskell. So I ask, such succinctness at what cost?
The excessive and obsessive usage of symbols may give mental kicks to the hardcore Haskellers out there, but sorry, I don't see any incentive to waste my time trying to wade through the gobble-de-gook of brainfuck type code.
So sadly, Haskell and me are not a match. Maybe some most common things in other languages when tried in Haskell seem to be (are) rocket-science (e.g. lenses for records) and/or maybe I am a daft blockhead, but that's so.
This is an oft-repeated criticism of Haskell which I frankly find has no basis. Firstly, symbols are preferred for these kinds of functions precisely because of their ubiquity. Once you're familiar with them (which you almost certainly will quickly become, for the common ones like `>>=`, and `< * >`) they make the code easier to read, not harder, in general. Few would complain about having to write "<" to compare two numbers in most languages, rather than, say, "lessThan", because it's all over the place, and having a symbol for it makes it easier for a developer to read. The same is true for many common combinators in Haskell. There is no "cult-like insistence" on them; it's simply the case that many Haskell developers find them more pleasant to write than named functions. Honestly, the comparison to brainfuck is completely unfounded, and the references to cults and religious fetishes are IMO unnecessarily derisive.
Furthermore, I'd add that what makes the symbols seem hard is not the fact that they're symbols, but that the ideas that they're representing are challenging when coming from the world of imperative programming. Using a named function like "bind" rather than ">>=" or "apply" rather than "< * >" is not going to help the fact that these functions are complicated and unintuitive to newcomers. I think a lot of the complaints about Haskell symbols, much like complaints about Haskell syntax, are more driven by how different Haskell semantics are to mainstream languages. Once a developer becomes comfortable with the "zen" of Haskell, these criticisms tend to melt away. That isn't to say that everything becomes easy: there are many concepts that one is only likely to encounter in Haskell, and they can often be challenging, or make someone else's code hard to grok. But the difficulty does not stem from the fact that they're written as symbols. IMO.
As for your comments on lenses and records, I've been writing Haskell for a good number of years and still haven't really spent the time to grok lenses. I think they're probably great, but certainly mastery of them is not required to be productive with the language.
My issue, back when I was exploring Haskell, was not so much with the symbols in the standard library (eg, for applicative functors), but with the amount of library authors who thought they needed their own little shitty ASCII DSL.
Give me any non-trivial industry scale web/database application which doesn't rely on the notion of records. The support for records in Haskell is if not pathetic is very poor.[1] (This is a rather older reddit post but I haven't kept track of it.)
I can be productive with Haskell if I restrict myself to math-type pure computations (e.g. DSL compiler) but if you want anything to do with web/database type non-trivial IO using Haskell, good luck.
So, I restrict myself to use Haskell only for doing pure computations that require extremely simple and trivial IO (mostly just readFile/writeFile).
>>This is an oft-repeated criticism of Haskell which I frankly find has no basis. Firstly, symbols are preferred for these kinds of functions precisely because of their ubiquity.
How about Haskell's notion of fixity of the symbols/operators/names?
It seems this almost arbitrary fixity [2] notion just adds cognitive overhead with little to zero semantic benefits. Correct me if I am wrong and please enlighten me on exactly what semantically significant benefits are offered by the notion of fixity and at what cost.
Most other mainstream languages don't offer such a useless flexibility like fixity which causes so much cognitive distraction when used by different people with different semantics, and that too just for kicks.
[1] https://www.reddit.com/r/haskell/comments/k4lc4/yesod_the_li...
Every language has implicit fixity and precedence for infix operators. What's different about Haskell is that you can actually define infix operators yourself. And if you're going to define your own you need to be able to also define the precedence of your operators. Fixity has always been there. You just didn't realize it.
It seems I didn't make my point clearer. I am asking what is the semantic benefit for the programmer of the explicit fixity and ability to change fixity at one's will?
The whole notion of explicit fixity in Haskell exists because of Haskell's fetish (pardon me using this term again) for symbols instead of functions-names with fixed implicit fixity. They needed to support explicit fixity only because they wanted the programmers to be able to define operator symbols arbitrary with arbitrary fixities.
So, now when you wish to read someone's code, then other than dealing with the inherent complexity present in their code, you also have to deal with the intentionally inflicted accidental complexity (to paraphrase Fred Brooks) over there because of their choice of using symbols with arbitrary fixities. [1]
IMHO, Haskell adds to accidental complexity in this way instead of curtailing it just to satisfy their urge on usage of arbitrary symbols.
Let me explain it a bit further: to read someone's code, first I have to see what symbols they have defined and what fixity they have ascribed to each of those. Now I have to keep all that additional unnecessary cognitive load in my brain to just be able to get a hang of their code with their fixity rules associated with their symbols.
[1] https://en.wikipedia.org/wiki/No_Silver_Bullet
edit: s/symbols over functions/symbols instead of functions/
Haskell lets you make your own infix operators so that you can get the same benefits in other domains than the ones that the language designers decided to provide for you. Therefore it follows that Haskell also needs to allow you to specify the fixity and precedence so that your operators will fit together in the way that is appropriate for your domain.
> Let me explain it a bit further: to read someone's code, first I have to see what symbols they have defined and what fixity they have ascribed to each of those. Now I have to keep all that additional unnecessary cognitive load in my brain to just be able to get a hang of their code with their fixity rules associated with their symbols.
The same is true of C, Java, Python, etc. The only difference is that those languages have a closed set of operators while Haskell's is open. There is a small amount of cognitive overhead, but I think you're greatly exaggerating it. In six and a half years of professional Haskell development I can count on one hand the number of times this has been an issue. And it can be trivially resolved by adding parentheses or by decomposing the expression into separate expressions.
This is the key point. Operators drawn from my target domain probably already have well established fixities. Forcing my expressions to look radically different than what's in the textbooks and papers discussing the domain in which I'm working is itself introducing incidental complexity.
As to the questions about fixity, this is once again in practice not a big problem. First of all the notions of operator fixity and association are familiar to all of us since grade school. We all know "please excuse my dear aunt sally" or some similar mnemonic for the precedence rules of the operators of arithmetic. It's this rule that lets us write "a^2 + 2 * b^2 * c^(2 / 3)" instead of "(a^2) + ((2 * (b ^ 2)) * (c ^ (2 / 3)))". In other words it's a way to make an expression easier to read and parse by removing superfluous parentheses. It's not arbitrary at all, and in fact operator precedence rules is a feature that every other mainstream language has.
As to what semantic benefits there are to fixity, fixity is a purely syntactic notion. It has nothing to do with semantics. Giving your operator an associativity and precedence only affects how a particular expression is parsed, not how it's interpreted or evaluated.
I'd like to mention that your comments so far seem unnecessarily combative and hyperbolic. You use phrases and words like "pathetic", "good luck", "useless flexibility", "zero benefits", "please enlighten me". This injects contention into a discussion where there needn't be any. Furthermore, your statement that you only use Haskell for pure computations with minimal IO makes me question the legitimacy of your criticisms. Certainly, there are countless others who have written all manner of heavily interactive programs, web servers, database interaction, or IO-intensive programs in Haskell. If Haskell isn't your cup of tea, that's fine, but do you really think you're in a position to be leveling such criticisms when your own usage is so restricted?
Be specific. Support for record field access is fine. Support for record creation is fine. Support for pattern matching on records is fine. There are two "problems with records" in Haskell.
First, unlike many languages, records don't create implicit namespaces, so if you define Foo { x :: a } and Bar { x :: b } in the same module you have ambiguity. This is addressed, in part, by several recent GHC extensions. It could always be handled by putting Foo and Bar in different modules (with some added complexity if Foo and Bar are mutually recursive), but the community has settled on prefixing (Foo { fooX :: a }, Bar { barX :: a }) which is a little ugly but perfectly workable.
Second, while support for member update is okay when shallow, it compounds in a particularly nasty way when it gets deep. So what in Java would be "x.y.z.a = 7", in Haskell is something like "x { y = (y x) { z = (z (y x)) { a = 7 } } }" - horrific, I agree. But note that "x.y.z.a = 7" isn't great practice in Java, either! Why do I know about the fields of a field of a field of my object? Far better if that's packaged up in a setter. And if you define setters, the Haskell gets cleaner too. Lens is a principled way of defining getters and setters (and more) that can be composed and manipulated consistently - but it does have a huge vocabulary which obviously creates a barrier until you've learned it. But you don't need lens to write setters (and for that matter, you don't need to know all of lens to use some pieces of it).
I've also found that RecordWildCards makes records genuinely nice to work with in many cases - unpack the record into the local scope, at which point the prefixed names don't seem verbose, transform things as I want, and then re-collect the fields I can while explicitly setting those I need to.
The recent trend of "functional JavaScript" is mostly using third party tools that emulate feature from other languages (like Immtutable.js or Ramda), but IMO the core language cannot be called a proper functional language.
Everything else is a convenience and not a requirement for the functional paradigm.
Mind you, I do much prefer a Lisp or Scala to Javascript, but I face no trouble writing functional code in Javascript.
Out of curiousity, what other criteria would you apply?
You're more than welcome to have your own definition, but it's my opinion that it's too broad since HOFs exist in some form in languages like Java and Ruby and I wouldn't call them functional though like another commented said, languages are mixing paradigms nowadays (with the exception of Haskell!).
The big paradigm divisions were largely a thing of the 1990s; since then languages have become increasingly multiparadigm, so that it's almost hard to find a language that isn't nowadays. I think Haskell and Golang might be just about the last remaining standard-bearers of the league of one-trick ponies.
Last year, I wrote an article [1] talking a little bit about this, arguing that the notion of 'language paradigms' is mostly a crutch to avoid thinking critically about what a language is.
[1] https://chadaustin.me/2015/09/haskell-is-not-a-purely-functi...
(0) It encourages using values over objects with a notion of physical identity.
(1) It encourages using procedures that compute functions: mappings from values to values.
(2) It discourages distinguishing between procedures that compute the same function.
Let's see how well JavaScript fares:
(0) Are `[1,2,3]` and `[1,2,3]` equal or different?
(1) Given `function(x) { return { first: x, second: y }; }`, are `f("hello")` and `f("hello")` equal or different?
(2) Are `function(x,y) { return x + y; }` and `function(x,y) { return x + y; }` equal or different?
This should tell you whether JavaScript is a functional language.
> A functional language is a procedural language
this doesn’t sound authoritative:
> that meets the following three additional conditions
this doesn’t sound like the right distinction:
> It encourages using values over objects
and this doesn’t sound meaningful:
> It discourages distinguishing between procedures that compute the same function.
A functional language still uses procedures, even Haskell! To see what I mean, consider the possibility of divergence (infinite loop, `undefined`, `error "foo"`, you name it). It doesn't make sense for a mathematical function to diverge. The difference between, on the one hand, Haskell, ML and Racket, and, on the other hand, JavaScript, Python and Ruby, is that the former group encourages you program with procedures that compute mathematical functions, whereas the latter group does not.
> this doesn’t sound like the right distinction
It doesn't suffice, but it's a precondition. For good or for bad, mathematical functions are mappings from values to values, so you can't talk about computing functions in a language without a rock-solid notion of (possibly compound) value.
> and this doesn’t sound like the right example for (2) [deleted from your original post, but I'm not deleting it from here]
Yes, it's the right example. Haskell and ML prevent you from accidentally distinguishing two extensionally equal functions by not letting you compare procedures in the first place.
> and this doesn’t sound meaningful
These functions ought to be extensionally equal in any mathematically civilized language:
function foo(x,y) { return x + y; }
function bar(x,y) { return x + y; }
But JavaScript lets me distinguish them.And Prolog programs are literally lists of declarations (of inference rules), yet Prolog isn't a functional language.
Backing up to equality as an aside, is there any language that will let you determine whether two functions are equal? If the functions that can be compared aren’t restricted to some silly level and the language is Turing-complete, I can’t imagine any better behaviour than Haskell’s that you already mentioned, which you can bring to JavaScript pretty easily.
Well, your quote doesn't even provide a full definition, sooo...
> Backing up to equality as an aside, is there any language that will let you determine whether two functions are equal?
No, that's obviously undecidable. (Rice's theorem.) But read what I said more carefully. Did I ever say “A functional language lets you determine whether two functions are equal?” I didn't! What I said is “A functional language doesn't let you distinguish equal functions.” Not letting you test functions for equality is a perfectly valid (and, in fact, the only valid) approach in a higher-order language.
> which you can bring to JavaScript pretty easily.
Not really. I can't undefined JavaScript's existing equality operator.
I thought as much and it would really help if you would just say these sorts of things outright so the comments are actually useful instead of deeply nested wording quibbles.
With that out of the way: you can define a subset of JavaScript that satisfies your third condition by saying “don’t compare functions”. That’s why I didn’t consider it a meaningful condition.
Functional programming works best as a paradigm and then we can just call languages where it’s standard “functional languages”. Much simpler.
I find it absolutely essential to make perfectly clear what exactly I'm refuting.
> With that out of the way: you can define a subset of JavaScript that satisfies your third
Second.
> condition by saying “don’t compare functions”.
That's a different language. Do you know of any implementation of it?
> Functional programming works best as a paradigm and then we can just call languages where it’s standard “functional languages”. Much simpler.
Nope. A functional language is one that makes it easy to write functional programs, by satisfying the conditions I gave above. A programming language is first and foremost its semantics, whatever the culture of its users.
Except you’ve still given no source for the conditions, condition -1 (“a functional language is a procedural language”) is wrong, and a language satisfying the conditions in no way makes it easier to write a functional program just by virtue of satisfying the conditions (because they can be satisfied entirely by removing features you don’t have to use). Maybe you meant “harder to write non-functional programs”.
> Second.
The last item in a list of three items is not the second item.
It's correct. In a procedural language, computations are structured as procedures that may call each other. In a functional language, that condition (plus three additional ones) hold.
> and a language satisfying the conditions in no way makes it easier to write a functional program just by virtue of satisfying the conditions
Removing pointer arithmetic makes it easier to write programs that don't contain memory errors. Removing spurious distinctions between memory blobs that hold the same value makes it easier to program with mathematical functions.
> (because they can be satisfied entirely by removing features you don’t have to use)
Removing features creates a different language. Often a nicer one!
> Maybe you meant “harder to write non-functional programs”.
Nope. That would exclude any general-purpose programming language. Lest you think otherwise: it's very easy to write imperative programs in Haskell and ML.
> The last item in a list of three items is not the second item.
Zeroth, first, second.
In other words, Haskell doesn't have try/catch for things like `undefined`, so we do have to worry about them occuring, but we don't have to worry about them causing the code to misbehave; it'll either behave or it will stop progressing (either halting or looping infinitely).
On the other hand, the "IO" DSL built in to Haskell can do try/catch; but once you're in IO, all bets are off.
The nice thing about Haskell is that it distinguishes between procedures that may only compute functions (or diverge), and procedures with arbitrary effects.
Erm, yes you can. You don't need "a rock-solid notion" of anything in order to talk about anything. It's certainly nice to have such well-defined concepts/terms, but requiring them is preposterous.
So? Is the problem here that you can’t compare objects equivalent according to this with `==`? You can make a comparison to do this properly. (This is why functional programming as a paradigm works better and functional languages are just those where it’s the obvious standard.)
That's not the point! Even if I made a custom equality comparison that says two different objects are “equal”, it would be unsound to treat them as equal, because there exist other constructs in JavaScript that will let me distinguish them. For example, if I mutate one of the objects, the other will remain in its original state. This is exactly why a usable functional language must provide a good notion of compound value!
I didn't see anyone making claims about soundness. From a Curry-Howard perspective, all JS code is sound: it's unityped, so all programs are proofs of the trivial theorem "true".
From the perspective of equality functions/operators, you're right that it won't be sound; but so what? It's pretty much given that "Javascript foo" is a poor model of "foo", for all values of "foo" (function, integer, boolean, etc.); why should equality make any difference?
Just add "==" and "===" to your list of things to avoid, alongside mutation, random numbers, user input and other things which aren't referentially transparent. To be honest, equality isn't particularly bad if your code has some notion of types/contracts (whether enforced or not); e.g. it's perfectly safe to use "==" when you know that both sides will be strings, for example.
> For example, if I mutate one of the objects, the other will remain in its original state.
Mutation doesn't seem like a great example when discussing (pure) functional programming patterns.
> This is exactly why a usable functional language must provide a good notion of compound value!
You introduction of the term "usable" seems like a moving goalpost/no true scotsman.
Haskell and ML aren't unityped, but, in both, every type is inhabited by expressions. (Caveat: In ML, not all types are inhabited by values, but that doesn't matter, because programs correspond to expressions, not values.) So this doesn't distinguish them from JavaScript.
> From the perspective of equality functions/operators, you're right that it won't be sound; but so what? It's pretty much given that "Javascript foo" is a poor model of "foo", for all values of "foo" (function, integer, boolean, etc.); why should equality make any difference?
Equality matters, because the very first condition to determine whether a procedure computes a function is to see whether it maps equal inputs to equal outputs!
> Just add "==" and "===" to your list of things to avoid, alongside mutation, random numbers, user input and other things which aren't referentially transparent.
That's a different language. Do you know of any good implementations of it? And, FWIW, procedures that compute random numbers are totally fine. You can't rule out procedures that don't compute mathematical functions. You can only keep their use to the minimum necessary.
> To be honest, equality isn't particularly bad if your code has some notion of types/contracts (whether enforced or not); e.g. it's perfectly safe to use "==" when you know that both sides will be strings, for example.
In my day-to-day programming I manipulate more complex data structures than strings.
> Mutation doesn't seem like a great example when discussing (pure) functional programming patterns.
I'm not talking about pure anything. I'm saying that mathematical functions are value mappings, and if the language doesn't let you define compound values (tuples, lists, trees, whatever), then it can only conveniently implement very trivial functions - not enough for practical programming.
> You introduction of the term "usable" seems like a moving goalpost/no true scotsman.
I'm not moving anything. A functional language is one that makes it easy to write functional programs. The lack of compound values means that I'm given two unpleasant choices:
(0) Use Gödel numbers to encode compound values. Would you?
(1) Do imperative programming.
In practice, Haskellers don't think this way about their programs. Haskell's library ecosystem attests to the fact they think of Hask as some moral analogue of the category of sets, which has both sums and tensor products. And the worst sin a semantics can commit is to not reflect how programmers actually think about their code.
function time() { return new Date() }
Referential transparency implies that we can everywhere replace an expression with any reduction of it but because the return value of this function can change even when its arguments don't, this is not true in Javascript.I'm aware, but that's too strong a requirement. I wanted my definition to capture the bare minimum necessary to make it pleasant to write functional programs. The basic idea is “functional programming is designing your programs in terms of mathematical functions whenever possible (not always!)”, not “functional programming is requiring an embedded IO DSL to do anything effectful”. (But don't take this as a dismissal of Haskell. While I do have complaints against it, effect segregation isn't one of them.)
> but because the return value of this function can change even when its arguments don't, this is not true in Javascript.
That's okay. A procedure that returns the system date will never be referentially transparent, yet such a procedure is a necessity in any programming language.
My beef is actually with the fact JavaScript doesn't have compound values: https://news.ycombinator.com/item?id=12191119 . Since compound entities (whether values or mutable objects) are a necessity in practical programming, the harsh reality is that JavaScript programmers have to use mutable objects whether they want it or not.
As long as it accepts a Universe or State parameter, a system time function is referentially transparent. This is rather like how every query in a database can be seen as implicitly depending on a transaction ID parameter.
One intuition that falls out of the Church-Turing thesis is that there is no routine in C, Pascal, &c that has no referentially transparent equivalent.
Which begs the question - where do you get that Universe parameter from? Note that getting a single Universe parameter at the beginning of the program is of no use, because then you couldn't query the system date right now. You could only query the system date at the moment the program started.
You can't completely get rid of non-referentially transparent procedures in a general-purpose programming language. What you can do, at most, is banish them to a special sublanguage, like Haskell does.
> the Church-Turing thesis
The Church-Turing thesis is a statement about computable functions, but a procedure that returns the system date clearly doesn't compute a mathematical function.
This is like saying, "...getting a single input at the beginning of the program is of no use..." but see below.
> The Church-Turing thesis is a statement about computable functions...
It is a statement about what is "computable", which is the alpha and the omega of what computers can do. If there is a Turing model for a particular "computation" there is a Lambda Calculus model for it.
A program that returns the system date operates on the machine state. You can treat this as "read from the tape" in the Turing sense or as input in the Lambda Calculus sense. The "output" can be seen as one of:
* A tape where the system states has been updated (since we took some steps, we updated the CPU counter, handled some interrupts, &c) and the system time has been written to a special place on the tape so the next computation can find it.
* A tuple of a new system state and the time value (picked out, again, for our convenience).
If we imagine the world "as a computer" then it is natural to compute a later state of the world as a function of the previous state. This approach actually works well in practice and it is not coincidental that this approach works with computers, since it is deeply tied into how they work.
Per Heraclitus, we never set foot in the same river twice. Treating the State or Universe as non-comparable captures this idea.
The Church-Turing thesis is about what functions are computable.
> A program that returns the system date operates on the machine state. You can treat this as "read from the tape" in the Turing sense or as input in the Lambda Calculus sense.
Are you saying that the input tape may contain the later time at which a program will query the system's date? That seems like a physical impossibility to me.
> If we imagine the world "as a computer" then it is natural to compute a later state of the world as a function of the previous state.
The fundamental deficiency of this model is that it assumes you can put the entire world inside your program, and then get an entire new world as result. It utterly fails to take concurrency into consideration.
Are you saying, that procedural programming can do something besides compute computable functions?
> Are you saying that the input tape may contain the later time at which a program will query the system's date? That seems like a physical impossibility to me.
When the computer retrieves the time from the clock, it does exactly that -- reads it from a location in memory.
> > If we imagine the world "as a computer" then it is natural to compute a later state of the world as a function of the previous state.
> The fundamental deficiency of this model is that it assumes you can put the entire world inside your program, and then get an entire new world as result. It utterly fails to take concurrency into consideration.
How does it fail to take concurrency into account? We can treat the world as being made up of many objects and evolve their state simultaneously if we like.
Or, say, SQL sans stored procedures?
I think the more appropriate term in this case is "declarative". "Functional" really implies something having to do with functions.
Purity is great help, but it isn't essential. The essential part is having a type system that actually captures what's going on in your program, without burdening you with manual annotations, and ML-family languages in general do very well in this regard.
(0) Abstract types, generated by means of opaque signature ascription, which Haskell doesn't have. This ensures that different modules can't “accidentally” each other's internal invariants.
(1) The fact variables stand for values (due to ML being strict), rather than potentially diverging computations (due to Haskell being lazy), which makes useful algebraic laws sound even in the presence of nontermination or whatever effects.
What do you mean? As far I can tell, functional languages get an amount of attention disproportionate to their usage and even this article is example - using js wouldn't get to the top of hn but Haskell would.
And it's not that I think this is necessarily a bad thing - functional is the "next big thing" for taming the insanity of programming and even sixty years in, we need still new models because programming is still a mess.
But let's frame things the way they are "here's an concrete argument the hype might be justified" or something like that.
JavaScript topics do make it to the front of HN. For example, https://github.com/getify/You-Dont-Know-JS was the top article on June 24.
If you mean that simply writing about using JavaScript without any particular angle probably wouldn't make it to the front page, you're right. But that's actually proportionate to the two languages' usage. It's similar to how "I drank some water" is less interesting than "I drank a rare Japanese whiskey" — absent of context, they're equivalent, but because everyone has done the former, the fact that you did it too is boring on its own.
I was referring more to people dismissing the benefits of functional programming as they dismiss the benefits of strictly typed and compiled languages. Many of the benefits of functional are the result of restrictions far more 'onerous' than knowing the type of an object at compile time. Those 'restrictions' make you think about your code more carefully and catch huge classes of bugs.
Using strings to access object properties in JS (to me) feels like shenanigans.
The issue is that many times the programming languages don't matter, what matters are the libs written for that language. For example, there are cryptocurrency libs for Ethereum and Bitcoins that are only available for JavaScript/Node.
Like it or not the better decision will be to use JavaScript only for this reason.
F#'s Type Providers basically make ORMs, query builders, scrapers, web request libs, parsers, etc, largely unnecessary, because they enable the language to have built-in support for fully typed foreign data sources.
Dart meanwhile, makes a large chunk of typical boilerplate js libs like jquery, underscore, promises, requirejs, etc, all obsolete while still being relatively similar to js.
However, js is still the language of the largest platform out there (the browser), and the F# community still doesn't push it's branding and marketing towards trendy startup engineers very much, compared to a community like ruby's.
Also, I would argue that what op was talking about did fall more in line with platforms than algorithms, despite what the libs actually did, because Ethereum and Bitcoin are themselves platforms that spark those kinds of libs. Not to mention that you're always liable to run into situations where some obscure algorithm isn't available in your language of choice, regardless of how popular it is (unless you're using c++, which marketed/encouraged the kitchen-sink approach to development, and has had a lot of time to build quite a few kitchens).
As for interfaces, I'm not sure exactly you mean that hasn't been covered already, like in the case of F# being able to smoothly interface with external data (which also includes the ability to interface with libraries from entirely different languages like R and Python). And interfacing with hardware is also a lot like a platform issue similar to the browser situation, except with C instead of js. And then there's stuff like arduino, which is also very clearly a platform.
All in all, branding and marketing shouldn't be underestimated as the driving forces behind a lot of tech decisions, because software development does have a very strong social component that shouldn't be ignored. I mean, just look at the type of site we're on...
> The issue is that many times the programming languages don't matter,
> what matters are the libs written for that language.
I'm writing a crypto currency app in Haskell [1], because even though every library exists written in JavaScript, I cannot read the code and feel like I know whether it works or not. There are too many ways for JavaScript to fail for me to be able to reason about whether the code is correct or not. And a financial library (one that can cause people to lose money if it doesn't work) with bugs in it is worse that no library at all, at least for my application.[1] https://github.com/runeksvendsen/restful-payment-channel-ser...
Hey, we have Typescript nowadays, which does help a lot.
I feel those would be the two libraries that I would probably miss the most when switching to Haskell for a project.
If you want to be at the same level of abstraction as SQLAlchemy Core, you'll want to try esquelito (a SQL DSL).
Esqueleto looks like it would fit the bill, since it does offer composability of queries, automated migrations, and protection against SQL-injections.
I think "#stars on Github" could be an interesting metric to add. Thanks for your effort in creating this site.
It's a fantastic framework, but it's not an 'http client library' as such.
There are several HTTP clients:
http-streams (http://hackage.haskell.org/package/http-streams)
http-conduit (http://hackage.haskell.org/package/http-conduit)
It is much more flexible than persistent. If you are not doing an yesod project (for what persistent is the standard), you should think about using it.
Such a great point and perhaps one of the biggest challenges as languages allow increasingly reusable and powerful abstractions. I would love to have GHC tell me something like "this piece of code here has a signature that is familiar, you could probably make this fit into {list of abstractions}".
Especially in the first year I learned a lot of new Haskell syntax and functions just from its suggestions.
For example, there is this fatal, unresolved bug that no one knows how to fix: https://github.com/carymrobbins/intellij-haskforce/issues/28...
EDIT: It's working again (for projects that do not exhibit above bug): http://i.imgur.com/jPo4Tas.png
Does more or less what you described.
Hope core.typed will be that!
I like Haskell also, but I feel Haskell is less readable than a lisp. But I will concede, this might be because I have been writing lisps longer than I have worked with Haskell.
Lisps are definitely the most readable language - and with something like Smartparens or Paredit, combined with Rainbow-delimiters in Emacs it's the best programming I know by very very far.
What is your experience of Typed Racket?
I realize the article is about Haskell but, but its also about a startup and a founder so I thought I would ask.
There was no particular effort to integrate -- of the team, I was the only one who regularly took lessons in Swiss German -- but it turns out that there are several practical reasons to run a European startup in Switzerland:
* Taxation in Switzerland is fairly light, lighter even than in the United States.
* Business regulation is comparatively streamlined, and labor flexibility is relatively great.
* The tradition of efficient public services is rather relevant to any one trying to manage a small tribe of people, anywhere.
* Lots of European people would like to work in Switzerland and, unlike SF, it is possible to hire from your European network without visas.
Relative to San Francisco, Zurich is not particularly expensive.