#BabelOfCode 2024
Week 7
Language: Haskell

Confidence level: Medium low

PREV WEEK: https://mastodon.social/@mcc/114308850669653826
NEXT WEEK: https://mastodon.social/@mcc/114463342416949024
RULES: https://mastodon.social/@mcc/113676228091546556

I was going to do Fennel this week, but then I looked at the problem and thought "this is ideal for Haskell "amb". I have been looking for an excuse to use Haskell "amb" for 25 years. So Haskell.

I have tried to learn Haskell 3 times now and failed. This "Babel of Code" thing was originally in part an excuse to do Haskell

I am not sure whether the reason I previously failed Haskell is

1. Because it's actually hard
2. Because of a mental block caused by failing at it more than once already
3. Because Haskell users are really bad at explaining things

I think it's a little 2 and mostly 3. I *love* ML, I know two MLs (3 if you count Rust) plus have in the past written my own ML. I understand the parts of Haskell that are just ML and get lost whenever I hit "do"— the point of divergence from ML; the dreaded Monad.

Question: The Haskell 2010 documentation describes its basic I/O functions, somewhat ambiguously, as "character oriented". I assume this means "ASCII character oriented". Is there a way in Haskell to get equivalents of getChar, putChar, string operations etc which are *UTF-8 character* oriented? I don't need graphemes, I'm happy with codepoint resolution.

In the Haskell docs

https://wiki.haskell.org/Haskell_in_5_steps

It states this is how you build a Haskell program to run it.

Assuming I realize I can drop -threaded, is actually the easiest/correct way to build a Haskell program to run in the year 2025?

Haskell in 5 steps - HaskellWiki

I run the given ghc --make command. It leaves some crap in src/. Say I do not want intermediate files in my source tree. I would like them to be moved to bin/ or obj/ or something, or simply not retained. Is this possible, or is Haskell in 2025 simply a "leaves crap in src/" kind of language in 2025?

I found -no-keep-hi-files and -no-keep-o-files (despite them technically not being documented) but say I want to retain them, just in a place of my choosing.

Welp, after 25+ years of trying, I have written my first working Haskell program. It reads one line from stdin and then prints it back out. I have now finally used a "monad", although I still don't feel I know what one ~is~.
I am trying to switch my program to Cabal-driven builds. This is one of the questions it asks you when you run `cabal init`. I think I understand what it is about the Haskell community that lead them to do it this way, but in my opinion, this is bad user experience. If the newest version of the Cabal format isn't the recommended one then why did you release it at all?

I am attempting to call "openFile" on the first command line argument in Haskell¹. It doesn't like it.

I'm not sure I'm looking at the right docs. I searched Google for "haskell system.io" and got https://hackage.haskell.org/package/base-4.21.0.0/docs/System-IO.html#v:openFile . I don't know if this is the newest Haskell2010 or if $GOOG is confused.

The doc (every doc I find) claims the type of openFile is FilePath -> IOMode -> IO Handle. But hls on my computer seems to think it's FilePath ->IOMode -> Bool -> IO Handle. Am I missing something?

System.IO

Answer to my previous question was I naively took the first suggestion from hls and imported GHC primitives where I should have imported System.IO. Cool. Works now

Haskell people, please help me.
There are 3 image attachments to this post, showing the same code block but with different amounts of indentation.

The first code block works,
the second block does not work,
the third one REALLY does not work.

According to my editor, none of these blocks of code contains tabs.

Haskell appears (?) to treat three spaces, four spaces, and eight spaces radically differently.

I dislike multiple-of-3 indents.

What do I need to read to understand what I am missing?

Another cursed question.

See attachment 1. This code compiles.

