I want to see the list of applications written in haskell that are useful and not used for programming a computer.
This list has seemed shorter than one might expect in the past given the interest among programmers in the haskell language (this includes my own interest fwiw). A list of applications written in haskell that are used for something that isn't programming a is a useful datapoint that tells us something about the strengths and weaknesses of the language - we can argue about the subtleties of the meaning of that data point but it is useful data.
Shameless self-plug. We've started using Haskell at Vacation Labs and we even spoke about it at Functional Conf 2017, Bangalore [1]. The talk was called "Joys & frustrations of putting 34,000 lines of Haskell into production" -- so it wasn't only fan-boyism!
Like Ruby and Erlang, Haskell is used a lot for building server-side applications. It's runtime offers N*M threading and software transactional memory, which make it an attractive choice to many. It's webserver, Warp, performance-wise is competitive with Nginx.
Your list should include Standard Chartered and Barclays. Standard Chartered probably has the largest commercial Haskell codebase in the world, it has over 30 full-time Haskell developers and many more users.
They call it a Haskell codebase, but as far as I understand it's their own fork of Haskell 98, that is strict not lazy, with quite a few modifications.
That might have the same appeal for some, but as much as I like Haskell, that sounds like an ancient legacy enterprise monolith to me.
hello! i worked there for 2 years. yes it is a fork called Mu, no it is not not lazy. The Mu Core team are devout Haskellers so don't be too quick to dismiss them as abandoning Haskell's core values.
Thanks for the clarification! I never thought they were not devout Haskellers, but I do always wonder about projects that fork a more standard thing for internal use ending up as poorly maintained legacy software.
They didn't fork GHC. They wrote a new Haskell compiler to target a pre-existing runtime that existed inside the bank. For various reasons related to the runtime that meant the language had to be strict. Mu's creation was, in fact, a step away from legacy.
There is plenty of GHC Haskell in the codebase. It's very common for banks to have proprietary in-house scripting languages. Mu, is at least based on a language standard and is not some ad-hoc design.
The codebases of Facebook and Google would be no less "ancient legacy enterprise monolith".
> It's very common for banks to have proprietary in-house scripting languages
Yeah I don't really understand this. From what I've heard, Goldman Sachs for example, have systems written in a language that no one outside of GS has used, was kinda clever in the early 90s, but hasn't really evolved since and therefore is difficult to get expertise in, and that developers working with it struggle with moving to other companies as their skills are somewhat less transferable.
Edit: at least Facebook and Google open source their internal languages/tools (Go, React, etc) so that those skills become somewhat more transferable.
30 developers is either sad, or really sad. Unfortunately, if software is going to wind up restricted to specialized druids adhering to Haskell, I'm not sure that is a good thing
IMHO, sad is when you have large numbers of people hacking on a multi-million line codebase in Python with no static types, unfortunately a far more common situation in finance!
Why would it be sad? There are 30 Haskell developers at SCB, 0 Ruby developers and probably 100s of Excel developers. I don't see why one of these facts should make you sad (or happy) and not the others.
His point is obviously that they should hire more to make Haskell seem popular, even though one of the reasons they don't need more than 30 is because it's Haskell and not JavaScript.
Or OP's point was the largest company using Haskell only employs 30 Haskell devs. A Haskell proponent will say exactly, you only need a few developers with Haskell, while someone suspicious will say if this tech were truly great more people would use it
Number of haskellers in some particular company (despite your attempt at labeling it as "largest" without providing any evidence), do not prove or disprove quality of haskell.
Number of developers implying perfection of language is also easily disproved. PHP or JS are good examples, where original languages become popular because of other factors.
Also, original poster was trying to sell us single data point as measure of popularity O_O
Getting good quantitative informations about languages is hard. But claiming that X devs hired in some company implies poor quality but X+10 would imply good quality is just ridiculous.
Vanilla Ice song, Titanic song (my heart will go on), and Hansen Mmmbop might make you cringe, but people went nuts over those songs for a reason. At the time they were amazing. Php might not be the world's greatest language, but at the time it had what people needed in the way they needed it (easy to build websites). Haskell may be objectively better than Php from a language design standpoint, but there is still a LOT that keeps your average Joe/Jane developer from really learning the language (which string library to use as the seemingly default one is really slow, bad IDE support, unpredictable performance due to laziness, monads...etc). You can tell me Haskell is great all day and on paper I would agree, but due to the simple fact that in reality nobody is using it in production or hiring for it (yes some outliers exist), that tells the average person all they need to know. Where is Haskell on tiobe (yes not a perfect source I'm aware, but good enough)? I don't think it even shows up in the top 50 languages. That means drastically reduced employability to many people.
I'm definitely not bashing Haskell btw. I think it's great, but I could also understand a lot of people shying away for good reason.
Sure but these companies develop mission-critical software applications in Haskell. They are not open source for the most part, but in terms of demonstrating that Haskell is practical they are good examples.
They're just names of companies, which aren't very useful as demonstrations. If you could describe some particular problems these companies have solved in Haskell, then that would certainly be useful.
I don't see how that's a very good measure at all.
Nevertheless, in the haskell companies I've worked at, we've used haskell for network packet inspection (not programming) and marketing automation. In both, our frontend was dependent on Haskell as well: in the former, through purescript, and in the latter, directly using reflex-js.
I'm writing a NES emulator in Haskell atm. It's still in progress, so please don't judge the code too harshly :P There's lots of nastiness I mean to fix up:
The long-term goal is to run Linux on standalone VR headsets. Right now we have something working with the HTC Vive.
The Window Manager is being written in Haskell (with lots of C/C++ FFI). It's a very serious project, attempting to do something with Haskell that isn't merely useful for more programming.
I’ve written a Bitcoin payment channel server/client in Haskell. I know it’s not exactly a well-known problem, but it felt very practical to me. So there’s a lot of logic, but also quite a lot of input/output to handle (e.g. saving payments to the database)
Not trolling here: what do you mean by "not used for programming a computer"? I'm genuinely confused because I would think that all uses of a programming language qualify as programming a computer. Do you mean applications of Haskell to domains that are messier than compilers and other classic computer science problems?
This is a strange distinction to make when evaluating a programming language. I can understand an evaluation that begins like "I need a language that is useful for solving x problem" or even "problem of x type". But if you're trying to evaluate Haskell in general terms, a list of the Haskell apps targeted at a non-technical audience is a dubious heuristic.
I'm pretty sure what OP is considering is that Haskell is well known for its usage in theory heavy works (where the domain is mostly well-specified, and possibly even beautifully so), but not well known for applications where you have to make unspecified, awkward, temporary and sometimes nonsensical updates (anything that so much as breathes near a layman). And given haskell's background as a research language, it makes sense for it to be biased towards the former. So the question is, by how much?
I disagree. C++ is hard to use too (for different reasons), but tons of non-programming related applications are written in it. Most problems aren't programming related, so it makes sense to measure a PL by its effectiveness in non-programming related applications.
http://sentenai.com is a modern data historian replacement targeted at data science use-cases. Data science involves programming, so do we consider that using Haskell for something other than programming? If so, I think it disqualifies a broad range of software like database systems from being considered as "applications not used for programming a computer".
Yeah at the margin these things will always be somewhat arbitrary. I would fall on the side of data science is not programming. The purpose of data science (aka practical statistical analysis) is to perform some data transform with a purpose being to provide insight on the world. Programming (in haskell or any other language) is a tool to achieve that insight into crop yields or sales figures or economic policy effect or whatever.
How I'd go on the "used for programming question"
A database of haskell libraries - used for programming - rejected.
A database used as storage for a web site shopfront - used for a web site shop front (or data that isn't to assist a programmer doing programming) - accepted.
For many years, my employer's SDK release notes were stored as a Haskell DSL, which could be compiled and executed to produce HTML or plaintext. As far as I know, it was the only "production" Haskell in the company.
jq was originally written in haskell (then rewritten in c, mainly for a small self-contained executable, I believe).
I don't see it as a criticism that haskell is useful as a cradle in this way, just because the project later moves - it would have been much harder to start without haskell (and maybe impossible, without haskell concepts - which TBF could be acquired in other ways).
I use Haskell for a special-purpose MediaWiki parser, wikiparsec [1]. It extracts information from Wiktionary to put into the ConceptNet knowledge graph. (Does that still count as programming a computer?)
I actually do not like the Haskell ecosystem very much. I don't like the broken Prelude (standard library) that you're supposed to replace but there's no consensus on what to replace it with, and I don't like how every library introduces its own unpronounceable infix operators. I also don't even think that using the word "monad" is a good attempt at communication.
But for non-trivial LL parsing, there's no alternative. There's Attoparsec in Haskell, and there's libraries that wish they were Attoparsec.
So you are mainly interested in GUI applications for laypeople to use, right?
I think there is a simple reason for that not existing as much as you'd think or hope: writing a GUI framework from scratch is a lot of work. So what people do nowadays is try to bind to existing libraries, GTK, fltk, DOM, Qt, etc...
But that comes with a cost: Those libraries need mutation to work, and therefore feel alien, and pointless to use in an immutable functional programming context. Most of your app would need to live in `IO`, basically. Why use Haskell at that point?
But there is a way to tame this beast: Functional Reactive Programming, or FRP for short. This way one could compose pure functions, events and behaviors that drive an underlying mutating GUI library in a deterministic way.
reflex-frp comes closest to this in my experience so far, which with reflex-dom and jsaddle is able to drive a browser window through a low-latency, local websocket connection. Meaning that you can, for example, run native Haskell code and only run a very thin layer of JS to interface with the DOM. This way you can build apps that have a GUI window on the desktop (e.g. with WebkitGTK) and is much faster than Electron.
You can also build the same reflex-dom apps to Android and iOS, and I've heard others remark that due to the native code doing most of the work in the background it is able to run faster on an ARM CPU than the comparable JS on the desktop. Though I've yet to try this myself to confirm.
At any rate, I think we are getting very close to a tipping point where GUIs become easy enough to write and then you'll see more and more written with Haskell.
Edit: Another potential "attack-vector" will be WebAssembly: Instead of compiling Haskell to JS via GHCJS, you could compile it to wasm and use it on websites' front-end much more performantly. When there is a working Haskell-to-wasm compiler, (hopefully soon) you'll also likely see more and more GUIs written with Haskell on the web.
shellcheck is great, I use it, endorse it and recommend it. It is used for programming a computer, I want to exclude those applications. Pugs for the same reason without comparing the two.
I agree, this seems like an issue to me. I work on a B2C web application, and I generally love working on projects like that, but I've seen so few made with Haskell, and every time I try to start a project along those lines (be it with Yesod, Snap, Spock, Servant, etc), I find the libraries and tooling far less productive for creating those sorts of applications than, say, Django.
I think the Haskell ecosystem desperately needs a Django/Rails level web framework, including the documentation that goes along with that, because right now nothing comes close.
I've written and deployed 3 applications written with Yesod so far. I also have some experience with Ruby on Rails. I'd say the two frameworks are rather on the same level of abstraction and similar in terms of productivity in my experience. And I like the type safety Yesod is able to provide, e.g. even for routes.
My experience with Haskell for a webserver is that, yes, it's finnicky to get going. However, type checking from URL router to database [0], with esqueleto, is wonderful. With stack and docker the setup has gotten better.
Still, I don't think it's a "fair" question, because the main reason there are not many practical applications written in Haskell, is because companies prefer dynamic scripting languages. They do that to "get things done" or in other words, ship fast a product full of bugs.
data is neither fair not unfair. It is collected or not. The collection has a bias or doesn't. It's not fair to count the number of haskell applications compared to pdp-11 assembly language apps to prove the superiority of soft cheese over those hard cheese heretics! Counting however is just counting. Once counted inference can be drawn, debated, rejected, alternative hypotheses advanced. All better done in the presence rather than absence of data, no?
I just meant the interpretation of the data is not fair if you say "not many practical applications are made with Haskell, so it's not a practical language".
So yes, collect the data of "how many practical applications are there in X language", but also collect the data that show why X language is picked, and the results ( quality, bugs, maintainability, etc )
That's very hard to do though.
I did. I can't imagine anybody other than you made your interpretation, as the asking for data was clearly intended into subsidizing a specific conclusion.
My company, https://www.lumi.com (W15) uses Haskell almost exclusively on the backend. We use it to write our API and internal tools such as our custom ERP.
Thanks for bringing it up. That's a terrible list. Each use is unknown in both content and purpose and you can't verify it or really draw much in the way of conclusion from it at all. Was it an intern project that was abandoned or is it now performing core functionality. We'd wish the latter but no way to know. Have 10% of those not worked out (which would be a fabulous success rate or 99% which would be comparable to Java? </troll></just kidding></java is ok really>
Signal to noise is awful on such a thing other than to provide the confirmation bias we might be looking for. We're better than that.
Thanks for mentioning Wire! Although I guess the question was about applications and not "services". FWIW, our backend services are almost exclusively written in Haskell.
There’s also threadscope (even available in Ubuntu without adding new APT repositories: https://packages.ubuntu.com/xenial/threadscope) which is a visualization tool for debugging with Haskell event logs.
34 items on that list. Looking at the content I don't think it's fair to use it as much of a datapoint you'd have to reduce that 34 number down yet it's missing git annexe, xmonad and pandoc which exist and are well used! Also as you point out it notes that it's obsolete. Next to where it notes that the list is obsolete it links here: https://wiki.haskell.org/Libraries_and_tools
Which buries applications in libraries and makes it challenging to get the actual list of applications, which is what I want here and is just one useful datapoint (of many). There is definitely a very long list of haskell libraries, many of which are excellent - this is a separate datapoint that can be considered separately or indeed in concert once we have the actual datapoint of the haskell applications that exist, are used and used for something that isn't programming.
> A list of applications written in haskell that are used for something that isn't programming a is a useful datapoint that tells us something about the strengths and weaknesses of the language
A certain kind of strengths and weaknesses, but not the only kind. It feels like you're asking a someone who mostly makes raku pottery to show how many pieces they have sold in Walmart... Haskell certainly can be used in the way you're looking for examples, as someone could find a market for their pottery through Walmart, but I think it's ok to let Haskell be in that more artful or avant garde space.
The question here is: is Haskell an artful, avant garde language, or is it one that's effective for line-of-business apps? There is space for both kinds of language but it's worth knowing which is which.
For people that have gotten into Haskell, but struggle to get over the intermediate level, I would warmly recommend checking out https://www.fpcomplete.com/haskell-syllabus. As for good tips, I've come to like the ReaderT "design pattern"[0].
For newcomers, I would recommend the Haskell Book[1], which covers everything quite extensively.
> Writing Haskell is almost trivial in practice. You just start with the magic fifty line {-# LANGUAGE ... #-} incantation to fast-forward to 2017, [...]
This was my biggest frustration with Haskell the last time I used it, I find it sad that it doesn’t seem like they’re trying to fix it.
Haskell prime[0][1] is an effort to consolidate some of these features into "Haskell 2020" - so there is work going on in that regard.
Semi-related: I wanted to voice that while it can be tedious to enable extensions, what they do give us is a great way to explore several solution spaces without making the whole ecosystem commit to any in particular. Some become defacto, while some are more controversial and change the actual semantics of the language. The freedom to choose is both a burden and a gift though!
There are some extensions like UndecidableInstances or OverloadableSyntax which are great in specific files but which would be awful in the entire codebase.
As a Java programmer, I don’t have to have the equivalent of 75 LANGUAGE pragmas to be able to use the features in Java 8 instead of what Java 1.1 shipped with.
It’s nice there’s a way not to put them at the top of every single file but they still exist.
There must be some reasonable subset that basically everyone uses and should just be included in the language.
One of the declared goals during the initial design phase of the Haskell language
was to "... be usable as a basis for further language research." From "A History of Haskell: Being Lazy with Class" available at https://www.microsoft.com/en-us/research/wp-content/uploads/.... Language pragmas feels like a nice tool to support this goal.
I think the equivalent is packages which provide annotations. There are plentiful of such packed providing variations of "notNull". It seems even worse than extensions, as they are third party.
Getting everyone to come to a consensus on what should or shouldn't be included would be very difficult. Then there's also the issue of backwards compatibility. At the end of the day they're opt in so you can just simply not use them.
Optimizing individually for every individual has a tendency to de-optimize the community as a whole.
This is one of those things that seriously limits Haskell's growth. It's something for new users to bounce off of, and that contributes to Haskell's reputation as a write-only language.
Hiding the ways in which your code isn't really Haskell (e.g. isn't going to be compatible with other random Haskell code) doesn't strike me as a fix. Why are there so many extensions in the first place?
"Extensions" has just turned out to be a sub-optimal moniker. They're not some third-party-compiler-plugins-to-be-installed-into-GHC, they're more like (rather involved) syntax sugars you can enable if needed. Each such "language feature" amounts to a tiny sub-compiler necessitating additional "core-to-core" and "core lint" (Core being the intermediate Typed Lambda Calculus representation all Haskell syntax sugars, built-in or "extension", compile down to) passes during compilation, which in GHC feels pretty sluggish to begin with.
So in pragmatic terms, enabling/disabling "extensions" lets you prevent some unnecessary processing loads during compilation.
Obviously one can do everything in "vanilla" (no-"extensions") Haskell just like one could express everything in raw Lambda Calculus. Syntax sugars as always allow for reducing patterns of verbosity --- those provided by "extensions" tend to be such patterns-of-verbosity that don't always occur in-all-projects-all-the-time, just in enough-projects-much-of-the-time.
Most of the aversion to them seems kneejerk "but my other language had another (or none at all) approach to this" --- never understood the strong opinions about GHC extensions. "Other-language" typically also delights in a culture of repetetively verbose syntax itself and no such APIs to hack new language constructs into the compiler in a plug-and-play manner (without forking).
And yes one can enable these via cabal file or GHC -X flags, but there's something to be said for per-module pragmas: they only activate where needed, and it aids the code reader as to what non-vanilla syntactic constructs are used in the-module-at-hand.
I'm aware that they're syntax sugar. I'm saying syntax sugar is bad. It's especially bad when there are dozens of mutually incompatible sugars. It's double especially bad when the base language is already one of the most complicated ones around.
> Hiding the ways in which your code isn't really Haskell (e.g. isn't going to be compatible with other random Haskell code) ...
Haskell code with any extensions enabled is pretty much always compatible with Haskell code with any other extensions enabled.
(I only said "pretty much" to hedge my bets. I literally can't think of any case where extensions could make your code incompatible with other code across a module boundary.)
Honestly this bugs me too. I don’t really see why the community doesn’t just decide the GHC really IS Haskell.
I’m aware there are other compilers but basically everyone uses GHC. So all these things that have to be turned on because they’re not “compatible“ with all the other compilers that no one else uses just add complexity for new users (and I suppose current users.)
Yes they are but they may not be compatible with all existing code as it is presently written. For example turning on OverloadedStrings can introduce type ambiguity in existing code where the type of a string literal cannot be deduced.
Can you name some extensions that are not compatible with each other. I can't, off the top of my head, and I've been using Haskell for some years. (I don't deny there might be some, but I'm finding it hard to think of any.)
I disagree with this, I like how it is right now. For certain kinds of code I need things like FlexibleInstances that I absolutely wouldn't want enabled everywhere. As it currently stands in Haskell I can turn it on only in the files that need it and it doesn't leak to the rest of the program. And having them at the top lets me know ahead of time that things I would normally view as syntax errors are in effect for this file.
The biggest complaints I see people make about Haskell are language extention lines and imports but for me these are both IDE issues. I'm not aware of many readable languages that end up with lots of imports for any non-trivial code.
Yep. Here's an interesting list of extensions as of a couple years ago: https://www.reddit.com/r/haskell/comments/2z248l/language_ex... ... But that many extensions being pretty much necessary had definitely wore me down and turned me away from Haskell.
Some extensions are considered bad ideas later, e.g. -XImplicitParams.
Some extensions can make existing code fail to compile, e.g. -XOverloadedStrings.
Some extensions are fairly specialised and their use can cause error messages that are confusing if you don't know what the extension does, e.g. -XAllowAmbiguousTypes.
Some extensions can cause certain pathological pieces of code to suddenly start behaving weirdly, e.g. if you turn on -XApplicativeDo for a type whose Applicative instance isn't lawful, but whose Monad instance is.
Of course, many, many extensions could be enabled by default without making the UX worse or restricting the set of allowed programs at all: a large section of Haskell users is likely in favour of enabling syntactic conveniences like LambdaCase or MultiWayIf, or automatic code-generation utilities like DeriveFoldable, DeriveTraversable, and so on. That's what Haskell Prime is for, I suppose.
And lets not forget about UndecidableInstances and its ilk, that enable amazing pieces of functionality if used well, but incredibly dangerous if you are not careful
UndecidableInstances is actually not bad. I turn it on all the time and don't feel scared when I do. Worst-case, I write some bad instances and the compiler fails due to the recursion limit. IncoherentInstances is the one that really has no place since it can make code unsound.
Because sometimes you have to explore a space to figure out if it’s any good. No point arbitrarily adding stuff to the language if it doesn’t turn out to be that useful.
That’s how you end up with situations like ‘null’.
Some of these extensions are nuts, too. They can make the type system behave in ways where it basically has to multivariate solve or do things that are overly clever. You probably don’t want that most of the time..
As a semi-recent initiate to Haskell, it's really helpful to see such an accessible compilation of important Haskell developments. Although it's easy to find Haskell material to read, it's not as obvious to highlight what's important.
Haskell is definately one to watch for Blockchain. IOHK have released a currency with their codebase largely in Haskell. Both IOHK and Digital Asset have smart contract / modelling languages based on Haskell.
I'm particularly excited about the prospect of linear types; not so much for helping with space/GC efficiency (which is good of course) but for modeling state transitions and ensuring that, for instance, some intermediate state must get consumed once and only once. It's one of the bugs I often run into when writing Haskell code.
The linear type hype is a bit overblown. As one skeptical GHC developer has said, there’s not a single concrete example of how linear types would help real-life code. Haskell already has great facilities to handle state transition; linear types isn’t needed to ensure “some intermediate state must get consumed once and only once.”
"linear types isn’t needed to ensure “some intermediate state must get consumed once and only once.”"
If you don't use linear types to ensure this, then you're probably not ensuring it, and it just comes down to "hopefully the programmer eyeballed it correctly and read the docs."
More than 20 years ago people solved the token-that-must-be-consumed-once problem with the IO monad. Just a secret token that gets threaded everywhere. The monadic bind is just great syntactic sugar.
Also more than 20 years ago people figured out a way to keep separate tokens completely independent and forbid them from interacting with each other. That’s the phantom `s` type variable in ST.
Nowadays we have so many more complicated solutions (not all of them worthwhile!) giving you choice how you want to make sure an intermediate state is consumed once. Why not implement a state machine at the type variable using DataKinds, TypeFamilies et al?
Or you know, for the case of ensuring file handles/database connections get closed correctly, use monadic regions: http://okmij.org/ftp/Computation/resource-aware-prog/region-... (disclaimer: I haven’t read this paper or used this approach myself but I heard it’s a good technique)
But more importantly, even if introducing linear types can make what’s currently barely doable in Haskell easily doable, maybe even replacing five language extensions with one, does it have a good power-to-weight ratio? I’m not convinced. Is the kind of mistakes prevented by this language extension often made by typical Haskell programmers? My answer is no.
> Is the kind of mistakes prevented by this language extension often made by typical Haskell programmers? My answer is no.
The kind of mistakes often made by Haskell programmers interacting with objects not well managed by the Haskell garbage collector? Yes, absolutely. Whether there's enough Haskell programmers in that situation, I am unsure; quite possibly not, but it includes me and my colleagues, as well as those currently doing the work.
He writes: "Most large Haskell projects (GHC, Stack, Agda, Cabal, Idris, etc) no longer use Haddock for documentation."
So, what do they use? How do I get (I mean, build in stack) my useful documentation? (Actually I appreciate any tips how to build documentation for off-line use.)
My impression of Haskell is that the main issue is that laziness is simply wrong, since it causes space leaks that are hard to reason about, and seems generally less efficient than eagerness; furthermore, it seems rarely beneficial, so it seems more appropriate to explicitly request laziness rather than the opposite.
The next problem is that complete immutability is also wrong, because controlled mutable aliasing with linear types (like the system available in Rust) is what you really want since it's more general, efficient and expressive (allows mutable data structures), and complete immutability is just a special case of controlled mutable aliasing.
The third problem of Haskell is the weird syntax, that doesn't follow the common C/Java/JS or Python syntaxes for no good reason, making it hard to read and learn the language.
And if one were to change these things in Haskell, the result would essentially be Rust (or more precisely, a future version of Rust once all the missing abstractions like HKT, specialization, etc. are added), so I think that's what one should use instead.
No it's not. It's not a particularly sensible default (which you allude to) but it's not wrong. Modelling delayed resolution is very handy. For example, you'd not get far in Rust without Result<>, Option<> or Future<> which are all make use of delayed resolution for their value. And the more we move to parallel models, the more important it will be.
> because controlled mutable aliasing with linear types ... is what you really want
Again, no. It's less restrictive than pure immutability, and Rust has shown that statically managing some mutability is viable, which is great, but it's not what you really want.
What you really want is a static checker that only prevents shared, simultaneous mutability but admits everything else. The Rust borrow checker is an important step in that direction but it's not it.
> The third problem of Haskell is the weird syntax
I'm not one of life's Lisp apologists. Syntax is important, though not as important as semantics. But just because you find it difficult to read doesn't make it objectively difficult.
And if you're implying that Rust is easy to read, for a beginner, because it uses curly braces, I'm not entirely sure what to say.
I think Rust is an excellent language. I hope and believe it will be an influential language as it's a fantastic combination of innovation and pragmatism.
I'm also enormously grateful that a lot of the decisions are made in the open, I've learnt a lot and continue so to do. But it's a good language because the designers clearly have an appreciation of what has gone before. That's a good way to be.
Obviously a language needs to be capable of expressing laziness (which requires closures and some mutability), but it being the default is the issue in Haskell, since basically everything returns a closure computing the value rather than the value itself, which is really weird and unexpected, and causes unintuitive (at best) memory usage.
As for implicit parallelization with no annotations at all, the main Haskell implementations don't seem to do it, so I guess it just doesn't work (the reasons probably being that there are penalties due to caches being CPU local and the cost of synchronizing threads that make automatic micro-parallelization much worse that explicit coarse-grained parallelization, unless the micro-parallelization is done in hardware by the CPU out-of-order logic).
And obviously a type system with controlled mutability like Rust's allows to express explicit parallelization just fine (in fact, writing a safe explicitly parallelized web browser in Servo was one of the main goals of it).
> What you really want is a static checker that only prevents shared, simultaneous mutability but admits everything else. The Rust borrow checker is an important step in that direction but it's not it.
Is there any example of a static checker that allows more expressive programs than Rust's while preserving the same guarantees? (and without requiring extra annotations like programmer-supplied proofs or using best-effort automatic theorem proving). Is that even possible?
> And if you're implying that Rust is easy to read, for a beginner, because it uses curly braces, I'm not entirely sure what to say.
Rust is easier to read because the visually natural distinction between words and punctuation has the same kind of meaning in Rust that it does in prose. Reading Haskell feels like reading a paragraph without any punctuation; there's no structure to grab onto. A lot of the time you can't tell whether something is a type parameter or a value.
Also $ is utterly alien compared to any other remotely mainstream language, . means something very different from what it usually does and what it usually means is rarely used ("subject verb object" is very rare in Haskell code), the :: type syntax is obscure, the mandatory split/duplication between function declaration and definition feels weird...
> A lot of the time you can't tell whether something is a type parameter or a value.
> […] the mandatory split/duplication between function declaration and definition feels weird...
I believe you just answered your own critique: you can always tell whether something is a type parameter or a value, because type and implementation are kept separate.
They're in separate clauses but they look the same on their own. You have to look out to a few lines of context to see which is which. At a glance it's not even obvious which lines are function declarations and which are implementations.
Totally disagree and as of now I only dabbled in Haskell (though it's first on my to-do list).
1. Immutability works when everything is immutable. Otherwise you're never sure and you lose benefits.
2. Syntax is a non issue. Honestly, I stopped caring a long time ago about how language looks. You get used to it in the first month.
3. Laziness is disputable. While it can be a optimization problem, it also allows for more generic description of ideas.
> 1. Immutability works when everything is immutable. Otherwise you're never sure and you lose benefits.
That seems like a poor way to explain the concept since the entire point of programming is to mutate state and that's how the hardware actually works, too. I prefer to discuss it as tackling that uncertainty by allowing the language to indicate more nuanced contracts and enforce them, where immutability-by-default is a reasonable design which still gives you the option of using a mutable structure where necessary.
> 2. Syntax is a non issue. Honestly, I stopped caring a long time ago about how language looks. You get used to it in the first month.
I think this is a big mistake: if you want a language to be successful, that first experience is pretty important if you want big open source community with many contributors. In the case of Haskell, this is especially important since there are a number of other hard concepts which people need to learn along with the syntax and the nature of the language means that other programming experience is less helpful.
> That seems like a poor way to explain the concept since the entire point of programming is to mutate state and that's how the hardware actually works, too.
The hardware actually works using gotos, too (well, jumps in assembly language). That does not make it either ergonomic or safe to write code that way.
The entire point of programming is to compute results. In the hardware that results in mutations; in the programming model it doesn't necessarily have to.
My point was simply that saying “immutability works when everything is immutable” often sounds confusing and/or wrong because people know they need to mutate things. Saying that you're using a language with more advanced concepts to ensure that things only change in the desired manner avoids that initial point of confusion.
In Rust, if you have an & or Rc/Arc, you know that it's immutable, except for parts contained in Cell/RefCell/Atomic/Mutex.
If you have an &mut or owned mut variable, then you know that you can only mutate it through that name.
If you have a non-mut owned variable, then you know that only the Cell/RefCell/Atomic/Mutex parts can be mutated, and only through that name.
If a Rust program doesn't contain the words "mut", "unsafe", "Cell", "RefCell", "Mutex" or "Atomic[...]", then everything in it is immutable.
That gives the same guarantees as Haskell's immutability where desired, but it allows to actually make use of CPU capabilities like writing to memory, and express basic operations like assigning a value to a position in an array.
Or in other words, what you really want is a system that lets you mutate something as long as you are the only one with a reference to it, and then make it immutable and give it around, while also allowing you to "get it back" exclusively either with static checks (via lifetimes) or dynamic reference count checks.
Haskell's pure immutability system, while safe unlike Java's, is just a very restricted version of such a system, that is not powerful enough to write safe programs that are as efficient as C programs (since you can't modify memory), or express imperative code even when they are safe.
You make it sound like Haskell doesn’t have the capability to do things like “writing to memory, and express basic operations like assigning a value to a position in an array.” That’s utterly false. It’s not impossible or unusual in performance critical code to deal with mutable bytes strings and mutable arrays extensively. If you want you can just write to arbitrary locations in memory and cause segfaults in Haskell.
> what you really want is a system that lets you mutate something as long as you are the only one with a reference to it
You mean like STRef[1]? Because that is exactly what STRef does. It is worth noting that STRef is significantly safer than similar rust constructs because they deal with reference cycles. On the other hand implicit resource freeing is more awkward and you still need a gc.
This allows you to write programs that are asymptotically more efficient through mutability. Though there is a class of algorithms where lazy pure implementations gain the same benefits as impure ones using the sharing/implicit mutation that call-by-need provides.
There is an experimental linear haskell branch which might be merged into ghc proper eventually. It is stricter then rust, though, and prevents things like reference cycles which is more awkward but safer in places.
> except for parts contained in Cell/RefCell/Atomic/Mutex.
This caveat is the problem. You lose a ton of properties when you have “interior mutability” (I.e. an object typed as immutable is, in fact, mutable). You probably can’t appreciate the benefit if you haven’t actually used a purely immutable language. Rust touts “fearless concurrency”, but it’s nowhere close to what you can get with Haskell’s guarantee.
> not powerful enough to write safe programs that are as efficient as C programs (since you can't modify memory)
Wrong on both counts. The vast majority of mutable algorithms can be expressed recursively and immutably. Consider haskell’s Vector library. It generates C-equivalent output a lot of the time despite being immutable and much higher level.
The one exception I’ve run into is algorithms that inherently rely a lot on random array writes (like in-place random shuffles), and in this case Haskell has the ST monad for externally pure, referentially transparent mutability that cannot escape into the rest of your program. The fact that haskellers almost never need or want to use ST is very strong evidence that, in fact, you don’t really need mutable semantics at all once you’ve figured out what you’re doing with immutable data.
Well, you could change Rust so that the names of types with interior mutability have to start with an "@" or something like that, and then it would be obvious, and could expose a trait to denote that (there is Sync and the internal Freeze, but they don't quite do that).
I think it hasn't been done because interior mutability is relatively rarely used, and being able to assert immutability in generic code is not actually that useful (and for things like hash map keys you'd need to implement Hash or other traits manually anyway, so you'll probably notice the issue).
In non-generic code, you have to call functions like "borrow()", etc. to access the internally mutable data, so you know it's mutable.
Regarding "fearless concurrency", the Sync trait ensures that, allowing "interior mutability" only if thread-safe (i.e. Mutex or Atomic).
Obviously you can have race conditions between modifications of different Mutex/Atomics, but that's an unavoidable fundamental problem: even transactional systems will have race conditions if you use two separate transactions where you should have used one, and single-threaded event based systems also have them if you "defer" part of the computation improperly.
It would be better to rephrase the original point as "Some guarantees need to be embedded in the language itself, or otherwise you can't depend on them." Having the language itself guarantee immutability means that you can depend on it, because you know that no library will ever be written that expects you to pass things to it to be randomly mutated.
Rust has a different set of guarantees about mutability, but again, you want them embedded in the language so that you can depend on them, which you can. Even though you can escape to "unsafe" in either language, the rest of the affordances of the language prevent library authors from going too crazy violating guarantees.
That is, the important point isn't about "immutability", but about the guarantee.
Within the Haskell community, the idea that something like Rust may be closer to what we're seeking is perhaps controversial, but certainly not beyond the pale. And some of that controversy would be that Rust may not go far enough with things like linear types rather than the idea that immutability is the end goal. Personally, I think immutability was a sensible thing to try out in the 1990s, but we have legitimately made progress since then and continue to make progress.
(If you want to see a language where the 1990s beliefs about immutability really hurt it, check out Erlang. What Erlang ought to have had, in my 2017 opinion, is processes that have their own conventional mutable data storage, but with messages between processes incapable of carrying pointers/references. This would have accomplished their goal of total isolation and all communication being forced to go through copying, but without having the barrier to adoption raised by trying to teach people how to program in immutable languages, to what I found to be no great effect. Haskell does at least make some hay out of its immutability, in Erlang it is mostly just getting in the way. But back when it was written, the ideas were not as well teased apart as they are now. I don't mean this as criticism of the Erlang authors, because I think they did quite well, but this still would have improved the language.)
> My impression of Haskell is that the main issue is that laziness is simply wrong, since it causes space leaks that are hard to reason about, and seems generally less efficient than eagerness; furthermore, it seems rarely beneficial, so it seems more appropriate to explicitly request laziness rather than the opposite.
Agreed; Idris is strict by default and I expect other post-Haskell languages will be too.
> The next problem is that complete immutability is also wrong, because controlled mutable aliasing with linear types (like the system available in Rust) is what you really want since it's more general, efficient and expressive (allows mutable data structures), and complete immutability is just a special case of controlled mutable aliasing.
Disagree. Mutability is so rarely what you want that it's better to have immutability by default, and a clunkier syntax for mutable is ok.
> The third problem of Haskell is the weird syntax, that doesn't follow the common C/Java/JS or Python syntaxes for no good reason, making it hard to read and learn the language.
Yes and no. Curried-by-default is the source of a lot of Haskell's power, and that requires a certain amount of unusual ordering. The terseness of pointfree style is worth some learning time. $ is a big ergonomic advantage, though I'm not entirely convinced it's worth the cost. There are a few cheap wins, some of which Idris has (e.g. single colon for type annotations, standardised syntax highlighting) but you don't want to throw away the good stuff.
> And if one were to change these things in Haskell, the result would essentially be Rust (or more precisely, a future version of Rust once all the missing abstractions like HKT, specialization, etc. are added), so I think that's what one should use instead.
Agreed, but that thing doesn't exist yet, and the Rust that exists today isn't an acceptable substitute (it's 2017, I shouldn't have to use a language without HKT). For the moment I'd say Idris is as close as you can get.
>Yes and no. Curried-by-default is the source of a lot of Haskell's power, and that requires a certain amount of unusual ordering. The terseness of pointfree style is worth some learning time.
Correct me if I'm wrong, but you don't need Haskell's syntax to have automatically curried functions or a pointfree style. Using a JS-like syntax as an example:
function add(a, b) {
return a + b;
}
// functions could be automatically curried,
// so you could do this:
let add5 = add(5);
add5(10) // => 15
// "Normal" sum definition
function sum(list) {
return reduce(add, 0, list);
}
// Pointfree version
function sum() {
// returns a function that takes
// 1 argument (the list)
return reduce(add, 0);
}
You can do that up to a point, but you pay a cost for it. It means functions are taking up a big chunk of your available syntax space, and it can make things ambiguous if something might be seen as both a function and a value. Look at how it works in Scala: much of the time you can call a function partially, but in an unsatisfyingly random-feeling set of cases you need to explicitly use "_" expansion.
"for no good reason"? It come from the ML family of languages, that's the reason, is it not as good a reason as why Java has it's syntax? It's quite easy to read once you get used to it, no more difficult than the languages you mention.
Saying a particular paradigm is simply wrong seems like a very closed minded way of looking at things.
This list has seemed shorter than one might expect in the past given the interest among programmers in the haskell language (this includes my own interest fwiw). A list of applications written in haskell that are used for something that isn't programming a is a useful datapoint that tells us something about the strengths and weaknesses of the language - we can argue about the subtleties of the meaning of that data point but it is useful data.
I've got
to get things started. Let's get them down.