[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)