Readit News logoReadit News
imoverclocked · 8 months ago
> 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 · 8 months ago
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 · 8 months ago
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.

yodsanklai · 8 months ago
> 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 · 8 months ago
> 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 · 8 months ago
> burrow

FYI it's "borrow" (as in when someone lends something to you) not "burrow" (which is a tunnel/hole)

ggm · 8 months ago
Good intuition is a gift from god. For the rest of us, notational confusion abounds. Types are great. Syntax (not semantics) and notation can be a major bummer.

I think use makes master: the more you use it, the more mastery you will have and the better intuition will follow.

withinboredom · 8 months ago
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.
elbear · 8 months ago
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 · 8 months ago
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 · 8 months ago
With the downside of 99% of all devs not understanding anything. Sure haskell may be a great language, but even greater languages are accessible.
seanparsons · 8 months ago
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.
haskman · 8 months ago
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).
BeetleB · 8 months ago
> 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.

Deleted Comment

thdhhghgbhy · 8 months ago
> first class effects

There's not really first class effects though, ultimately just IO.

sfvisser · 8 months ago
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.
tome · 8 months ago
Effect systems like effectful and Bluefin absolutely provide first class effects, ultimately not too dissimilar to Koka.
amelius · 8 months ago
When will we get the best of both worlds where the imperative parts of a Haskell program run at C++/Rust speeds?
lmm · 8 months ago
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)
dismalaf · 8 months ago
Probably never since Haskell is always copying and GC'ing data...
tome · 8 months ago
Thanks for sharing, I really liked that!
sfvisser · 8 months ago
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 · 8 months ago
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 · 8 months ago
Correct! I simply copied the definition from the type class, but the context is important.
rrgok · 8 months ago
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 · 8 months ago
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.

sfvisser · 8 months ago
Note that deriving Traversable for you own datatypes mean changing the structure to map effect over, the `t` variable. Not the effect `f`, which is generic and the Monad/Applicative in this case.

Besides Maybe/Either `t` could represent anything, like container types Lists/Trees/Hashmaps etc, but also more complicated structures like syntax trees for programming languages or custom DSLs. Or (my favorite use case!) recursive command types for robot control. I'm doing this mostly in Rust, but borrow all the ideas from Haskell.

RobotToaster · 8 months ago
Somewhat tangential, but the only software I know of that's written in Haskell is ImplicitCAD https://implicitcad.org/
epolanski · 8 months ago
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 · 8 months ago
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 · 8 months ago
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?
Locutus_ · 8 months ago
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.

hiAndrewQuinn · 8 months ago
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.

_jackdk_ · 8 months ago
> 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.

internet_points · 8 months ago
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.
astrange · 8 months ago
> 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.

MaxGabriel · 8 months ago
You’re correct that Mercury uses Haskell for its backend: https://serokell.io/blog/haskell-in-production-mercury
yehoshuapw · 8 months ago
https://hledger.org/

hledger - a plain text accounting tool

AlgebraFox · 8 months ago
https://simplex.chat/

Private messenger for desktop and mobile platforms. It's mostly written in Haskell except for UI.

internet_points · 8 months ago
Also the server backend for the Wire messenger https://github.com/wireapp/wire-server
exe34 · 8 months ago
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

MaxRegret · 8 months ago
Don't forget Pandoc!
f1shy · 8 months ago
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.
internet_points · 8 months ago
https://tidalcycles.org/ – think ImplicitCAD but for live music performances?

also http://detexify.kirelabs.org/classify.html was surprisingly useful in university

cropcirclbureau · 8 months ago
PostgREST and Hasura (before rewrite) are written in Haskell.
TypingOutBugs · 8 months ago
I worked on Cardano which is probably one of the larger Haskell projects (and Agda, which lets you write proofs and compile into Haskell!)
colordrops · 8 months ago
Xmonad, though most people have moved on to other window managers at this point.
ulrikrasmussen · 8 months ago
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!
mbwgh · 8 months ago
Not me, because I hate change!
bekantan · 8 months ago
Still on XMonad, what are some good alternatives?
weikju · 8 months ago
Shellcheck is another useful one (linter for shell scripts)

https://www.shellcheck.net/

tmountain · 8 months ago
Haskell is an amazing type checker that’s occasionally used for writing software. ;-)
thesz · 8 months ago
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 · 8 months ago
ShellCheck is an essential part of my projects' CI, couldn't do without it!
ryandv · 8 months ago
The aura package manager for AUR, as well as the xmonad tiling WM.
henrebotha · 8 months ago
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.

simonmic · 8 months ago
That got fixed the other day.

https://implicitcad.org

endgame · 8 months ago
The other ones most people point to are https://pandoc.org and https://shellcheck.net
cryptonector · 8 months ago
You're missing out if you don't know about PostgREST.
IshKebab · 8 months ago
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 · 8 months ago
BoingBoomTschak · 8 months ago
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.
Vosporos · 8 months ago
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
aranchelk · 8 months ago
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.

asplake · 8 months ago
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 · 8 months ago
Sure, use

    {-# LANGUAGE GHC2024 #-}

kreetx · 8 months ago
What cruft?
tasuki · 8 months ago
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

EE84M3i · 8 months ago
Exceptions n+k patterns Lazy IO
mbwgh · 8 months ago
> >> 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 · 8 months ago
Maybe I'm not so bad for using "superfluous" braces quite often, although this is not specifically the reason why I do.
tome · 8 months ago
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 · 8 months ago
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.

708145_ · 8 months ago
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 · 8 months ago
> 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).

hackandthink · 8 months ago
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...