Reading the documentation ( https://hackage.haskell.org/package/megaparsec-9.7.0/docs/Text-Megaparsec-Char.html#v:space ), I realize I do not want space but "space1" (see attachment 2).

I change the symbol "L.space" to "L.space1". No!! Says GHC. L does *not* export space1!! only space!!

But the documentation says it exports space1?

Text.Megaparsec.Char

My problem can be explained if when I put dependency "megaparsec ^>=9.7.0" in my cabal file it picked like version 5 or 6 or something.

Is there a way to get cabal to print out for me what version it actually chose of each solved dependency? In npm or Rust for example I would consult the lock file.

I never solved the space1 problem but worked around it with a solution from @dysfun . I now have three new questions.

1. In attachment 1, why is "return" not required on L.decimal? I originally wrote "return" and it gave me a hint saying I could remove it, and removing it works. But return *is* required on (lsum, nums)?

2. In attachment 2: If att. 1 is allowed, why is this not allowed? It gives "parse error (possibly incorrect indentation or mismatched brackets)" on the _. Wrong type syntax?

@mcc haven't done Haskell in a while, but I don't think it cares how many spaces, just that they're the same. Your problem is that the alignment point is the start column of the first token after the `do`.
@kw217 oh no. okay, I see :(

@mcc only in the first case is the "let" keyword (and following code) aligned to "do"

It's just significant whitespace. You can either align everything on the column after "do " (that's where the 3 chars come from) or indent on a newline with an indent width of your choice.

@mcc in pic 3, it's the "if" that breaks the alignment

@mcc

This should compile, with 'do' on the prev line:

```
takeLine :: Handle -> Int -> IO Int
takeLine inHandle acc = do
inEof <- hIsEOF inHandle
if inEof
then return acc
else do
inStr <- hGetLine inHandle
let result = 1
takeLine inHandle (acc + result)
```

@mcc in my opinion, the neatest way to get consistent indents is to have a newline straight after "do", then your preferred number of spaces the next line
@mcc would you be interested in a video chat intro sometime tomorrow, east coast USA biz hours?
I got paid to write Haskell for five years, could maybe speed up your getting started process?
@shapr That is a very generous offer, at the moment I don't think it's needed but thank you.

@mcc If you change your mind, I'm free on Mondays and Fridays, I hope it goes well for you!

❤️ 

@mcc I am excited you’re writing Haskell. 😄

In general, it’s unlikely you want to be importing `GHC`. If ever you do need to, you’ll know.

@samir @mcc I came here to say this 😁
@mcc any particular reason why you're importing GHC.* and not System.IO?
@bars Because it's what hls recommended. Thank you
@mcc Sure. Keep us posted!
@mcc wait till you see the tiny cabal file it geberates. Who thought this was a good idea? Cabal init should ideally ask no questions at all
@aj the file it generated has what appears to be hundreds of lines of comments actually
@mcc oh god I had forgotten about the comments
@mcc Maybe they liked the python2/python3 schism so much they would like to see if they can get that for their community?
@mcc LTS and supporting multiple versions of GHC in the wild... 😮‍💨
Debian stable is at GHC 9.0.2, Debian unstable at GHC 9.6.6 and upstream GHC is maintained at versions 9.6 up to 9.12. I have seen some good work from Cabal devs, but feature sets may be more practical to have packages available over a broad range of versions. I totally agree and would want new packages to take up the new features, though!
@mu Oh, is that the idea? That 3.0 is LTS?
@mcc I think it's a bit conservative for that. Cabal 3.0 is relatively old, and I suspect the choices in cabal init might not be up to date.
@mcc I heard they are like burritos
@onelson I heard that too but it doesn't help me
@onelson @mcc bananurrito
@onelson @mcc (I hate these metaphors so much because if you take them to their logical conclusion you have to start thinking about things like "now you unwrap this banana and put these bananas inside and then wrap it all up into one new banana")

@megmac @mcc idk if this is anything but I have long held that monads are just like null propagation in sql, e.g. how `null or false` is `null` not `false`.

I'm not even sure this is accurate, tbh.

@onelson @mcc I think analogies just don't really work at all, whether to abstractions like food or to other different sorts of languages.

Imo the useful definition of a monad is just that it is a particular kind of nested data structure that happens to be useful for describing computation constructively. A math nerd will tell you that's not all it is, but from a programmer's perspective that's why it matters.

@onelson @megmac @mcc Null propogation is basically the Maybe monad. So yes that's a monad, but just one example of a monad: there are lots of other different things monads can do.
@onelson @megmac @mcc Actually it's a good way to understand them: a monad represents something that can be propogated.
@onelson @megmac @mcc Wait, maybe that's just a functor.
@mcc at one point I understood Monads by implementing Deferred (which everyone kept telling me was actually a Monad) in Haskell. As I recall, I started with about 700 lines of code and eventually got it down to 0 as I finally, fully realized the idea that it was a monad. But the experience of learning Haskell this way felt like ascending in a roguelike; I accomplished something, but I could not take it with me. Today, I do not know what a Monad is and I cannot write Haskell.
@glyph @mcc this is also my experience of Haskell. At one point in time, my language of choice. Now, don't ask me to explain anything about it.
@glyph @mcc we live in a mysterious universe.
@mcc It’s Rust’s ? being implicitly added on every line.
@mcc -odir and -hidir for where to put the files. i think --make and -threaded are there by default today
@mcc i guess the "normal" way to use haskell is exclusively via cabal which puts all the output files elsewhere

@annanannanse Wait, what? I can use cabal for normal builds?

Are these the directions to use ?

https://cabal.readthedocs.io/en/stable/nix-local-build.html

3.1. Quickstart — Cabal 3.14.2.0 User's Guide

@mcc i'm afraid i'm not up to speed on howto cabal best (i only use haskell via a custom build system that also just calls ghc...), but you need a .cabal file defining your exe and the `cabal build/run` should work.

@mcc @annanannanse You need a cabal project to be able to use `cabal build` or `cabal run`. There is `cabal init` to produce a skeleton. Not so simple unfortunately.

I think for simple tasks it is better to run stuff from ghci and just forego compiling entirely.

@mcc

Feels a bit like programming in C.

"Just put rm *.o in your makefile."

"That's how it has always been."

"It's not the job of the compiler to not leave a mess."

@mcc While researching this online, I found a build tool called cabal.

cabal build --builddir=dist/build-artifacts

@mcc Isn't that like rustc/cargo, most people using cargo instead of rustc directly? 🤔