[HN Gopher] Haskell: The Bad Parts, part 2 (2020)
       ___________________________________________________________________
        
       Haskell: The Bad Parts, part 2 (2020)
        
       Author : anuragsoni
       Score  : 198 points
       Date   : 2021-02-09 14:27 UTC (8 hours ago)
        
 (HTM) web link (www.snoyman.com)
 (TXT) w3m dump (www.snoyman.com)
        
       | IggleSniggle wrote:
       | I've been meaning to learn Haskell. I appreciate this post for
       | being honest that there are warts and that there's a body of
       | "community" knowledge about "the right" and "the wrong" way to do
       | Haskell that might not be immediately evident.
       | 
       | Anyway, a good read, even for an outsider to the ecosystem.
        
         | kitd wrote:
         | This is probably just personal taste, but I found the Haskell
         | Wikibook [1] the single most useful resource when first coming
         | to the language from a typical OO background.
         | 
         | It keeps things simple and practical, how to solve typical
         | problems, etc, without getting overrun by theory.
         | 
         | Eg, it talks about 2 types of code, normal declarative and the
         | imperative-like 'do' style, when to use them and how you can
         | make them interact. Just accepting and working with that
         | without having to understand monads etc reduces the cognitive
         | load a lot. Once you're comfortable with it, it then goes
         | through the underlying theory. I appreciated that. YMMV
         | 
         | [1] https://en.wikibooks.org/wiki/Haskell
        
         | ABeeSea wrote:
         | Coming from a math background, once I had the syntax sorted and
         | could write a few simple file parsers in Haskell, "Category
         | theory for programmers" helped me understand some of what the
         | Haskell type system is trying to accomplish. (Endofunctors of
         | the category of Haskell types). I didn't and still don't
         | understand the template C++ in the book but still my favorite
         | Haskell resource. Many of the examples are written in both
         | Haskell and C++.
         | 
         | Caveat: I just started learning this last year.
         | 
         | https://bartoszmilewski.com/2014/10/28/category-theory-for-p...
        
         | cies wrote:
         | I like this book as an intro: https://haskellbook.com/
         | 
         | (since others were recommending)
        
         | dgellow wrote:
         | In case you're looking for a resource to learn more about
         | Haskell, I would highly recommend
         | http://dev.stephendiehl.com/hask/. I started recently to learn
         | the language and tooling and found this guide randomly on
         | Twitter, and it's by far the best codex of knowledge on the
         | language I've seen so far. No bullshit, straight to the point,
         | everything in one page (so easy to Ctrl-F around).
        
           | Koshkin wrote:
           | This is worth a separate submission.
        
           | als0 wrote:
           | Amazing find, thanks for sharing.
        
           | almostdeadguy wrote:
           | Easily the best reference to this I've found. I wish every
           | language had one of these.
        
         | headbee wrote:
         | Definitely check out the part one of "Haskell: The Bad Parts"
         | which is more relevant to the beginner to average Haskeller:
         | https://www.snoyman.com/blog/2020/10/haskell-bad-parts-1/
        
         | lubesGordi wrote:
         | I struggled for a long time to get past the basics presented in
         | Learn you a Haskell for Great Good. Then someone on HN proposed
         | Graham Hutton: Programming in Haskell and it definitely cleared
         | some things up for me. I recommend it!
        
       | Ericson2314 wrote:
       | I have a https://github.com/ghc-proposals/ghc-proposals/pull/351
       | in the works for the stupid partiality issue.
       | 
       | As for the library ones:
       | 
       | 1. Rust's std is better now, but will eventually sink to the
       | level of base as ideoms improve because neither language has a
       | good process for making breaking changes to the standard library
       | 
       | 2. Rust has the core---std split, which Haskell desperately
       | needs. Vector absolutely should be a separate package, but just
       | the way alloc and hashbrown are separate packages in Rust land.
       | 
       | Trying to work on that too.
       | 
       | Now that Michael Snoyman thinks that exceptions in pure code are
       | bad, and likes Rust, I wish he would think that exceptions in IO
       | code are also bad, and we should use more EitherT like Rust.
        
         | ragnese wrote:
         | Yeah. Rust's standard library already has cruft (e.g.,
         | std::error::Error deprecations), and some potential sore spots
         | (no custom allocators, Pin/Promise unsoundness:
         | https://internals.rust-lang.org/t/unsoundness-in-pin/11311).
         | 
         | I do wonder if there's some way to hide parts of the standard
         | library with the Rust "edition" mechanism. It'll stay there,
         | technically, for backwards compatibility, but any one who opts
         | in to the new edition wouldn't see deprecated stuff exported
         | from the std prelude.
        
           | Ar-Curunir wrote:
           | > I do wonder if there's some way to hide parts of the
           | standard library with the Rust "edition" mechanism. It'll
           | stay there, technically, for backwards compatibility, but any
           | one who opts in to the new edition wouldn't see deprecated
           | stuff exported from the std prelude.
           | 
           | Maybe in the future one could introduce pub(< ed2018) to
           | indicate that an item is available only before edition 2018
        
             | steveklabnik wrote:
             | This has been discussed, but it's got pros and cons, and is
             | semi-controversial. We'll see.
        
               | Ar-Curunir wrote:
               | Right, that's what folks said the last time I asked about
               | it. That's why I put a maybe there :P
        
           | nynx wrote:
           | Custom allocators are coming soon.
        
         | tomp wrote:
         | > neither language has a good process for making breaking
         | changes to the standard library
         | 
         | What language/ecosystem in your opinion _does_ have a good
         | process for making breaking changes? I 'm curious as that's
         | what I see one of the main remaining problems of language
         | design, I don't really see any viable solutions but if there
         | _are_ some, I 'd love to learn about them!
        
           | Ericson2314 wrote:
           | The solution is simple: Don't have standard libraries!
           | 
           | The compile repository should just contain the absolute bare
           | minimum of primops and what not, regular libraries should do
           | the rest. _Yes_ there should be a batteries-included starter
           | pack, and it can be bigger than most standard libraries even,
           | but it should be maintained separately.
           | 
           | Ironically, C, with it's terrible stdlib, gets this right.
           | Neither GCC or Clang is in the business of maintaining libcs.
           | C does this for Conway's law reasons with kernel vs compiler
           | competing for ownership, but still, it's a good result.
           | 
           | People squirm at the thought of this. I think the issue is
           | not that this is inherently bad UX, but that Cargo/Cabal/etc.
           | are simply too shitty. I think people's expectations of those
           | tools is too low, and if those are fixed, this problems with
           | this will evaporate.
        
             | sweeneyrod wrote:
             | OCaml does this and it's moderately annoying.
        
             | nindalf wrote:
             | Could you mention what you're lacking in cargo? Feel like
             | it's not lacking in any features, and it's really easy to
             | create plug ins for new functionality.
             | 
             | If your issue is that the Rust standard library is too
             | large, that's not a view I've ever heard before. What I
             | hear consistently is that packages that could be in the
             | stdlib like serde, rand, regex are crates pulled in by
             | cargo.
        
             | tomp wrote:
             | Ok, I understand your proposal. It definitely makes sense
             | from the _language_ evolution perspective, although to some
             | degree it 's a cop-out - codebases can still be stuck on an
             | old version (of the _non-_ standard lib) and have
             | difficulties upgrading.
             | 
             | I use Python for my day job, and the experience there is
             | illustrative - it has a stdlib which I will use with very
             | strong preference, except extremely good non-standard
             | libraries (e.g. _numpy_ and _requests_ ). The ecosystem can
             | still get stuck on that (2-to-3 transition was painful
             | partially because of ascii/unicode distinction, and
             | partially because of _numpy_ taking time to migrate).
             | 
             | But in principle I agree! Disentangling language/compiler
             | from stdlib is strictly better, as it allows the _free
             | market_ to take over, competition to flourish, and people
             | to  "vote with their feet".
             | 
             | > inherently bad UX, but that Cargo/Cabal/etc. are simply
             | too shitty
             | 
             | More strong opinions :) I like that! What are some examples
             | of good package managers in your opinion? Some people
             | praise Node/NPM, and I'm personally quite happy with
             | _conda_ for Python, but then my needs are fairly vanilla
             | and even I can see some pretty obvious improvements...
        
             | Ar-Curunir wrote:
             | Doesn't Rust mark deprecated functions as, wel, deprecated?
             | 
             | Also, the edition system (with some small extensions) in
             | theory enables removing stuff from future editions
             | 
             | Finally, there is incredible value in sharing common
             | vocabulary for some core objects like basic data structures
        
               | steveklabnik wrote:
               | > Doesn't Rust mark deprecated functions as, wel,
               | deprecated?
               | 
               | Yes.
               | 
               | > Also, the edition system (with some small extensions)
               | in theory enables removing stuff from future editions
               | 
               | In the language, but not the standard library.
        
           | xiphias2 wrote:
           | Rust is making breaking changes in the language (called
           | editions), but different versions of the language can use
           | each other as libraries.
           | 
           | They use a common IR format, just like languages targeting
           | the same VM.
        
             | Ericson2314 wrote:
             | editions sharing std good, but that also means this doesn't
             | help with the need to change std at all.
             | 
             | You have to design the institutions assume you will
             | constantly be making mistakes and they will accumulate.
             | Wishing away braking changes is like pretending buildings
             | don't need vacuuming and dusting. The second law of
             | thermodynamics would like a word with you.
        
           | nlitened wrote:
           | I like Clojure's process: thoughtfully design things to be
           | simple, then commit to never breaking compatibility. It works
           | amazingly well from what I can tell.
        
             | Kototama wrote:
             | It's amazing and I love the language but I also did not see
             | any great innovations around Clojure in the last years,
             | after ClojureScript for example.
        
       | dgellow wrote:
       | This series of article should be turned into a linter. I'm only
       | half joking!
        
       | Decabytes wrote:
       | I'm curious about the tweet that says
       | 
       | > True mastery of Haskell comes down to knowing which things in
       | core libraries should be avoided like the plague.
       | 
       | and one of the examples is foldl. foldl is used in Racket and
       | other lisps, is Haskells implementation poor? Or are there better
       | alternatives? I admit it's tricky to grok at first
       | 
       |  _Nevermind_
       | 
       | I read further down the page and my question was answered. Should
       | have actually read the article!
        
         | almostdeadguy wrote:
         | Typically, you want to use the strict form of foldl, foldl'.
         | The lazy version is susceptible to collecting a large number of
         | thunks. See here: https://wiki.haskell.org/Foldr_Foldl_Foldl%27
        
       | mcguire wrote:
       | Wow. It's almost like Rust has 20 years of programming language
       | development experience on Haskell.
       | 
       | (Note: Haskell and Rust are different languages, for different
       | uses. Haskell has many advantages over Rust (some of which the
       | author (and I) have probably complained about at some point). But
       | many of the things that Rust does right and Haskell does wrong,
       | Rust gets right _because_ Haskell did them wrong.)
        
       | madmax96 wrote:
       | I'm a relative newcomer to Haskell. I still haven't used it for
       | anything serious, but I'd like to.
       | 
       | I had experienced all of these problems. Initially, I was
       | attracted to Haskell for the promise of "if it compiles then it's
       | probably correct." I quickly discovered that isn't true, for the
       | reasons discussed in the article.
       | 
       | But I also had issues with Cabal. I couldn't get Snap to install.
       | I tried installing it in a container, still didn't work. I
       | finally figured out something that would let me build with Snap,
       | but for some reason LSP in Emacs couldn't find the snap
       | libraries, so it couldn't provide me correct feedback. And then
       | the build times. Wow. I gave up on writing that program in
       | Haskell and wrote it in Go instead.
       | 
       | I think Haskell has a lot to offer. I'd be open to trying it
       | again. Hopefully these shortcomings improve.
        
         | marcosdumay wrote:
         | Haskell has a real tooling problem.
         | 
         | At this point, I do not allow any Haskell IDE engine to manage
         | project data. Things work much smoother if you call cabal
         | directly.
         | 
         | But that's actually a low ball. I don't let any Java or
         | Javasript, or Python (except Conda) IDE manage it either. And,
         | of course, I've long gave up on anything integrated for C, C++
         | or Perl.
        
       | cies wrote:
       | I'm enjoying the read. Having written a fair deal of Haskell, I'm
       | really grateful for what leaning Haskell told me about
       | programming at large. Snoyman has written amazing libs and acute
       | articles, and I appreciate this series a lot.
       | 
       | My interest though are: what's next?
       | 
       | Can Haskell evolve? Can we move to a better standard lib? Here
       | Snoyman has put forward a great effort by releasing his classy-
       | prelude, but iirc he also stopped using it.
       | 
       | So what can be done? Could we come up with a new Haskell-spec
       | version that fixes some of these, flips some pragmas to be on-by-
       | default? I can imagine that laziness is not going out, it too
       | much at the heart of the language: or am I just assuming that?
       | But besides laziness there is a lot to fix by just setting a new
       | standard. It will help newcomers, an eventually even old
       | codebases may make the jump.
       | 
       | Some part of this article talks about partial functions. To what
       | extend can we do without? Can we build a checker [1] for that and
       | simply disallow it?
       | 
       | 1: https://stackoverflow.com/questions/42151927/what-is-
       | haskell...
        
         | jrochkind1 wrote:
         | The OP addresses your question, saying he is not advocating for
         | removing laziness, but does have some specific suggestions.
         | 
         | > I'm not advocating for removing laziness in Haskell. In fact
         | I'm not really advocating for much of anything in this series.
         | I'm just complaining, because I like complaining.
         | 
         | > But if I was to advocate some changes:                 *
         | Deprecate partial functions       * Introduce a naming scheme
         | for partial functions to be more obvious       * Introduce a
         | compiler warning to note partial function use (with a pragma to
         | turn off specific usages)       * Warn by default on partial
         | pattern matches       * Advocate strict data fields by default
        
           | colonwqbang wrote:
           | Change all partial functions from returning a to returning
           | Maybe a. Then we rename the fromJust function into the $%&!#
           | operator (name intended to suggest the exclamation made by
           | the programmer when function fails).
        
         | SkyMarshal wrote:
         | Keep Haskell as a research language imho, but perhaps create an
         | industrial-oriented subset of the language, similar to Ada's
         | SPARK [1]. Make it strict if necessary, flip whatever pragmas
         | support an industrial use case, etc., and create a separate
         | brand but which still references Haskell.
         | 
         | [1]:https://en.wikipedia.org/wiki/SPARK_(programming_language)
        
         | js8 wrote:
         | > Can we move to a better standard lib? Here Snoyman has put
         | forward a great effort by releasing his classy-prelude, but
         | iirc he also stopped using it.
         | 
         | He mentioned https://github.com/commercialhaskell/rio in the
         | 1st article, it's interesting, I wasn't aware of it. (I am
         | using classy-prelude but I might try it out.)
        
         | ivanbakel wrote:
         | >Some part of this article talks about partial functions. To
         | what extend can we do without? Can we build a checker [1] for
         | that and simply disallow it?
         | 
         | You don't need an advanced checker for partial functions - the
         | link you use is discussing proving termination, but total vs
         | partial functions just requires checking whether the function
         | is defined for all arguments, which is easy and something that
         | GHC can already do.
         | 
         | And to be honest, I'm not sure we _can_ do without. Partial
         | functions and `error` (or `undefined`) are necessary escape
         | hatches for the programmer who knows something the compiler
         | doesn 't. Partial functions should probably not end up in any
         | APIs (and definitely not in the Prelude), but they are still an
         | important part of the language.
         | 
         | Only the introduction of dependent typing has the possibility
         | of really eliminating partial functions for good.
        
           | Ericson2314 wrote:
           | The issue is not "undefined" and "error" but accidental
           | partiality.
           | 
           | `head` absolutely _doesn 't_ know something the compiler
           | doesn't, because there is no guarantee the user is using it
           | correctly!
           | 
           | The fact that https://doc.rust-
           | lang.org/std/vec/struct.Vec.html#method.pop returns Option<T>
           | really says it all. We're not trying to be perfect, we're
           | just trying to be no worse than Rust, which shouldn't be a
           | high bar at all.
           | 
           | I wrote https://github.com/ghc-proposals/ghc-
           | proposals/pull/351 to address just these sorts of issues, and
           | not the harder ones people confuse them with.
        
             | ivanbakel wrote:
             | > The issue is not "undefined" and "error" but accidental
             | partiality.
             | 
             | I don't understand what you mean by this.
             | 
             | >`head` absolutely doesn't know something the compiler
             | doesn't, because there is no guarantee the user is using it
             | correctly!
             | 
             | But the user _can_ use `head` when they know that the list
             | they are handling is non-empty (but then they can also opt
             | for `Data.List.NonEmpty`). I never said the partial
             | function itself carried any knowledge - only that it was a
             | necessary escape hatch for cases where the programmer has
             | the knowledge to use it correctly.
             | 
             | >We're not trying to be perfect, we're just trying to be no
             | worse than Rust, which shouldn't be a high bar at all.
             | 
             | I'm not opposed to that - and I don't see why you would
             | think that I am. I'm just arguing that, in response to the
             | question made by the other commenter, we can't "do without"
             | partial functions entirely. That doesn't mean I believe
             | they belong everywhere, and certainly not in most APIs.
        
               | cies wrote:
               | > I don't understand what you mean by this.
               | 
               | I think that GP tries to say the escape hatches are not
               | the problem. "error" and "undefined" are fine (we can
               | check for undefined not being in production code, and
               | errors, well, could be implemented as a "die" or "halt").
               | 
               | But head though... It should return a Maybe wrapped
               | value! And as you say the other option is nonempty lists
               | a.k.a. "(a, [a])".
               | 
               | > only that it was a necessary escape hatch for cases
               | where the programmer has the knowledge to use it
               | correctly.
               | 
               | Well the docs do not present head as an escape hatch out
               | of Haskell iron type system! It's documented rather
               | innocently [1] (search for head). Unlike
               | unsafePerformIO[2] and the likes, which have whole
               | epistols detailing their danger.
               | 
               | > I'm just arguing that, in response to the question made
               | by the other commenter, we can't "do without" partial
               | functions entirely.
               | 
               | Please consider escape hatches differently from basic
               | stuff like head and you see we argue for the same end
               | result. Head is plain bad, should be removed or annotated
               | as the smelly thing it is. maybeHead and non-empty-
               | collections are the way fwd there, would you agree?
               | 
               | 1: http://hackage.haskell.org/package/base-4.3.1.0/docs/P
               | relude...
               | 
               | 2: https://hackage.haskell.org/package/base-4.14.0.0/docs
               | /Syste...
        
         | massysett wrote:
         | Laziness - or more precisely, non-strictness - is the essence
         | of Haskell. Haskell was created because there were a bunch of
         | non-strict languages out there, and folks saw a need for one
         | non-strict language to rule them all.
         | 
         | Often a defining feature of Haskell is thought to be its
         | purity. Purity arose from non-strictness. Monadic IO arose from
         | non-strictness (other solutions to IO were also tried.)
         | 
         | This is best discussed in a paper:
         | https://www.microsoft.com/en-us/research/wp-content/uploads/...
         | 
         | I guess you could make Haskell strict by default...but why? It
         | wouldn't be Haskell anymore. Non-strictness is the defining
         | element of the language. In my view a strict-by-default Haskell
         | is...something else. Maybe it's better for certain things.
         | Maybe it takes things to the next level. But it isn't Haskell.
         | Don't call it that.
        
           | cies wrote:
           | I understand the historic context of laziness, and what it
           | "brought" or "taught" us. But is it really that important
           | now, or moving forward? I side with Snoyman in the "strict by
           | default would probably be better".
           | 
           | Sure it's no longer Haskell then. But could there not be a
           | language like Haskell leveraging strict by default? Possibly
           | even on the GHC?
        
             | cycloptic wrote:
             | Isn't that basically every other ML variant?
        
               | wk_end wrote:
               | Not exactly. Syntax-aside, MLs in general: have better
               | module systems than Haskell, no typeclasses, and aren't
               | pure.
        
             | pera wrote:
             | Idris for instance is strict "by default" but still
             | supports laziness through the Lazy data type.
        
               | cies wrote:
               | Forgot about Idris.
               | 
               | Elm, PureScript, OCaml (Reason/ReScript) and F# did come
               | to mind.
        
           | rtpg wrote:
           | I think what's being proposed around "strict data fields by
           | default" (or, an alternative or "some new concept that evokes
           | strictness where it makes sense") could let us continue to
           | use Haskell and its laziness.
           | 
           | This isn't really obvious to me but I think you could build a
           | mixed reality without splitting a community up.
        
           | mst wrote:
           | "strict-by-default" isn't really the proposal here.
           | 
           | Only "some things' laziness is more a footgun than a feature,
           | maybe we should consider fixing those".
           | 
           | The foldl/sum/product example in part one is maybe clearer
           | about that.
        
         | 3np wrote:
         | Do you think there's any hope of Haskell community moving to
         | Idris? It's years since I did either but I think Idris is just
         | a Better Haskell without several of the issues brought up.
        
           | lupire wrote:
           | For small programs, but not in general. Haskell has too many
           | more language features and libraries.
        
         | gizmo686 wrote:
         | GHC already supports strict-by-default
         | 
         | https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/stri...
         | 
         | Obviously making it the default would be backwards
         | incompatible. But, as long as lazyness remains supported and
         | simple to use on a per-case basis, it doesn't actually seem
         | like that big of a change to me.
         | 
         | Also, the fact that GHC supports so many extensions actually
         | gives a good path to updating the language. Since extension are
         | enabled on a per-file basis, in most cases old libraries would
         | just work with new-Haskell as long as the build system knows to
         | build them in old-Haskell mode.
        
           | nightski wrote:
           | I worked with Haskell for a few years and have a book on
           | immutable data structures (Okasaki) in my book shelf here.
           | From what I remember, with immutable data structures strict
           | by default is actually a pretty bad default (please correct
           | me if I remember wrong). I can only imagine this is one of
           | the reasons Haskell is lazy by default (seriously, it's more
           | than just that they wanted to try this crazy thing).
           | 
           | Just enabling strictness everywhere in Haskell isn't just
           | inherently better. Laziness is actually quite good (and the
           | OP even says this to an extent). It's more nuanced than it
           | seems.
        
             | gizmo686 wrote:
             | For small amounts of data, strictness is better then
             | laziness. There is a simmilar lesson even in non-pure
             | languages: copying small amounts of data is almost always
             | faster than fancy scheme you want to do to avoid copying.
             | 
             | For large amounts of data in an immutable structure,
             | laziness is pretty much essential. The theory behind
             | strict-by-default is that most of the time, you are not
             | dealing with large amounts of data; even if you have
             | pointers to large data, or pointers to pointers of large
             | data. Even though laziness is vital to a pure language, you
             | only need to use it in a relatively small number of places.
        
               | dwohnitmok wrote:
               | Wait why is laziness vital to large data structures? Even
               | with strict functions on strict, but persistent
               | functions, you almost never copy the entire data
               | structure.
               | 
               | For example a strict function on a strict persistent list
               | that swaps the head of the list doesn't have to copy the
               | entire tail of the list.
               | 
               | The thing that laziness enables is short-circuiting, so
               | e.g. a fold over the list can bail out early if
               | necessary. But this is actually a minority of use cases
               | for large data, since we often bound the size of data
               | structures with something like a `take n` function for
               | some integer n, which only processes n elements in a
               | strict language.
        
             | lupire wrote:
             | Data should be strict, logic should be lazy. It's the
             | programmer's job do use judgment to decide which parts of
             | which data structures are data vs logic. One heuristic is
             | that anything recursive should be lazy, and the rest strict
             | (Tree = Leaf !Int | Node [Tree]))
             | 
             | and if you really need a lazy int in there, delay it
             | yourself using partially applied functions.
        
               | nightski wrote:
               | Yea, I agree with this and it's a good pattern in the
               | case you know the type is "Int" or some other primitive.
               | But Haskell thrives on type variables and data structures
               | are usually defined generically making it more
               | complicated.
               | 
               | From what I remember to get around this one would use a
               | type class to define a data structure's operations, and
               | then provide specific implementations of that data
               | structure for specific strict data types. But that is
               | more complicated than just having the compiler apply
               | strictness everywhere.
        
         | mumblemumble wrote:
         | > Can Haskell evolve?
         | 
         | Should it? Haskell has already shown an impressive ability to
         | evolve. Evolution is how it got where it is today. Language
         | design analogues of the blood vessels being on the wrong side
         | of the retina and all.
         | 
         | If the goal remains to have an effective research language with
         | a more-or-less unsurpassed capacity for evolution and pushing
         | the state of the art in interesting directions, I'd say Haskell
         | is still great just as it is. If, on the other hand, the goal
         | is to have a productive industrial programming language that
         | encompasses all of Haskell's great ideas, but without quite so
         | many troublesome language warts, evolutionary vestiges, or {-#
         | LANGUAGE
         | EverythingInterestingThatsHappenedOverThePastThirtyYears #-},
         | it might be better to make a clean break.
        
           | agentultra wrote:
           | GHC is a production-ready compiler (for Haskell) suited for
           | industry use. I work on a growing, healthy team and we ship
           | an application written in Haskell several times a day. It has
           | decent tooling, which is improving, and the language itself
           | is what makes it possible to tolerate the pace of those
           | developments.
           | 
           | Haskell isn't a pure "research-only" language. I realize
           | people like to point out that it's original goal was
           | research... however the nature of that research has changed
           | over time. Take Galois' _Crucible_ project where they used
           | advanced dependent-types in order to write their security
           | analysis tool [0]. They could have used research-grade
           | dependently-typed languages that are much more expressive and
           | easier to use... but they chose Haskell because they needed
           | an industrial-grade compiler capable of producing fast code
           | to run in a production setting. The kind of research being
           | done in GHC and Haskell these days is bringing innovation and
           | advancement in functional programming into industrial
           | applications.
           | 
           | Every compiler is going to have warts. Especially with people
           | using it and depending on it for industrial use. That's a
           | good thing. You could be like _Lean 4_ which will make no
           | promises of backwards compatibility or consideration for
           | industrial users in the name of staying purely for research.
           | 
           | Although I agree that Haskell is impressive in its ability to
           | evolve and grow! Linear types just landed in GHC 9.0.1 and
           | many fine folks are improving the compiler to make way for
           | dependent types. It's good stuff!
           | 
           | And to see languages like Java, C#, C++, and others pick up
           | on the low-hanging fruit of FP languages is a sign that the
           | paradigm is gaining popularity and adoption: ADTs, lamdbas,
           | type inference, pattern matching... Maybe in 20-30 years will
           | see these languages adopting higher-kinded types, rank-n
           | types, GADTs, and more?
           | 
           | [0] https://www.researchgate.net/publication/334751646_Depend
           | ent...
        
             | jcelerier wrote:
             | > Maybe in 20-30 years will see these languages adopting
             | higher-kinded types, rank-n types, GADTs, and more?
             | 
             | it's possible to express those in C++ pretty much since
             | templates exist.
        
             | haskellandchill wrote:
             | Growing healthy Haskell team? _Checks notes_ oh I already
             | applied :)
        
               | roflc0ptic wrote:
               | Have you tried Digital Asset?
        
               | haskellandchill wrote:
               | Yes, general rule is if there's a Haskell job I've at
               | some point applied for it.
        
               | lambda_obrien wrote:
               | I want to work in Haskell, do you have any other
               | suggestions for companies?
        
               | haskellandchill wrote:
               | Bitnomial, College Vine, Sentenai, Mercury, and Co-star
               | are Haskell companies I've applied to recently. There's a
               | different set if UK or EU is an option.
        
               | roflc0ptic wrote:
               | IOHK, Lumi are two others
        
             | lambda_obrien wrote:
             | Has anyone that you know of started using linear types for
             | anything interesting yet?
        
             | why_Mr_Anderson wrote:
             | Is there _anything_ of any significance written in Haskell
             | outside academia?
        
               | lalaithion wrote:
               | Shellcheck - https://www.shellcheck.net/ PostgREST -
               | https://postgrest.org/en/v7.0.0/ Semantic -
               | https://github.com/github/semantic Pandoc -
               | https://pandoc.org/
        
               | dgellow wrote:
               | Depends your definition of "significance" but Cardano is
               | written in Haskell, and its main smart contract language
               | is also Haskell.
               | 
               | https://cardano.org/
        
               | T-R wrote:
               | Facebook's spam filtering:
               | https://engineering.fb.com/2015/06/26/security/fighting-
               | spam...
        
               | agentultra wrote:
               | They also occasionally contribute libraries like _Haxl_
               | [0] and _HsThrift_ [1]
               | 
               | [0] https://hackage.haskell.org/package/haxl
               | 
               | [1] https://engineering.fb.com/2021/02/05/open-
               | source/hsthrift/
        
               | mumblemumble wrote:
               | Pandoc comes to mind.
        
               | tome wrote:
               | It might help if you define "of significance".
        
         | willtim wrote:
         | IMHO Haskell is missing three features that would really
         | improve programming in the large:
         | 
         | 1) better structural types. For example, polymorphic extensible
         | records and variants. These could even be the basis for all
         | algebraic data types. Current encodings have poor syntax and
         | poor type inference (due to non-injective type families). The
         | need to make all records nominal types really gets in the way
         | when dealing with structured data. Python is the main
         | competitor here; and so we could also just use strings and
         | maps, but we can do better.
         | 
         | 2) a better module system. Even Miranda had a better module
         | system than Haskell. OOP has first-class modules.
         | 
         | 3) a better commitment to backwards compatibility. There have
         | been controversial and breaking changes to Haskell's standard
         | libraries that have done more harm than good. This has likely
         | damaged industrial adoption of Haskell. For example, my
         | employer, a prominent Haskell sponsor, is stuck on a 7-year old
         | version.
         | 
         | Unfortunately the above problems are difficult to retrofit for
         | and so I think a new language may ultimately be needed.
        
           | Ericson2314 wrote:
           | You realize 3 would make 1 and 2 harder or less valuable? And
           | 3 would have also prevented a gazillion 1s and 2s from being
           | still with us?
           | 
           | I think breaking changes are highly underratted and people
           | only shy away from them because the tooling expectations in
           | all languages are so rock bottom.
        
             | willtim wrote:
             | > You realize 3 would make 1 and 2 harder or less valuable?
             | 
             | Yes that is why I think (1) and (2) need a new language.
             | 
             | > And 3 would have also prevented a gazillion 1s and 2s
             | from being still with us?
             | 
             | I disagree. Most new features have been implemented as
             | extensions that must be enabled with language pragmas. This
             | is not the same thing as large breaking changes in the
             | standard libraries.
        
               | IngoBlechschmid wrote:
               | Isn't constructing a new language the ultimate breaking
               | change :-)
        
       | steelheadfly wrote:
       | The Good Parts book by Crockford had its' focus on the language
       | features (as opposed to prevailing conventions or the failures in
       | the standard library). Not sure if this lived up to the spirit
       | 100%. But outside of that, great stuff! (:
        
       | brokencode wrote:
       | Speaking as an outsider to Haskell, I have to say that while its
       | core purely functional ideas are a little hard to wrap my head
       | around, what daunts me the most is the incredible number of
       | different ways there are to do everything, some recommended, some
       | relics.
       | 
       | You have to ask so many questions when you start learning
       | Haskell:
       | 
       | Should I use an alternate prelude? What string type should I use?
       | Should I use lens? What package manager and build system should I
       | use? What IDE plug-in works the best? What language extensions
       | should I use? Should I make use of laziness, or try to avoid it?
       | Are linear types a thing yet and should I use them?
       | 
       | And on and on the questions go.. these aren't questions that move
       | forward the product, but just an endless list of boring details
       | to figure out.
       | 
       | I'd love a new version of Haskell with all the incredible power
       | of GHC but without the standard library cruft. With all the best
       | extensions picked out and on by default. With a wonderfully
       | thought out stack and set of recommendations, along with a clear
       | guide describing all of this, similar to what the Rust ecosystem
       | has.
       | 
       | In short, Haskell to me seems like a playground of interesting
       | ideas rather than a coherent ecosystem for building software.
       | Which I think is true since it's a research language, but that's
       | what stops me from using it.
        
       | swagonomixxx wrote:
       | I'm not sure I understand the relationship between partial
       | function and exception handling. Aren't partial functions just
       | curried? One or more arguments are bound, but not all? At least
       | in Python, if you partial-ify a function that raises an
       | exception, it still raises. I don't understand if the author
       | likes that behavior or doesn't. Maybe this is some Haskell
       | implementation detail that I'm not aware of.
       | 
       | Last time I wrote any non-trivial Haskell was in 2014, so a long
       | time ago, but I found that my biggest problem with it at the time
       | was the really huge variety of Haskell in the wild. If you're
       | doing simple stuff, you probably stick to the prelude and you'll
       | be happy. But if you're doing anything that's a bit complex,
       | you'll end up seeing hundreds of mini-dialects of Haskell in the
       | wild, so much so that I found it really difficult as a newcomer
       | to understand code on the net. In many cases it's almost like a
       | different language completely, what with the user-defined infix
       | functions, tons of currying everywhere, laziness, and the like,
       | made it very difficult to follow code paths.
        
         | UncleMeat wrote:
         | "Partial function" is an overloaded term. In this context it
         | means "a function which does not map its entire domain to an
         | output". This means that there are some inputs which return
         | "Bottom", which happily gets propagated through the system
         | until it is needed as input to some function and then your
         | application explodes.
         | 
         | The downside here is that rather than blowing up your
         | application immediately upon a bug it blows up your application
         | somewhere else depending on your logic.
         | 
         | This can be a useful thing. You can write powerful and elegant
         | algorithms that avoid error management because the bottom
         | values never actually get used. But most people aren't doing
         | that and instead these are time bombs.
        
           | cies wrote:
           | > This can be a useful thing. You can write powerful and
           | elegant algorithms that avoid error management because the
           | bottom values never actually get used.
           | 
           | Agreed. This would be with some escape hatch function, maybe
           | even from Unsafe.
           | 
           | But having head in Prelude, without huge warning in the docs,
           | without deprecation warnings, it just, well, not very
           | Haskelly, I'd say.
        
           | codesnik wrote:
           | when bottom is actually reached that point of evaluation,
           | does haskell provide any indication on where it came from?
           | stacktrace or something?
        
         | NovemberWhiskey wrote:
         | > _Aren 't partial functions just curried? One or more
         | arguments are bound, but not all?_
         | 
         | No; a partial function is one that isn't well-defined for the
         | whole of its domain. So, as per the article, _head_ is a
         | partial function because its type signature of [a] - > a
         | implies that all arrays have a head value. But head [] does
         | not. It's a partial function.
        
           | mhotchen wrote:
           | What approaches and tools do Haskell developers take to guard
           | against this? I assume in the head case a Maybe would be a
           | better return type? But then why doesn't the Haskell core do
           | that in the head function?
        
             | NovemberWhiskey wrote:
             | In my experience (which is quite dated at this point; my
             | Haskell usage is back to the turn of the millennium), the
             | usual approach was pattern matching. i.e. if you knew you
             | were going to use a function that might not be defined you
             | would write an alternate case.
             | 
             | The type system isn't helping you at all there.
             | 
             | The feeling I had was that much of the prelude stuff was
             | there to provide for beautiful, terse examples of
             | functional programming and less to protect a software
             | engineer.
        
               | mhotchen wrote:
               | Few interesting things in this comment, thanks! What you
               | say makes sense; it sounds like Haskell can't literally
               | hold my hand for me which is fair enough.
               | 
               | I've just started my Haskell journey and the undefined
               | paths through partial function implementation caught me
               | by surprise.
               | 
               | Just skimming Wikipedia it looks like I would want to use
               | "total/strong functional programming" but apparently
               | "total functional programming is not Turing-complete"
               | 
               | https://en.wikipedia.org/wiki/Total_functional_programmin
               | g
               | 
               | Also found this on the Haskell site after some more
               | googling:
               | 
               | https://wiki.haskell.org/Partial_functions
        
         | samthecoy wrote:
         | Partial functions are not the same thing as "partially applied
         | functions". Partial functions means that not every element of
         | the domain is mapped to an element of the range, for example:
         | divTenBy :: Double -> Double         divTenBy n = 10 / n
         | 
         | If you actually call the above function you get a runtime
         | exception. We really don't like functions that do this; they
         | are called partial.
        
           | a_wild_dandan wrote:
           | Ah, so partial functions aren't onto (i.e. surjective).
        
             | [deleted]
        
             | curtisf wrote:
             | No, they are not _functions_ in the mathematical sense. It
             | 's not that they don't cover the output space, they don't
             | cover the _input_ space.
        
             | wetmore wrote:
             | Not exactly, partial functions can be surjective, eg
             | 
             | f :: Int -> Bool
             | 
             | f 0 = True
             | 
             | f 1 = False
             | 
             | f n = f (n + 1)
             | 
             | is surjective onto Bool but also partial (doesn't return
             | for n > 1). In Haskell we say that when a function doesn't
             | return, the output is bottom, written as [?].
             | 
             | You could say that a function f : A -> B is partial if
             | f^{-1}(B \ [?]) is surjective.
        
             | [deleted]
        
           | Athas wrote:
           | Am I missing something subtle? Why would you get a runtime
           | exception if you call this function?
        
             | jsmith45 wrote:
             | The parent missed a part. If you call it with 0 you get an
             | exception, because division by zero obviously.
        
             | the_af wrote:
             | If you call it with argument 0 you get a runtime exception,
             | because it results in a division by zero.
             | 
             | It's "partial" because it's not defined for 0.
        
               | magicalhippo wrote:
               | If Double is IEEE 754 compatible then it should be
               | perfectly defined for 0, you'd get +/- infinity. There
               | are algorithms which rely on this.
        
               | cies wrote:
               | So it should have been: Double -> Maybe Double
               | 
               | That would be the "right way" to fix this.
               | 
               | (or create a type NotNilDouble, lol)
        
               | the_af wrote:
               | The "right" way for division by zero is controversial,
               | though maybe that would be a solution.
               | 
               | It's clearer for partial function "head :: [a] -> a",
               | which takes the first of a list if it exists, and
               | explodes without dignity if the list is empty (this is
               | what makes head partial).
               | 
               | A proposal is "head :: [a] -> Maybe a", so head returns
               | Nothing when the list is empty.
        
               | bidirectional wrote:
               | Or, controversially, define 1/0 = 0.[1]
               | 
               | [1]: https://www.hillelwayne.com/post/divide-by-zero/
        
               | cies wrote:
               | Enough controversy in defining std libs as it is :)
        
         | augusto2112 wrote:
         | A partial function is a function where all inputs have an
         | output. For example, calling head on an empty list will throw
         | an exception. To make this a total function you'd need to
         | return a Maybe instead.
        
           | samthecoy wrote:
           | *not all inputs
        
             | augusto2112 wrote:
             | Exactly, meant to say that :)
        
         | masklinn wrote:
         | > I'm not sure I understand the relationship between partial
         | function and exception handling. Aren't partial functions just
         | curried?
         | 
         | You're thinking "partially applied function", which uses very
         | similar terms but means something completely unrelated.
         | 
         | A partially applied function is a function which is applied to
         | a subset of its formal arguments, yielding a function taking
         | the leftover arguments.
         | 
         | A partial function is contrasted with a total function and the
         | term is about the relation between inputs and outputs, namely
         | does _every possible_ input value yield an output. The example
         | of `first` used in the essay is pretty common because it 's
         | quite clear: given `first :: [a] -> a`, what happens if you
         | call `first` with an empty list? Well it can't succeed, it
         | can't just give you an `a` out of nowhere because it doesn't
         | have anything to do that. So despite an empty array being a
         | possible input value, there is no output for it: it is a
         | partial function, it only partially maps its inputs to its
         | outputs.
         | 
         | `first :: [a] -> Maybe a` would be total: in the case of an
         | empty input it returns `None`, otherwise it returns `Some a`.
        
           | Serow225 wrote:
           | Ah thanks, I'm used to this being called a "complete
           | function".
        
           | yjh0502 wrote:
           | `Just a` / `Nothing` in case of Haskell :)
        
       | jamwt wrote:
       | > And if I write a "Rust: The Bad Parts", believe me, I'll be
       | mentioning panicking.
       | 
       | This is why `panic=abort` should be the default, and the whole
       | unwind-and-try-to-keep-the-world-sound path should be opt-in.
       | Then panic is truly like `assert` and I'm guessing most of his
       | objections would be gone.
       | 
       | My guess about default-panic behavior being unwind is rust's
       | origins in the servo project. When you're part of a very large
       | monolith that should try very hard not to crash (a browser), you
       | will put some work in to try to make this unwinding okay. Yes,
       | tests still want panic to unwind, but you could opt in to this
       | to, or change the default in a `[test]` context, or a bunch of
       | other things I'm sure smarter folks could argue in an RFC. But
       | getting correctness right in prod should be goal #1 IMO, so it
       | should bias toward abort.
       | 
       | For most places rust is probably actually used today (server-
       | side), crashing is the safer and simpler behavior, and things
       | like lock poisioning are not things you need to reason though.
       | 
       | I know the article is about Haskell, so not trying to derail it,
       | but I have a really similar Haskell -> Rust path in my
       | background, so a lot of the rest of Michael's reactions here are
       | just +1 for me. For example, yes, exactly this about partial
       | functions.
       | 
       | And, IMO, laziness, which he hints at in this section. The
       | default should be the other way. Nothing worse than an `error`
       | that's fired in some unexpected place/time due to lazy
       | evaluation, and some thunk landed somewhere technically correct,
       | but infuriating. Trying to figure out what the heck is going on
       | can be really challenging/frustrating (as of my prod experience
       | in Haskell 8-11 years ago, not sure what's gotten better/worse
       | since then in ghc-land.)
       | 
       | I learned a _ton_ from Haskell, and am so glad I used it in depth
       | for awhile (ditto ML). But these days, to actually build
       | something I want to build with long-term value in mind, either
       | individually or as part of a team, I just use Rust. I get most of
       | what I loved about Haskell without the annoyances.
        
         | kevincox wrote:
         | > the whole unwind-and-try-to-keep-the-world-sound path should
         | be opt-in
         | 
         | The unfortunate thing is that if this was the default even less
         | code would be ready for it. The only way to make catching
         | panics have any hope of working is to have it default-enabled.
        
       | munk-a wrote:
       | So if I'm a PHP developer that actively uses lazy evaluation
       | patterns in his day to day that means I'm... best of both worlds?
       | (Hint: I am!)
       | 
       | Lazy values are a very powerful tool in the context of responding
       | to requests as an intermediary between a client and a database
       | due to how neatly you can reduce your peak memory usage - a lot
       | of web-stuff follows the basic pattern of
       | 
       | 1) Accept request
       | 
       | 2) Figure out query to send to DB
       | 
       | 3) Sent results to the user
       | 
       | Since results aren't actively scanned by the server in many cases
       | the goal of being able to pass-through data without directly
       | exposing any of your internal guts to the client is a noble one
       | to pursue.
        
         | steelheadfly wrote:
         | Yeah the critique about laziness here lives in a different
         | context. Lazy eval is great, don't worry about it. :)
        
       ___________________________________________________________________
       (page generated 2021-02-09 23:00 UTC)