[HN Gopher] Haskell: A Great Procedural Language
___________________________________________________________________
Haskell: A Great Procedural Language
Author : kqr
Score : 208 points
Date : 2025-01-19 05:50 UTC (17 hours ago)
(HTM) web link (entropicthoughts.com)
(TXT) w3m dump (entropicthoughts.com)
| RobotToaster wrote:
| Somewhat tangential, but the only software I know of that's
| written in Haskell is ImplicitCAD https://implicitcad.org/
| weikju wrote:
| Shellcheck is another useful one (linter for shell scripts)
|
| https://www.shellcheck.net/
| tmountain wrote:
| Haskell is an amazing type checker that's occasionally used
| for writing software. ;-)
| thesz wrote:
| This thing is awesome!
|
| I tried it on couple of one liners and it found a couple of
| potential problematic points, one for each one liner.
| Vosporos wrote:
| ShellCheck is an essential part of my projects' CI, couldn't
| do without it!
| MaxRegret wrote:
| Don't forget Pandoc!
| f1shy wrote:
| I had no idea it was Haskell. So much for what I heard "no
| practical use". Pandoc seems like very real life practical
| Application for me.
| sudahtigabulan wrote:
| And Pandoc's author is not even a software professional.
| He's a philosophy professor. :^)
| tombert wrote:
| I've said for awhile that Pandoc is one of the very few
| Haskell programs that isn't exclusively used by Haskell
| programmers.
|
| There are plenty of interesting Haskell programs (e.g.
| Xmonad, Darcs), but a lot of people explicitly use them
| _because_ they 're Haskell.
|
| Pandoc, on the other hand, is useful to pretty much anyone
| who has ever needed to convert documents. It's one of the
| first things I install on most computers.
| AlgebraFox wrote:
| https://simplex.chat/
|
| Private messenger for desktop and mobile platforms. It's mostly
| written in Haskell except for UI.
| exe34 wrote:
| Having a quick look through their repos, it looks like they
| don't use Haskell for the mobile platforms?
|
| > cpp-for-mobile > Template for cross-platform mobile app
| with native UIs and C++ core
| AlgebraFox wrote:
| https://github.com/simplex-chat/simplex-
| chat/blob/stable/fla...
|
| Their flake.nix indicates they use Haskell to generate
| cross compiled shared library for Android, iOS, Mac, Linux
| and Windows.
|
| I am not expert in Nix but at high level I can see they are
| indeed using Haskell.
| exe34 wrote:
| ah that makes sense thanks!
| internet_points wrote:
| Also the server backend for the Wire messenger
| https://github.com/wireapp/wire-server
| FabHK wrote:
| Pity that Wire never took off. It combined all the
| advantages of messaging apps for a while (available on many
| platforms, e2ee, etc.).
|
| Goes to show that success is not determined by technology.
| ryandv wrote:
| The aura package manager for AUR, as well as the xmonad tiling
| WM.
| yehoshuapw wrote:
| https://hledger.org/
|
| hledger - a plain text accounting tool
| colordrops wrote:
| Xmonad, though most people have moved on to other window
| managers at this point.
| mbwgh wrote:
| Not me, because I hate change!
| ulrikrasmussen wrote:
| I still use it, it's the main reason I don't want to switch
| to Wayland. Everything looks and feels like it did 15 years
| ago, it's great!
| internet_points wrote:
| https://github.com/waymonad/waymonad (no idea if it's
| usable)
| cap11235 wrote:
| There are stronger reasons to avoid wayland. It's a
| protocol that manages to have all implementations slightly
| different, and creating an EGL context per the glxgears
| results in a hard crash, both with AMD and NVIDIA cards. I
| assume I messed up the EGL context, but why does my entire
| desktop need to crash? Xkill is a much better UX, and
| that's kinda sad. An Xorg app failing dramatically doesn't
| murder my desktop session
| bekantan wrote:
| Still on XMonad, what are some good alternatives?
| internet_points wrote:
| If you want to stay in the land of monads there is
| https://github.com/SimulaVR/Simula?tab=readme-ov-file "a VR
| window manager for Linux". Should've been called MetaMonad
| ;) but I guess that was already taken by the phylum
| metamonada, don't want to get on their bad side.
| TypingOutBugs wrote:
| I worked on Cardano which is probably one of the larger Haskell
| projects (and Agda, which lets you write proofs and compile
| into Haskell!)
| cropcirclbureau wrote:
| PostgREST and Hasura (before rewrite) are written in Haskell.
| henrebotha wrote:
| Man I really want to learn a good code-driven CAD app sometime.
| Last time I tried to look at ImplicitCAD the website was down
| so I just gave up lol
|
| CADQuery/build123d is the other big one I'm interested in.
| hiAndrewQuinn wrote:
| Everyone else is responding with FOSS, so I'll respond with
| some companies:
|
| Co-Star, the astrology SaaS, is apparently written with a
| Haskell backend. I'd love to have seen the casting call for
| that.
|
| I believe the Mercury bank also runs most of their backend
| stuff on Haskell. Functional languages in general are
| surprisingly common among financial investment firms.
|
| Some of Target's stuff is written in Haskell. I think there was
| at least one big Facebook project that was written in Haskell,
| but they may have moved away from it by now. Awake Security
| does some Haskell stuff.
|
| One thing which might be surprising is Haskell is apparently
| quite strong for general backend web dev.
| internet_points wrote:
| I maintain Haskell code for five different customers, some
| large projects, some smaller, projects of varying ages up to
| over a decade. All the projects do "server backend" stuff,
| some web frontend too. I love how secure I feel making
| changes to things I haven't touched in a while.
| _jackdk_ wrote:
| > Haskell is apparently quite strong for general backend web
| dev
|
| Yep. Mostly because of the https://www.servant.dev/ framework
| (but see also IHP, Yesod, and other frameworks). Servant lets
| you declare your HTTP API at the type level, and then it will
| infer the correct types for your endpoint handlers. You can
| also extract OpenAPI specs from it, generate clients for
| Haskell or other languages, etc.
|
| My current employer, Bellroy, uses Haskell for pretty much
| all new code and Servant for all new HTTP APIs.
| https://exploring-better-ways.bellroy.com/our-technology-
| sta... is an older post discussing the shift to Haskell.
| We've found Haskell code to be much more compact than the
| equivalent Ruby, and significantly more robust.
| hiAndrewQuinn wrote:
| Going from Ruby to Haskell is, itself, quite a good signal
| of quality for me. Start strong and end stronger. Sounds
| like you've got a good thing going!
| colonial wrote:
| Oh, hey, I purchased one of your leather phone cases
| recently. Big fan!
| MaxGabriel wrote:
| You're correct that Mercury uses Haskell for its backend:
| https://serokell.io/blog/haskell-in-production-mercury
| hiAndrewQuinn wrote:
| How could I forget Serokell, too! An Estonian software
| development firm that uses Haskell and Nix as basic
| building blocks.
|
| I think they were using Agda or something too for a while,
| but it appears I can't find what I'm thinking of on their
| site anymore. Really interesting guys if you're located in
| the Baltic states.
| astrange wrote:
| > Co-Star, the astrology SaaS, is apparently written with a
| Haskell backend. I'd love to have seen the casting call for
| that.
|
| They have a page about it:
| https://www.costarastrology.com/why-haskell/
|
| It does one of the things I find most annoying about Haskell
| programmers, which is that they think the language is magic
| just because it has ADTs.
|
| > HASKELL MAKES ILLEGAL STATES UNREPRESENTABLE.
|
| That's not true!
|
| It's especially not true for numeric programming, Haskell
| really doesn't provide good support for that. Basically only
| Ada and dependent-type languages do.
| tome wrote:
| It is most definitely true for numeric programming (and
| Haskell is _somewhat_ dependently typed). For example, look
| at this finite bit width concatenation operation:
|
| https://hackage.haskell.org/package/clash-
| prelude-1.8.2/docs...
|
| How it compares to Ada though, I could not say.
| astrange wrote:
| By "support" I don't mean that you can do it. Rather I
| meant the opposite - it doesn't stop you from doing it
| incorrectly. There isn't a lot of thought put into ranged
| integer types, floating point modes, etc.
| epolanski wrote:
| I like Haskell, but I think it suffers from the same issues
| preventing most lisps to gain any traction: every codebase is
| different and reinvents its own DSL or uses different
| extensions.
|
| Lispers hailing macros and metaprogramming cannot understand
| that power is also the very thing that makes jumping from
| project to project difficult, I have no intention of relearning
| your cleverly designed DSL or new extensions.
|
| There's a reason why Java or PHP have plenty of widely used
| killer software, while monocle-wielding lispers and haskellers
| have very little to show after many decades.
|
| It's not FP being the issue, it's just that their power
| attracts crowds interested in code more than the
| features/business/product.
|
| I don't blame them, I love Haskell and Racket but I think very
| few teams can scale and make the compromise worth it.
| kreetx wrote:
| The upside for haskell is that whenever you come to some new
| code, or return to your own old DSL-y code, the types are
| there as spikes in a cliff to help you move onward. With, e.g
| elisp, I always get a mild headache when I need to add some
| new feature to a codebase _I myself wrote_.
| colordrops wrote:
| Perl was like this, though it did have wide adoption at one
| point. But people got sick of having 20 ways to do the same
| thing so Python was born. Is there an equivalent in the FP
| world?
| internet_points wrote:
| https://tidalcycles.org/ - think ImplicitCAD but for live music
| performances?
|
| also http://detexify.kirelabs.org/classify.html was
| surprisingly useful in university
| endgame wrote:
| The other ones most people point to are https://pandoc.org and
| https://shellcheck.net
| Locutus_ wrote:
| I've used Haskell several times for implementing isolated
| 'maths business logic units in commercial backend applications.
|
| In one such system I built had the main (REST API exposing)
| backend implemented in Kotlin with a separate application in
| Haskell doing a complex set of maths driven business rules
| against GIS data to calculate area specific prices.
|
| The amount of IO on the Haskell side was fairly minimum and
| abstracted away quite nicely.
|
| Haskell allowed expressing all complexity in a way that was
| easy to audit and translate from business/data analyst
| requirements.
|
| Would do again :-) But only with the correct amount isolation
| so you can lean into Haskell's strong sides.
| IshKebab wrote:
| I think Pandoc too, but yeah it's a fairly accurate meme that
| Haskell isn't really used to make anything except Haskell
| compilers and tutorials. Last time I checked there was actually
| an exhaustive list of software written in Haskell somewhere,
| which they meant as a "look how successful it is - all these
| projects!" but is really "it's so unsuccessful we can actually
| write down every project using it".
| simonmic wrote:
| Here you go :)
|
| https://joyful.com/Haskell#What+Haskell+apps+are+out+there+%.
| ..
| IshKebab wrote:
| Oh yeah I was thinking of this list:
|
| https://wiki.haskell.org/Haskell_in_industry
|
| Imagine a "Go in industry" page. Even a "Rust in industry"
| would be laughable at this point.
|
| Ok I'm maybe being a little unfair, but you get the point.
| cryptonector wrote:
| You're missing out if you don't know about PostgREST.
| imoverclocked wrote:
| > and (c) requires a language feature (sequence points) to
| disambiguate in which order side effects get executed. With
| Haskell, we just don't have to care.
|
| Reading up to this point, I had to chuckle a bit. I have
| struggled with the Haskell type system more than I care to admit;
| It's almost never "we just don't have to care" when comparing to
| most other popular languages.
|
| That being said, this article does a nice job of gently
| introducing some of the basics that will trip up somebody who is
| casually looking through code wondering what *> and <*> and <*
| do. As usual, there is a steep learning curve because of stuff
| like this all over the codebase. If I walk away for a month, I
| need to revisit >>= vs >> and other common operators before I can
| be productive. It probably doesn't help that I never actually
| speak to a human about these concepts so in my head it's always
| ">>=" and not "bind."
| internet_points wrote:
| I try to avoid >>= and >> (or *>) because I know it trips
| people up; do-notation is more than fine. The exception is when
| parsing with one of the parsecs where you get a lot of <* and
| *> usage and all the tutorials use those symbols.
|
| But I like <|> , it feels very clear that it has a sort of "or"
| meaning.
| youerbt wrote:
| One exception for me about >>=, is instead of this:
|
| thing <- getThing
|
| case thing of
|
| writing this:
|
| getThing >>= \case
|
| Not so much because it is less code, but fewer variables to
| name.
| tome wrote:
| Yeah `>>= \case` is a really nice pattern! I use it a lot.
| behnamoh wrote:
| You can pipe into Elixir cases too, and the language I'm
| developing also allows for this and more.
|
| The difference here seems to be that \case is a lambda?
| leifmetcalf wrote:
| Elixir doesn't even need a special syntax -- it gets
| Haskell's LambdaCase as a natural consequence of case
| being just another function, and the pipe operator always
| chaining by the first argument.
|
| Haskell's >>= is doing something slightly different.
| 'getThing >>= \case...' means roughly 'perform the action
| getThing, then match the result and perform the matched
| branch'
|
| Whereas 'getThing |> \case...' means 'pattern match on
| the action getThing, and return the matched action
| (without performing it).
|
| The >>= operator can also be used for anything satisfying
| the Monad laws, e.g. your actions can be non-
| deterministic.
| yodsanklai wrote:
| > I try to avoid >>= and >> (or *>) because I know it trips
| people up; do-notation is more than fine
|
| Interesting. Probably it's just me, but I first learned
| monads using >>= (in OCaml), so at the beginning I found the
| Haskell do notation more confusing (and indentation rules
| didn't help). >>= is just a function and I understand its
| signature well. On the other hand, "do" is a syntactic sugar
| that I sometimes had to "mentally desugar" in some cases.
| youerbt wrote:
| > It's almost never "we just don't have to care" when comparing
| to most other popular languages.
|
| Struggling with Haskell type system is not an experience of
| somebody who has developed an intuition about Haskell type
| system. Granted, it is not a binary thing, you can have good
| intuition about some parts of it and struggle with others.
|
| I think they way you put it is, while technically true, not
| fair. Those "most other" languages are very similar to one
| another. It is not C# achievement, that you don't struggle with
| its type system coming from Java.
|
| This is like people struggling with Rust because of burrow
| checker, well, they have probably never programmed with burrow
| checker before.
| SkiFire13 wrote:
| > burrow
|
| FYI it's "borrow" (as in when someone lends something to you)
| not "burrow" (which is a tunnel/hole)
| agumonkey wrote:
| i'm sure burroughs liked checker
| elbear wrote:
| Starcraft player detected
| withinboredom wrote:
| I struggled with the borrow checker because I'm smarter than
| it is, not because I haven't worked with one before. Mainly I
| say "I'm smarter" because I've worked on big projects without
| it and never had any issues. Granted, I've only gotten in a
| fight with it once before giving up on the language, mainly
| because it forced me to refactor half the codebase to get it
| to shut up and I had forgotten why I was doing it in the
| first place.
| tinco wrote:
| Did you rewrite the code base into C++ to avoid the borrow
| checker?
| withinboredom wrote:
| No, I just stopped contributing to the project and never
| touched Rust again.
| maleldil wrote:
| > I'm smarter than it is
|
| Given how many important projects maintained by smart
| people have dealt with bugs that safe Rust makes
| impossible, I'm inclined to doubt that.
| withinboredom wrote:
| In my particular case, I was absolutely sure it was safe
| without having to test it. Would it remain so forever?
| Probably not because software changes over time.
| eddd-ddde wrote:
| In those cases what you are supposed to do is write your
| logic using unsafe, and wrap it with lifetime safe APIs.
| That way your "smartness" joins efforts with rusts, and
| not-so-smart people can use your APIs without having to
| be as smart.
| nyrikki wrote:
| Let's be realistic, rust is great, especially compared to
| C.
|
| But it shifts the need to memorize undefined behavior
| with the need to memorize the borrow checker rules.
|
| If you are dealing with common system level needs like
| double linked lists, rust adds back in the need for that
| super human level memory of undefined behavior, because
| the borrow checker is limited to what static analysis can
| do.
|
| IMHO, the best thing Rust could do right now is more
| clearly communicate those core limitations, and help
| build tools that help mitigate those problems.
|
| Probably just my opinion, and I am not suggesting it is
| superior, but zig style length as part of the type is
| what would mitigate most of what is problematic with
| C/C++
|
| Basically a char myArray[10];, really being *myArray is
| the main problem.
|
| Obviously the borrow checker removes that problem, but
| not once you need deques treeps etc...
|
| If I could use Rust as my only or primary language,
| memorizing the borrow checker rules wouldn't be that bad.
|
| But it becomes problematic for people who need to be
| polyglots, in a way that even Haskell doesn't.
|
| I really think there's ways for Rust to grow into a
| larger role.
|
| But at this point it seems that even mentioning the
| limits is forbidden and people end up trying to invert
| interface contracts, and leak implementation details when
| they're existing systems are incompatible with the
| projects dogma.
|
| It is great that they suggest limiting the size of unsafe
| code blocks etc....but the entire world cannot bend to
| decisions that ignores the real world nuances of real
| systems.
|
| Rust needs to grow into a language that can adjust to
| very real needs without and many real needs will never
| fit into what static analysis can do.
|
| Heck C would be safer if that was the real world.
|
| I really do hope that the project grows into the role,
| but the amount of 'unsafe' blocks points to them not
| being there today, despite the spin.
| maleldil wrote:
| > But it shifts the need to memorize undefined behavior
| with the need to memorize the borrow checker rules.
|
| With one massive difference: the borrow checker will tell
| you when you're wrong at compile time. Undefined
| behaviour will make your program compile and it will look
| like your program is fine until it's not.
|
| I'd take that trade.
| kstrauser wrote:
| > IMHO, the best thing Rust could do right now is more
| clearly communicate those core limitations, and help
| build tools that help mitigate those problems.
|
| rust-analyzer is the tool that does this. Having it flag
| errors in my editor as I write code keeps me from getting
| too far down the wrong road. The moment I do something
| Rust thinks is goofy, I get a red underline and I can
| pause to consider my options at that moment.
| marsten wrote:
| > But it shifts the need to memorize undefined behavior
| with the need to memorize the borrow checker rules.
|
| You have a choice: Fight the borrow checker on the front
| end until your code compiles, or chase down bugs on the
| back end from undefined behavior and other safety
| problems. No single answer there, it depends on what
| you're doing.
|
| Cognitive load is a big issue. In Rust I find this comes
| in a couple of phases, the first being figuring out what
| the borrow checker does and how to appease it. The second
| is figuring out how to structure your data to avoid
| overusing clone() and unsafe blocks. It's a bit like
| learning a functional language, where there's a short-
| term adaptation to the language, then a longer-term
| adaptation to how you think about solving problems.
|
| You mention doubly-linked lists, which are a good example
| of a "non-star" data structure that doesn't nicely fit
| Rust's model. But: for how many problems is this the
| ideal data structure, really? They're horrible for memory
| locality. Those of us who learned from K&R cut our teeth
| on linked lists but for the last 20 years almost every
| problem I've seen has a better option.
| elbear wrote:
| Funny, but I remember the difference between `>>=` and `>>`
| even though I haven't written Haskell in a couple of years.
| `>>=` passes to the right the value coming from the left, while
| `>>` drops it.
|
| To give an example, you use `>>=` after `readFile` to do
| something with the contents. You use `>>` after `putStrLn`
| since `putStrLn` doesn't return a meaningful value.
| haskman wrote:
| I gave a talk that goes more into what makes imperative
| programming better with Haskell compared to traditional
| imperative languages.
| https://speakerdeck.com/ajnsit/supercharged-imperative-progr...
|
| In a nutshell, first class effects and built in set of patterns
| for composing them get rid of boilerplate code. Combine that with
| type safety and you can churn out relatively bug free code very
| fast.
| zwnow wrote:
| With the downside of 99% of all devs not understanding
| anything. Sure haskell may be a great language, but even
| greater languages are accessible.
| haskman wrote:
| Accessibility is not an issue. It takes only a little bit of
| effort to get productive with a Haskell codebase. I think
| it's more of a mental block because the language is different
| from what one might be used to. What Haskell needs, and
| doesn't have, is a compelling reason for people to make that
| small effort (i.e. the killer usecase).
| AnimalMuppet wrote:
| "Relatively bug free code very fast" sounds like a killer
| use case to me.
|
| So why hasn't it happened? Some possibilities:
|
| 1. People are just ignorant/unenlightened.
|
| 2. Haskell is too hard to use for most people. I think that
| different programmers think in different ways, and
| therefore find different languages to be "natural". To
| those whom Haskell fits, it _really_ fits, and they have a
| hard time understanding why it isn 't that way for
| everyone, so they wind up at 1. But for those who it
| doesn't fit, it's this brick wall that never makes sense.
| (Yes, this is about the same as 1, just seen from the other
| side. It says the problem is the _language_ , not the
| _people_ - the language really doesn 't fit most people
| very well, and we can change languages easier than we can
| change people.)
|
| 3. Haskell isn't a good fit for many kinds of programming.
| The kind of programs where it fits, it's like a superpower.
| The kinds where it doesn't, though, it's like picking your
| nose with boxing gloves on. (Shout out to Michael Pavlinch,
| from whom I stole that phrase.)
|
| What kinds of programs fit? "If you can think of your
| program like a pipe" is the best explanation I've seen - if
| data flows in, gets transformed, flows out. What kind of
| program doesn't fit? One with lots of persistent mutable
| state. Especially, one where the persistent mutable state
| is due to the _problem_ , not just to the implementation.
|
| Myself, I lean toward a combination of 2 and 3.
| youerbt wrote:
| > So why hasn't it happened?
|
| 4. History. In those types of discussions, there are
| always "rational" arguments presented, but this one is
| missing.
|
| > One with lots of persistent mutable state.
|
| You mean like a database? I don't see a problem here. In
| fact, there is a group of programs large enough, that
| Haskell fits nicely, that it cannot be 3; REST/HTTP APIs.
| This is pretty much your data goes in, data goes out.
| layer8 wrote:
| HTTP APIs are just an interface. The application behind
| it can be almost anything.
| AnimalMuppet wrote:
| > > One with lots of persistent mutable state.
|
| > You mean like a database?
|
| No, I mean like a routing switcher for a TV station. You
| have a set of inputs and a set of outputs, and you have
| various sources of control, and you have commands to
| switch outputs to different inputs. And when one source
| of control makes a change, you have to update all the
| other sources of control about the change, so that they
| have a current view of the world. The state of the
| current connections is the fundamental thing in the
| program - more even than controlling the hardware is.
| youerbt wrote:
| Thanks. This does sound like a state machine, though, but
| the devil is probably in the details. Yes, here Haskell
| is probably a bad choice, and something where direct
| memory manipulation is bread and butter should do better.
| Which is completely fine; Haskell is a high level
| language.
|
| But in your example, PHP is also a bad choice, and alas,
| it dwarfs Haskell in popularity. I can't really think of
| where PHP is a great fit, but Haskell isn't.
| mrkeen wrote:
| Sounds like you want green threads, a concurrent runtime,
| and best-in-class synchronisation primitives.
| odyssey7 wrote:
| Oracle influenced / bought academia into teaching Java
| for a generation. See Dijkstra's criticisms[1] from the
| time, from when his department was forced to stop
| teaching Haskell to undergrads for political reasons.
| Note that Haskell had not been too hard for Dijkstra's
| undergrads.
|
| Later, Python took its place, since people realized the
| Java ecosystem was way too complicated and was turning
| off would-be CS students. Python directly targeted the
| academic use case by having similarities to C, Java, and
| Bash----it was not a better language, it just made
| existing imperative and object-oriented assignments
| easier for classroom environments. Believe it or not, a
| lot of programmers and even academics sort of give up on
| exploring significantly unfamiliar directions after
| graduating.
|
| [1] https://www.cs.utexas.edu/~EWD/OtherDocs/To%20the%20B
| udget%2...
| chikere232 wrote:
| I don't think that's it. I know plenty of people who were
| taught lisp first thing at university, and as soon as
| someone handed them an imperative language, they never
| looked at lisp again. And lisp is way easier than haskell
| IMO as IO is just a function and not a pilosophical
| concept
| odyssey7 wrote:
| I wouldn't assume that your colleagues were less capable
| as undergrads than the students that Dijkstra encountered
| at UT Austin.
|
| Imperative languages do offer many advantages over
| Haskell, in that most coursework and industry jobs use
| them and that, consequently, their ecosystems are much
| further developed. These advantages are a consequence of
| university programs' alignment with the imperative and
| object-oriented programming paradigms, to Oracle's
| benefit.
|
| Your colleagues having never looked back at lisp is
| hardly evidence that Haskell would have been too
| difficult for them or that Oracle didn't have a hand in
| this.
| chikere232 wrote:
| I don't think that holds water. We've had functional
| programming for longer than oracle or java have existed,
| and for far longer than oracle has owned java. Haskell
| itself has been around for longer than java or oracle-
| owned java.
|
| Functional programming just seems harder for people to
| get into. Perhaps it's bad for everyone that people don't
| make that effort, but it doesn't seem like a conspiracy
| cap11235 wrote:
| My first CS class was in Scheme (R6 iirc), and the year
| after they switched to python. Then a thousand cries in
| failure to understand python metaclasses. They are
| garbage at the repl, and you have a distinct set of folks
| that edit their editors.
| maleldil wrote:
| Most Python programmers don't really have to understand
| metaclasses or other advanced concepts like descriptors.
| The main metaclass they'd use would be to create abstract
| classes, and these days you can just subclass ABC.
| AnimalMuppet wrote:
| Re Dijkstra:
|
| He's asking for something that is _really_ inappropriate.
| He lost in the CS department, and he wants the _Budget
| Council_ to decide on what languages should be taught?
| Like they know anything about it!
|
| He lost a political battle, and he's appealing it to the
| only place he can, and he's buttering them up to do it,
| but the people that actually _know something_ about the
| topic decided against him already.
|
| And, you're quoting only one side of the battle. One
| vocal and eloquent side, but only one side. Maybe look
| into why the UT CS department made that change? (And not
| why Dijkstra says they did.)
| tome wrote:
| > Oracle influenced / bought academia into teaching Java
| for a generation
|
| I think you probably mean Sun? Or maybe that Oracle
| continued what Sun started.
| bdangubic wrote:
| Nah man, Oracle... here is a personal story. I was
| teaching at a Uni, introductory programming course. Dean
| hits me up and asks if I can teach introduction to web
| development as then current professor was going on
| maternity leave. I was like "heck yea, that sounds like
| fun."
|
| before the first class I get an email from one student
| asking if they must purchase the book for the class since
| it $275 (this is years ago) and I was taken aback, what
| kind of book costs $275 - even for a college textbook
| that was nuts. I told him to not purchase it until we
| meet for the first class. I go to the office and see my
| copy of the book, it is programming the web with oracle
| forms from oracle press!!!! I talked to Dean and he was
| like "yea, that is what we need to teach!" needless to
| say that, none of the kids bought the book, and I did NOT
| teach oracle forms, and I was never given that class
| again :)
| kstrauser wrote:
| I wish I'd have had either of those. C++ was the fad when
| I was going through, so freshmen learning how to write
| linked lists got a fast introduction to (and a hatred of)
| the STL.
| thdhhghgbhy wrote:
| >So why hasn't it happened?
|
| Off the top of my head, memory safety challenges for
| junior Haskellers (laziness footguns), State monad being
| fundamentally flawed: there is an inability to get at and
| log your application state just before a crash, bloated
| tooling, GHC frequently breaking existing code. Laziness
| and monadic code makes debugging painfully difficult.
|
| I like Haskell for Advent of Code only.
| AnimalMuppet wrote:
| > State monad being fundamentally flawed
|
| Could you explain a bit about what you mean here? In what
| way is it fundamentally flawed?
|
| I'm not saying you're wrong; I just want to understand.
| thdhhghgbhy wrote:
| Basically StateT uses Either to model either the
| application state or an error. So if your application
| throws, you lose the current state forever. They sort of
| painted themselves into a corner with this choice of
| type, there's no real way out now.
|
| This explains in more detail:
|
| https://github.com/haskell-
| effectful/effectful/blob/master/t...
|
| I agree loosely with what haskman above says about
| creating relatively bug free applications, the guard
| rails are so robust. But _those same guard rails mean you
| can paint yourself into a corner that it is harder to get
| out of without imperative state_ , case in point above.
| tome wrote:
| I acknowledge that those things can be challenging,
| however I'd like to respond to some of the specific
| issues:
|
| - Space leaks due to laziness are a solved problem. I
| explain the technique to solve it at:
| https://h2.jaguarpaw.co.uk/posts/make-invalid-laziness-
| unrep... This technique has not completely percolated
| throughout the community, but I am confident that it does
| actually resolve the "laziness causes space leaks issue"
|
| - Flawed state monad: well, you point out the analysis of
| its flaws from the effectful documentation. That's
| correct. The solution is: just use effectful (or another
| similar effect system. I recommend my own: Bluefin)
|
| - GHC breakage: I've been keeping an inventory of
| breakage caused by new GHC versions, since GHC 9.8:
| https://github.com/tomjaguarpaw/tilapia/ There has been
| very little! The Haskell Foundation Stability Working
| Group has had a massive effect in removing breakage from
| the ecosystem.
|
| - Laziness and monadic code makes debugging painfully
| difficult: I mean, sort of, but if you're using monadic
| code in the style of a decent effect system like
| effectful or Bluefin this is a non-problem. It's hardly
| different from programming in, say, Python from the point
| of view of introducing debugging printfs or logging
| statements.
| thdhhghgbhy wrote:
| Thanks, I've followed along with a lot of your posting on
| the Haskell discourse. One thing regarding this matter:
|
| >well, you point out the analysis of its flaws from the
| effectful documentation. That's correct.
|
| Thinking deeper about this, that there is essentially no
| way to fix this issue with StateT, because of the type
| choice, the monad, the composability requirement, all
| conspiring together to not be undone, does that signal
| something deeper that is wrong with the flexibility of
| Haskell, that we can progressively paint ourselves into a
| corner like this. Could it not happen again, but with
| another late breaking requirement, for effectful, or
| bluefin?
| haskman wrote:
| The reasons are going to vary depending on who you ask. I
| personally don't agree with any of your reasons. In my
| opinion, as a long time user of Haskell, the practical
| reasons are the following -
|
| 1. Tooling has historically been a mess, though it's
| rapidly getting better.
|
| 2. Error messages are opaque. They make sense to someone
| familiar with Haskell, but others cannot make the leap
| from an error message to the fix easily.
|
| 3. It's a jack of all trades. The resulting binaries are
| not small. Performance can be very good but can be
| unpredictable. It doesn't compile nicely to the web.
| Doesn't embed well. There is basically no compelling
| reason to get into it.
|
| 4. The ecosystem is aging. You can find a library for
| almost any obscure usecase, but it would be many years
| old, and possibly require tweaking before it even
| compiles.
| kccqzy wrote:
| Most functional languages give you so much more choice in
| managing persistent mutable state than the typical
| imperative languages. In the latter everything can be
| made into persistent mutable state so you have both those
| that are due to the problem and those that are due to the
| implementation.
|
| Haskell gives you a wide range of tools, from simulated
| state like the State monad, to real ones like the ST
| monad and IORef inside the IO monad. For synchronization
| between threads you have atomic IORef, MVar, and TVar.
|
| If you problem requires you to have persistent mutable
| state, Haskell helps you manage it so that you can truly
| separate those persistent mutable state that's due to the
| problem from those that's due to the implementation.
| bcrosby95 wrote:
| I generally don't like Haskell, but the view of non-
| Haskellers (myself included) regarding state does remind
| me a bit of resisting structured programming because
| gotos are easier to program.
| kccqzy wrote:
| That's a good analogy and I like it! If someone is really
| used to simply using goto for all kinds of flow control
| purposes, there could be some resistance if there's a
| coding style guide that enforces using if/else/while/do
| and such. It reminds me of similar arguments about
| recursion is too powerful in Haskell and similar
| languages and good style means using map/filter/foldr and
| the like; or the argument that call-with-current-
| continuation is too powerful. When a single tool is too
| powerful, it can be used for a large number of purposes
| so it ends up being the code reader's job to figure out
| which purpose the code writer has intended.
| tome wrote:
| Haskell is _superb_ at handling persistent mutable state.
| The only problem is that you may have analysis paralysis
| from choosing between all the choices (many of them
| excellent).
| zwnow wrote:
| I don't have a math background. Without a math background,
| you will have a bad time with Haskell.
| tome wrote:
| There are many successful Haskellers without a math
| background.
| TuringTest wrote:
| [delayed]
| seanparsons wrote:
| I always maintain that this is just familiarity, Haskell is
| in truth quite a simple language. It's just that the way it
| works isn't similar to the languages most people have started
| with.
| agumonkey wrote:
| I believe there's a strange boundary around the idea of
| simple vs easy (to quote rich hickey) and I don't know how
| to call it.. (or if somebody named it before)
|
| functional and logical languages are indeed very simple,
| small core, very general laws.. (logic, recursion, some
| types) but grokking this requires unplugging from a certain
| kind of reality.
|
| Most people live in the land of tools, syntax and features
| .. they look paradoxically both simpler than sml/haskell so
| people are seduced by them, yet more complex at the same
| time (class systems are often large and full of exceptions)
| but that also makes it like they're learning something
| advanced, (and familiar, unlike greek single variables and
| categ-oids :).
| chongli wrote:
| Maybe at its core, but Haskell in the wild is monstrously
| complex because of all the language extensions. Many
| different people use different sets of extensions so you
| have to learn them to understand what's going on!
| tome wrote:
| Not really, the vast majority of extensions just relax
| unnecessary restrictions. And these days it's easy to
| just enable GHC2021 or GHC2024 and be happy.
| liontwist wrote:
| Familiarity is a part, but abstract reasoning is
| fundamentally harder than concrete.
|
| Understanding the map signature in Haskell is more
| difficult than any C construct. Now do IO monad.
| cartoffal wrote:
| > Understanding the map signature in Haskell is more
| difficult than any C construct.
|
| This is obviously false. The map type signature is
| _significantly_ easier to understand than pointers,
| referencing and dereferencing.
|
| I am an educator in computer science - the former takes
| about 30-60 seconds to grok (even in Haskell, though it
| translates to most languages, and even the fully
| generalised fmap), but it is a rare student that fully
| understands the latter within a full term of teaching.
| catlifeonmars wrote:
| That's an unfair comparison because these are two
| unrelated concepts. In many languages, pointers are
| abstracted away anyway. Something more analogous would be
| map vs a range loop.
| Sardtok wrote:
| Well, he responded to someone saying the type signature
| of map was more complicated than ANY C construct.
| dhruvrajvanshi wrote:
| And I'd say the average React or Java developer these
| days understands both pretty well. It's the default way
| to render a list of things in React. Java streams are
| also adopted quite well in my experience.
|
| I wouldn't say one is more difficult than the other.
|
| IMO `map` is a really bad example for the point that OP
| is trying to make, since it's almost everywhere these
| days.
|
| FlatMap might be a better example, but people call
| `.then` on Promises all the time.
|
| I think it might just be familiarity at this point.
| Generally, programming has sort of become more `small f`
| functional. I'd call purely functional languages like
| Haskell Capital F Functional, which are still quite
| obscure.
| liontwist wrote:
| Are the students who failed the pointer class the same
| ones in the fmap class?
|
| I didn't say "using map" I said understanding the type
| signature. For example, after introducing map can you
| write its type signature? That's abstract reasoning.
|
| Pointers are a problem in Haskell too. They exist in any
| random access memory system.
| itishappy wrote:
| `map` aint so bad... map :: (a -> b) ->
| [a] -> [b]
|
| I suppose an absolute beginner would need someone to
| explain that Haskell type signatures can be read by
| slicing at any of the top level arrows, so that becomes
| either:
|
| > Given a function from `a` to `b`, return a function
| from a `list of as` to a `list of bs`.
|
| or:
|
| > Given a function from `a` to `b` and a `list of as`,
| return a `list of bs`.
|
| I find the first to be the more intuitive one: it turns a
| normal function into a function that acts on lists.
|
| Anecdotally, I've actually found `map` to be one of the
| most intuitive concepts in all of programming. It was
| only weird until I'd played around with it for about 10m,
| and since then I've yet to be surprised by it's behavior
| in any circumstance. (Although I suppose I haven't tried
| using it over tricky stuff like `Set`.)
|
| `fmap` is admittedly a bit worse...
| fmap :: Functor f => (a -> b) -> f a -> f b
|
| But having learned about `map` above, the two look
| awfully similar. Sure enough the same two definitions
| above still work fine if you replace `list` with this new
| weird `Functor` thing. Then you look up `Functor` you
| learn that it's just "a thing that you can map over" and
| the magic is mostly gone. Then you go to actually use the
| thing and find that in Haskell pretty much everything is
| a `Functor` that you can `fmap` over and it starts
| feeling magical again.
| liontwist wrote:
| You and I have a math part of our brain that appreciate
| the elegance from the algebraic structure.
|
| I'm saying that thing you did where you start
| representing concepts by letters which can be populated
| by concrete objects is not a skill most people have.
| itishappy wrote:
| Oh. Yeah that's a good point.
| layer8 wrote:
| People intuitively expect things to happen imperatively
| (and eagerly). Imperativeness is deeply ingrained in our
| daily experience, due to how we interact with the world.
| While gaining familiarity helps, I'm not convinced that
| having imperative code as the non-default case that needs
| to be marked specially in the code and necessitates higher-
| order types is good ergonomics for a general-purpose
| programming language.
| BeetleB wrote:
| > People intuitively expect things to happen imperatively
| (and eagerly).
|
| Eagerly? Yes. Imperatively? Not as much as SW devs tend
| to think.
|
| When the teacher tells you to sort the papers
| alphabetically, he's communicating functionally, not
| imperatively.
|
| When the teacher tells you to separate the list of papers
| by section, he's communicating functionally, not
| imperatively.
|
| When he tells you to sum up the scores on all the exams,
| and partition by thresholds (90% and above is an A, 80%
| above and above is a B, etc), he's communicating
| functionally, not imperatively.
|
| No one expects to be told to do it in a "for loop" style:
|
| "Take a paper, add up the scores, and if it is more than
| 90%, put it in this pile. If it is between 80-90%, put it
| in this pile, ... Then go and do the same to the next
| paper."
|
| People usually don't talk that way.
| BeetleB wrote:
| > With the downside of 99% of all devs not understanding
| anything.
|
| I would imagine that probably the majority of Haskell devs
| would understand it, which is all that matters.
|
| 99% of the population doesn't understand anything in Perl,
| either. That's not a mark against Perl.
| amelius wrote:
| When will we get the best of both worlds where the imperative
| parts of a Haskell program run at C++/Rust speeds?
| dismalaf wrote:
| Probably never since Haskell is always copying and GC'ing
| data...
| amelius wrote:
| Well, you could implement (in Haskell) a separate heap for
| (all or some of the) imperative code.
| lmm wrote:
| We've had Scala for about 20 years. (Is the JVM quite
| C++/Rust speed? No. But it's close enough for any realistic
| use case)
| thdhhghgbhy wrote:
| > first class effects
|
| There's not really first class effects though, ultimately just
| IO.
| sfvisser wrote:
| They're first class in the sense that they can be described,
| stored, computed, separated etc from pure functions. Fair to
| call them first class.
| thdhhghgbhy wrote:
| Koka has first class effects, but I think we have different
| interpretations of the statement above.
|
| Could you please clarify what you mean by 'stored'?
| tome wrote:
| Effect systems like effectful and Bluefin absolutely provide
| first class effects, ultimately not too dissimilar to Koka.
| thdhhghgbhy wrote:
| Okay, might be definitional, but when I think of 'first
| class', I think of something baked in to the language. So
| in Haskell's case, in the Prelude I suppose.
| tome wrote:
| Thanks for sharing, I really liked that!
| mbwgh wrote:
| > >> is an old name for _>
|
| I once had a hard to track down bug in some code making use of
| conduit[0], which is introduced using examples like `main =
| runConduit $ (yield 1 >> yield 2) .| mapM_C print`.
|
| Dutifully replacing every occurrence of (>>) with (_>), because
| it was more modern, suddenly changed the semantics somewhere, due
| to the fact that (>>) is defined with fixity `infixl 1 >>` and (
| _> ) as `infixl 4 _>` - i.e. both are left-associated operators,
| but (*>) binds tighter than (>>) and some of the myriad of other
| operators you may encounter.
|
| -- [0] - https://github.com/snoyberg/conduit
| _0ffh wrote:
| Maybe I'm not so bad for using "superfluous" braces quite
| often, although this is not specifically the reason why I do.
| tome wrote:
| This is a bit surprising. Why replace `>>` with `*>` "because
| it's more modern"? Can't you just stick with what's working?
| The former hasn't been deprecated.
| mbwgh wrote:
| You are absolutely correct and I was an idiot without a
| proper understanding of premature abstraction or unnecessary
| complexity.
|
| If I were to start a new side-project using Haskell today (I
| probably won't), I would just stick to do-notation and even
| concrete types (instead of mtl-style or what have you) where
| possible.
| sfvisser wrote:
| The generalized version of 'traverse/mapM' that doesn't just work
| for lists, but any 'Traversable' type is absolutely amazing and
| is useful in so many cases.
|
| 'traverse :: Applicative f => (a -> f b) -> t a -> f (t b)'
|
| And you can derive it for free for your own datatypes!
|
| The amount of code I've manually written in other languages to
| get a similar effect is painfully large.
| tromp wrote:
| The fully qualified type is traverse ::
| (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
|
| and deriving it for your own types is as simple as
| data MyType ... = ... deriving (Traversable)
| sfvisser wrote:
| Correct! I simply copied the definition from the type class,
| but the context is important.
| rrgok wrote:
| Can you kindly make a real word example that is not usual Maybe
| or Either example, that uses user defined data type?
|
| I understand how Applicative works, but I don't know how to
| apply (pun intended) to my data types.
| lmm wrote:
| I had a generic report class that essentially fetched a bunch
| of database rows, did some stuff for each row, and then
| combined the results together into a report. (This was in
| Scala, I know Haskell doesn't have classes, but presumably
| similar situations can happen)
|
| For one client, we needed to accumulate some extra statistics
| for each. For another, we needed to call their web API (so
| async I/O) to get some of the data used in the report. By
| making the generic superclass use a generic Applicative type,
| we could keep the report business logic clear and allow the
| client-specific subclasses to do these client-specific things
| and have them compose the right way.
|
| Wanting custom applicative types is rarer than using a
| standard one, but it can be a good way to represent any kind
| of "secondary effect" or "secondary requirement" that your
| functions might have. E.g. "requires this kind of
| authorisation" or "must happen in a database transaction".
| But a lot of the time you can implement custom things using
| reader/writer/state, or free, rather than having to write a
| completely from-scratch applicative.
| BoingBoomTschak wrote:
| Am I the only one who never tried Haskell but who when reading
| discussion about it ends up thinking real-world (with GHC
| extensions) Haskell has way too much (sometimes historical)
| cruft? It really detracts me from it.
| kreetx wrote:
| What cruft?
| tasuki wrote:
| The standard library is atrocious, mainly I believe for
| historical reasons:
|
| - map only works on Lists (one needs fmap for functors)
|
| - head throwing exceptions instead of returning Maybe
|
| - dependent types bolted on later: they're much nicer in
| Idris
| kreetx wrote:
| - map is easier on newcomers. Once you you understand
| functor, you'll remember to use fmap anyway. Also, I still
| use map for lists after 10+ years.
|
| - I don't fully understand this, but do you mean that every
| `IO a` function would be better off being `IO (Maybe a)`?
|
| - AFAIK, there are no dependent types in Haskell yet, but
| say that the type level programming you can do today is
| what you mean then you are already in quite advanced
| territory. Yeah, I guess it could be more polished.
| tasuki wrote:
| I mean that `head` should be `List a -> Maybe a` rather
| than `List a -> a`. If you really really want an `a`, you
| should provide `NonEmptyList a` or something.
| tromp wrote:
| Haskell provides https://hackage.haskell.org/package/base
| -4.21.0.0/docs/Data-...
| kreetx wrote:
| I agree! The current `head` should be renamed
| `unsafeHead` (or something), and the new `head` should
| have the type you wrote.
| tome wrote:
| > head throwing exceptions instead of returning Maybe
|
| The choice is keep it is it is, or change it. Changing it
| would break vast amounts of existing code. Which option do
| you prefer?
| kqr wrote:
| I think this was given as an example of necessary-but-
| annoying cruft, not as a suggestion for change.
| Vosporos wrote:
| It's a thirty year-old language, it's bound to have cruft.
| However modern codebases tend to showcase a pretty efficient
| combination of language features, oftentimes optimised for
| productivity rather than research in lazy FP. Such codebases
| are https://github.com/flora-pm/flora-server or
| https://github.com/change-metrics/monocle
| asplake wrote:
| Am I right in thinking that there are efforts to provide a
| better out-of-the-box experience, with some of that cruft dealt
| with for people who don't need the backwards compatibility? For
| myself, I found long prologues of extensions/options/whatever
| massively off-putting.
| mrkeen wrote:
| Sure, use {-# LANGUAGE GHC2024 #-}
| revskill wrote:
| Nice.
| aranchelk wrote:
| In practice it only detracts a little bit. You can enable GHC
| extensions project-wide and there are alternate standard
| libraries (preludes) that are more modern.
|
| If you want a language that is very Haskell-like without the
| historical baggage or the laziness, PureScript is very good.
| It's main compile target is JavaScript, so it's built for
| different use cases.
| 708145_ wrote:
| It is definitely not procedural.
|
| "This seems rather ... procedural. Even though we get all the
| nice guarantees of working with side effectful functions in
| Haskell, the code itself reads like any other procedural language
| would. With Haskell, we get the best of both worlds."
|
| Working with the IO monad is much more complex, especially if you
| want to use other monadic types inside that code.
| tome wrote:
| > Working with the IO monad is much more complex, especially if
| you want to use other monadic types inside that code.
|
| Yes, mixing other monadic types with IO can be a challenge. One
| possible solution to that is not just not use other monadic
| types inside the code. Just use a general-purpose effect system
| instead. I recommend effectful or Bluefin (the latter my own
| project).
| scotty79 wrote:
| It's a very good explanation but I'd welcome more humility.
| Instead of using phrase lie-to-kids, I could say that all the
| dance around effects in Haskell is just implementation detail
| and/or leaky abstraction.
| ay wrote:
| Haskell dilettante here... The "IO a" vs "a" reminded me of async
| vs sync - where the first one returns a promise/future to be
| awaited on, rather than a result.
|
| Is there any parallel there, or is it an entirely wrong
| perception I got ?
| moffers wrote:
| There's a parallel because Promises in a language like
| JavaScript are "monad-like", so they're similar to the IO Monad
| here. I am not a functional wizard so I'm sure that was not a
| fair comparison in some way, but it's how I have thought of it.
| They're both a representation of a side effect and require that
| effect be respected before you can get to the value inside it
| svieira wrote:
| Not so much a representation of a side effect (after all List
| is a Monad as is addition on integers no side effects
| anywhere in sight) as the reification of a particular
| category.
|
| JavaScript's `Promise` is particularly interesting because it
| is a Monad over "all JS values which do not contain a method
| called `then` in their prototype chain" or as Dominic put it
| 12 years ago:
|
| > Indeed, I like the way @medikoo phrases it. There's,
| practically speaking, nothing wrong with being a monad on the
| category of non-thenables.
|
| https://github.com/promises-aplus/promises-
| spec/issues/101#i...
| acjohnson55 wrote:
| Yes, except in a lot of languages, a Promise is the
| representation of the result of a computation that may already
| be happening, whereas an IO is a computation that will have
| data injected into it by some interpreter. But it's a very
| close comparison, in the sense that both represent contexts
| that future computation can be appended on. Also, in some
| languages, the Promise type is truly monadic.
| lucasoshiro wrote:
| > Is there any parallel there
|
| Of course. Promise is a monad, .then is more or less equivalent
| to the >>= operator and await makes it look more imperative-ish
| just like <- in Haskell.
|
| Note that in JS you'll need to be inside an async function to
| use await, just like in Haskell you'll need to be inside the do
| notation to use <-. Otherwise, you'll need to play with .then
| just like you would need to play with >>= in Haskell.
|
| About other languages, one way to achieve asynchronous behavior
| without having it implemented in the language is using
| ReactiveX (https://reactivex.io/). It's hard to understand at
| first, but it if you understand the IO Monad it becomes easy,
| as it's basically the same.
| hackandthink wrote:
| Haskell has strong monads, missed this in the article.
|
| It makes Haskell even more procedural, you can use intermediate
| variables in do blocks.
|
| http://blog.sigfpe.com/2023/08/what-does-it-mean-for-monad-t...
| fire_lake wrote:
| In other languages we can do a poor man's IO with a function
| like: () => console.log("abc")
|
| Is this really so different from IO everywhere in Haskell?
| mrkeen wrote:
| When you express it has "Haskell has the IO monad" and "Haskell
| has the Maybe monad", it's no biggie because other languages
| have them. All Java Objects might or might not be there (so
| they all implement Maybe). And all Java methods might or might
| not do IO, so they all implement IO.
|
| The real sell is: Haskell objects which are not Maybe are
| _definitely_ there. Functions which are _not_ IO will _always
| give the same input for the same output_.
| corank wrote:
| I think as long as the code sticks to the discipline of never
| actually doing I/O but only manipulating functions that perform
| them it would basically be doing the same thing as IO monads in
| Haskell.
|
| So print(s) returns a function that when called prints s. Then
| there needs to be function that joins those functions, so
| print(a); print(b) evaluates to a function that once called
| prints out a and then b.
|
| What makes Haskell special in my opinion is 1) it generalises
| this way of achieving "stateful" functions, 2) enforces such
| discipline for you and makes sure calling functions never
| produces side effects, and 3) some syntactic sugar (do, <-,
| etc) to make it easier to write this kind of code.
|
| Also note that the above example only does output which would
| be the easier case. When it comes to code with input, there
| will suddenly be values which are unavailable until some side
| effects have been made, so the returned functions will also
| need to encode how those values are to be obtained and used,
| which complicates things further.
| lucasoshiro wrote:
| > Is this really so different from IO everywhere in Haskell?
|
| It's a starting point. You'll have to somehow chain (e.g. you
| want to print "def" after "abc", how would you do that?).
| You'll also need to put a value into your IO, e.g. if you are
| inside that IO, all the computations will need to produce IO.
| If you solve those things, you'll have a Monad.
|
| But, wait! You're in JS, right? You have IO Monads there,
| perhaps only don't know yet. They are the JS Promises! Look
| this:
|
| // assuming that you have a sync input function, that receives
| a string from stdin and returns it
|
| let input_str = new Promise((resolve, reject) =>
| resolve(input());
|
| let input_int = input_str.then(s => parseInt(s));
|
| let square = input_int.then(n => n * n);
|
| square.then(sq => console.log(sq));
|
| If you want to proceed with your poor man's IO, you'll need to
| implement your "then". Going further, the equivalent in Haskell
| (not the best code, though) to that is:
|
| main = result where input_str = getLine
| input_int = input_str >>= \s -> return (read s) :: IO Int
| square = input_int >>= \n -> return (n * n)
| result = square >>= \sq -> putStrLn (show sq)
|
| Ok, but Haskell has a syntax for cleaning that, called do
| notation:
|
| main = do input_str <- getLine let
| input_int = read input_str :: Int let square =
| input_int * input_int putStrLn (show square)
|
| And JS? JS has also its syntax, using await. Just like Haskell
| that it only works inside the do notation, in JS it will only
| work inside async functions:
|
| let input_str = await new Promise((resolve, reject) =>
| resolve(input()));
|
| let input_int = parseInt(input_str);
|
| let square = input_int * input_int;
|
| console.log(square);
___________________________________________________________________
(page generated 2025-01-19 23:00 UTC)