Salta al contenuto principale


#BabelOfCode 2024
Week 7
Language: Haskell

Confidence level: Medium low

PREV WEEK: mastodon.social/@mcc/114308850…
NEXT WEEK: mastodon.social/@mcc/114463342…
RULES: mastodon.social/@mcc/113676228…

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


#BabelOfCode 2024
Week 6
Language: Nameless experimental LISP

Confidence level: High

PREV WEEK: mastodon.social/@mcc/113975448…
NEXT WEEK: mastodon.social/@mcc/114433465…
RULES: mastodon.social/@mcc/113676228…

Okay… here things get weird!

My project to, gradually over the course of 2025, do each puzzle from Advent of Code 2024 in a different programming language I've never used before, has stalled out for exactly two months now as I've instead been creating…

…the programming language I'm going to use this week!


Questa voce è stata modificata (5 mesi fa)
in reply to mcc

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.

in reply to mcc

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 reply to mcc

In the Haskell docs

wiki.haskell.org/Haskell_in_5_…

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?

in reply to mcc

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.

in reply to mcc

-odir and -hidir for where to put the files. i think --make and -threaded are there by default today
in reply to ase

i guess the "normal" way to use haskell is exclusively via cabal which puts all the output files elsewhere
in reply to 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.
in reply to 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.

@ase
in reply to mcc

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

cabal build --builddir=dist/build-artifacts

Questa voce è stata modificata (5 mesi fa)
in reply to mcc

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

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~.

Oblomov reshared this.

in reply to 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.

reshared this

in reply to mcc

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?
in reply to mcc

Maybe they liked the python2/python3 schism so much they would like to see if they can get that for their community?
in reply to 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!
in reply to 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.
in reply to mcc

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 hackage.haskell.org/package/ba… . 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?

in reply to mcc

any particular reason why you're importing GHC.* and not System.IO?
in reply to bars

@bars Because it's what hls recommended. Thank you
@bars
in reply to mcc

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
in reply to 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.

in reply to 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?
in reply to Shae Erisson

@shapr That is a very generous offer, at the moment I don't think it's needed but thank you.
in reply to mcc

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?

in reply to 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`.
in reply to 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.

in reply to mcc

Another cursed question.

See attachment 1. This code compiles.

Reading the documentation ( hackage.haskell.org/package/me… ), 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?

in reply to mcc

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.

in reply to mcc

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?

in reply to mcc

I went from OCaml to Rust and coming back to Functional Land, one thing I'm really noticing is just *how much fricking better* the Rust error messages are than OCaml's, and consequently, how much fricking better they are than Haskell's. One thing is since Rust has less extensive type inference, you get way less "spooky action at a distance" in Rust than OCaml/Haskell and thus errors tend to actually be marked at the site where they really occur.
in reply to mcc

I've been having extensive problems in my program using the symbol "spaceChar" exported from Megaparsec, because if I use spaceChar in an expression, but the *variable to which the expression which uses spaceChar is assigned* is unused, everything breaks (and the error is inscrutable). OCaml is a spooky language but I never saw anything THIS spooky happen. Writing normal idiomatic code, too much wound up implicit and the compiler cannot even explain to me what it is that it doesn't understand.
Questa voce è stata modificata (5 mesi fa)
in reply to mcc

Something to do with monads? The do-block does something similar to the transform from async/await to callbacks and maybe you're doing the equivalent of dropping a future instead of awaiting it? But those error messages tell you none of that.
in reply to mcc

One more question (I think this question might be outright goofy).

Is there a specific syntax for "calling a monad of a different type" from the current monad?

I have constructed a megaparsec-monad combinator that parses my pattern. I've made an IO-monad function that reads lines one at a time. If I call the megaparsec combinator I made inside my IO monad, I get a confusing error.

The megaparsec tutorial implies a combinator can act like a function that takes strings: markkarpov.com/tutorial/megapa…

in reply to mcc

you're interested in "lift IO" or "parser combinators"
in reply to mcc

Haskell Programmers Will Literally Write Multiparagraph Comments Instead Of Just Giving The Parameter A Name Longer Than One Letter
in reply to mcc

There's some library, I think it's one of the Lens ones, where the type variables for a type spell "s t a b". It doesn't help me know what they mean or do, but it does give me a chuckle.

reshared this

in reply to mcc

So the answer to my last question was to use "parse"/"runParser" (aliases for 1 function) from megaparsec. Great.

It's not working and I think the problem is I don't understand destructuring. I want the equivalent of Rust

let Some(x) = f() else { panic!("Not found!"); }

I *think* I'm getting an Either, and I need to match its cases. But the way I know how to do that is "case…of". And the arrows in that "point the wrong way"?? Compare this similar attempt to unpack a list into its 1 item:

in reply to mcc

the argument order for parse is wrong. it take the parser first and the file second
in reply to Zoe : Void -> a

for destructuring, if a pattern match fails in do notation it calles "fail" from MonadFail. for IO that throws an error. You could use a Monad Transformer over IO to get difference behavior or have the first case be
"[x] -> [x]"
in reply to Zoe : Void -> a

@zmz I did try it with [inFile] -> [inFile] and the line after was still an indentation error.
in reply to mcc

Alright. Thanks for the explanations y'all. I am now correctly parsing my input file. Current source:

github.com/mcclure/aoc2024/blo…

I STILL can't figure out how to make the match on line 43 give a more human-readable error (on the array destructure on the command line argument processing) than "user error (Pattern match failure in 'do' block at app/Puzzle.hs:43:5-12)", but since I'm the only one running this I guess this only matters for "this hurts my sense of professionalism!!" reasons.

Questa voce è stata modificata (5 mesi fa)
in reply to mcc

I will say. I could have done *this entire 50 line Haskell program* in the following two lines of perl:

perl -e 'open(FH,"$ARGV[0]"); while (<FH>) { /^(\d+):\s+(\d+(?:\s+\d+)*)$/ or die "Invalid input"; my $sum=$1; my @‍ops = split(/\s+/, $2); }'

…and I suspect writing that not only required far, far less thought for me, but would have required far less thought for someone who was already versed in both Haskell and Megaparsec.

(EDIT: Note to run this snippet you must remove the 0-width space.)

Questa voce è stata modificata (5 mesi fa)

reshared this

in reply to mcc

I kinda feel like I should go back to Perl. All I do is admin stuff anyway and I'm not sure anything else is really better for it.
in reply to mcc

is this just summing numbers from a file?
print . sum . fmap (read @int) . words =<< readFile . head =<< getArgs
Questa voce è stata modificata (5 mesi fa)
in reply to Zoe : Void -> a

@zmz @int No; it is splitting a file into lines, then for each line matching the pattern

num: num [num num…]
and returning the tuple (initial_number, [list_of_following_numbers])

So that is a little more complicated.

I also intentionally did it such that it interprets each line one by line instead of buffering the entire file (the perl oneliner solution does this) which might complicate things.

in reply to mcc

@int sorry, distracted and wasn't reading carefully. still pretty simple to do idiomaticly, but too much for my phone. readFile isn't lazy io, but there is a version that does lazy io that would make that steam the file
in reply to mcc

@zmz my fortunate or unfortunate choice of username has somehow landed me in a Haskell community discussion on regular expressions.

Wrong number. Figures.

in reply to mcc

I haven't touched perl in at least 15 years and that example seems clear (it's a one-liner and therefore too terse). I can only imagine how alien and unreadable the Haskell version would be to me. Not that one-liner perl is any great prize but Haskell's syntax eludes me.
in reply to arclight

@arclight it's linked in the post before . You'll find it maybe not so bad because I don't use any of the . $ <*> << >> <* nonsense
in reply to mcc

The Haskell documentation is clearly written for humans to read— *some* human, *somewhere*. It is definitely not written with the intent of *me* reading it.
in reply to mcc

c'mon you're likely just 6 ICFP papers away from understanding a programming dad joke. 😁
in reply to mcc

Haskell syntax question: Is there a thing I can do to convince GHC that when I said "-1" I REALLY, definitely wanted the unary integer negation operator and not something else, so I don't get a spurious warning

(No one agrees with me but I still think SML was right to use ~ for integer negation.)

EDIT: Seems the problem isn't unary - at all but that Haskell feels unsure this is int and not some other Num. -(1 :: Int) solved it.

Questa voce è stata modificata (5 mesi fa)
in reply to Amélia Liao

@amy Hmm, it still puts a warning on "negate". I wonder if there's some way to explicitly tell it which instance of Num I meant by 1?
in reply to mcc

oh right. sry i thought this was the other issue with the unary negation operator. times :: Int <- amb [-1, 1] or [-1 :: Int, 1] (or [-1, 1 :: Int] etc)
in reply to mcc

I've reached the part of the puzzle that inspired me to write this in Haskell to start with: The "amb" combinator. I read about this decades ago and it captivated me as the one example I'd seen of what made Haskell's weirdo pure-functional model "useful".

Here's my outsider's awareness of what a monad is:

You write some code, and it's executed in a sequence.
*What does that mean*?

The monad defines what *sequentiality* means. For example "IO" means "linearly in time, as external events allow"

in reply to mcc

(Okay functional fans, THIS is the point where you can reply to object I'm describing monads wrong.)

I think this is why the Haskell folks latch onto category theory. As best I understand, category theory is about explaining what it means to "apply" a thing to another thing:

web.archive.org/web/2025010716…

I think I understand why the correspondence from the category functor to the Haskell functor typeclass is exact, so I'm willing to believe Haskell Monads mostly correspond to…something categorical.

in reply to mcc

Anyway, the Amb monad, as offered by the "nondeterminism" package¹:

hackage.haskell.org/package/no…

Stretches this definition by defining "sequential code" thus: *It is nonsequential*. The Amb monad can fork computations, and answer questions like "do all paths return?" "does ANY path return?" "give me a list of all possible results".

¹ For some reason until today I thought it was a language builtin. It's not; it's just something multiple people have implemented in various ways in the last 30 years.

in reply to mcc

So here's a test program with Amb. You can probably read it without knowing Haskell! "aMemberOf" in sumTen forks the computation by running the code following it with *both* members of the list -1, 1.

We de-monadify this, making it a real single computation, with the nondeterminism package's "isPossible", which returns true if any path does. Does *either* 6 + 4, *or* 6 - 4, sum to ten? 6 + 4 does, so this code prints true. Wow! That was shockingly easy!

in reply to mcc

Then I try to make it *marginally* more complex and I run facefirst into the wall that is Haskell's baroque syntax.

It took me like… 15 minutes to figure out how to modify the type signature of sumTen to take 1 argument. I kept expecting it to be Amb Bool Bool Int but no it's Int -> (Amb Bool Bool). I *think* I now understand why it's the way it is, and I *think?* I also understand why TakeLines upthread returned "IO Int" and not "Int -> IO", but I'm not sure how I was supposed to have known it

in reply to mcc

(Note: If my current mental model of the syntax is correct, this actually isn't a case of Haskell's syntax being overly baroque but a case of it being extremely minimal and things just working out the way they do "by coincidence". I think the problem I'm having is that the code keeps *alternating* being overly smart [like the indentation rules, still which feel complex to me] with being overly bone-dry simple [meaning you get one character off and the compiler can't give you a helpful error].)
in reply to mcc

I think you're right. Layout is smart, but type syntax is minimal and complex things happen by composing things, but basic rules are deceptively simple
in reply to cλémentd

The confusion you mentioned seems to be tied to confusion wrt type parameters, I suggest getting comfortable with it first.
in reply to cλémentd

The syntax is terse and scales impressively well (I'm still impressed by it after all these years), so it's easy to thing it's magic or complex, but in the end it's quite regular. The hardest part is not going too fast
in reply to cλémentd

@clementd Coming from ML, the pattern matching syntax seems more than a little baffling
in reply to mcc

@clementd I've been trying since last night to figure out how to put a second case on this destructure (something like Rust "let [x] = an_array else { panic!("Incorrect array length"); }

github.com/mcclure/aoc2024/blo…

in reply to mcc

Let only works for irrefutable patterns. You need `case` for multiple patterns
in reply to cλémentd

A refutable pattern within a do block will indeed add a `MonadFail` constraint on the whole blocks. If you want to explicitly work with multiple patterns, you'll need `case of`
in reply to cλémentd

@clementd Okay. But I did try that and it wouldn't let me use case…of either.
in reply to mcc

@clementd Incidentally a MonadFail really is what I want, I just wanted to control *the error message* of the MonadFail.
in reply to cλémentd

`case` blocks take regular expressions after `->`, so it would be a new `do` block
in reply to cλémentd

@clementd Thanks. However, adding the single "do" to that previous code results in an even odder error. It looks like the [inFile] <- getArgs was doing some sort of implicit path->string type conversion with the [inFile] -> version can't manage?
in reply to mcc

`Path` is an alias for `String`, so there's no conversion happening, it's two names for the same thing. Here the issue is that you have a `[Path]` where it expects an `IO [Path]`
Questa voce è stata modificata (5 mesi fa)
in reply to mcc

Patter matching itself is not that different imo. Maybe patterns within do blocks? But that's not exactly the same thing. Patterns on the LHS of an arrow are indeed syntactic sugar
in reply to cλémentd

@clementd I'm having a little bit of trouble also with the fact there seems to be implicit pattern matching in "defining multiple versions of" a function, which ML does a bit more explicitly with the | blocks. it's just a lot at once.
in reply to mcc

Ha yes. That's super convenient but indeed that can be surprising
in reply to mcc

If I am compiling a Haskell 2010 program in GHC 9.6.7 on an AMD64 machine, and I multiply two Int numbers to produce a third Int (Int NOT Integer), and the answer would be larger than a 64 bit integer can hold,

What happens?

Silent overflow?

A thrown exception?

in reply to Amélia Liao

@amy Imagine I believe a computation will stay under 63 bits but can't prove this. What level of performance sacrifices am I making if i use Integer instead of Int from the beginning and it turns out the 63 bit threshold is never breached?
in reply to mcc

@amy From my own domain (cryptography): A lot! GHC is very efficient in generating good Assembly from primitive operations (those with '#' at the end, though this is only convention), and libgmp or the other bignum implementations you can use from GHC Haskell are orders of magnitude slower.
in reply to mcc

I am assuming "silent overflow". But, this is based on people from the "FP community" on comp.lang.lisp stating that wrapping around instead of silently going bignum was a Good And Proper Thing To Do, which pretty much destroyed any wish I ever had of going type-hard, because "wrap around" only makes sense for an "integer modulo ..." and then it should not be named "Int".
in reply to mcc

Come use Haskell: Once you can get it to work at all, time travel is free! But good luck getting it to work at all

Working code:

github.com/mcclure/aoc2024/blo…

Things of note:

- Running a time-traveling computation across many parallel universes took 11 lines of code. Splitting a string on colons and then again on spaces took 30

- Keeping with the "hard things are easy, easy things are hard" rule, I switched this code from 64-bit to arbitrary-precision ints by find-replacing "Int" with "Integer".

in reply to mcc

I was really expecting to milk 5-6 more posts out of the process of learning to use "Amb" itself but in fact there was just not much to say! Once I figured out how the base Haskell syntax wanted me to write *anything at all*, the time travel just worked! It's just that figuring out what the base Haskell syntax wanted out of me took all day.
in reply to mcc

Say I want to count the decimal digits in a Haskell Integer (ie, the bignum type). With a regular int I'd cast to double then do ceil(log10(i)), but the number might be poorly representable as double.

I try to look around the Haskell standard library. I get lost. I scroll:

hackage.haskell.org/package/ba…

I find a hackage.haskell.org/package/ba… which looks like it offers a pure-integer arbitrary log base, but it says it's "for back compat" and returns "int#" (?)

Where's the REAL list of integral operations?

in reply to mcc

It seems like if you're already using any kind of bignum type (which Integer is) you ought to be able to at *least* give integer-ceiling log 2 trivially, because you know how many of your bignum constituents you're using to represent the bignum. So even if there's no integer logarithm on Haskell int (64-bit), I'd expect there to be a cheap one on Haskell integer (arbitrary precision).
in reply to mcc

Part 2, which is usually supposed to be a gotcha¹, took about 6 lines of code.

github.com/mcclure/aoc2024/blo…

The only hard part was navigating the Haskell documentation. Which was *very hard* because GHC publishes both an integer-gmp and a ghc-bignum package (which uses gmp) and you have to figure out which one is fake

¹ I suspect, but haven't tested, the true "gotcha" here is that The AOC Writer assumes you're using 64-bit ints, but the part 2 conditions require use of bignums for correct answers.

in reply to mcc

To wrap up: Using Haskell was a powerful argument in favor of *a Haskell-like language* (the monadic style) and I kinda never want to write Haskell again. I found the documentation for both the base language and every library impenetrable; the tools to be clearly powerful at core but full of jagged edges in practice; the syntax got in my way more than it helped; and the ubiquitous <*- type operators constantly inhibited understanding.

But I think I'll try Idris and Purescript later this summer.

in reply to mcc

There's also Elm, which is a Haskell-like browser-side thing. It's about the only way I've found that I can stand to write web front-end stuff.
in reply to mcc

I, in the aspect of Eris, shall now throw a golden ball of discord into the garden of Mastodon functional programmers.

1. If I'm auditioning pure functional languages, should I try PureScript or Elm? Why?

2. What's the difference between `<<` and `<*`?

in reply to mcc

@dysfun one has a Monad constraint and the other has an Applicative constraint. which since 2014 every Monad is necessarily an Applicative you can always use <* and it results in more general code but it used to be the case that << and <* were incomparably general
in reply to mcc

this is coming from someone who has only read a lot of the PureScript docs but has prototyped a couple things in Elm so I'm by no means authoritative

Elm is worth checking out but I think using it for the first time in this context will just lead to pain. it really shines when building interactive things imo and is annoying to do crunchier algorithms in

in reply to iamevn

@iamevn Thanks. Maybe I'll find some other test environment for Elm.
in reply to mcc

Gonna be honest these questions created remarkably little discord among the Mastodon functional programmers. Everyone is pretty much on the same page.
in reply to mcc

is elm still alive? I thought it bit the dust years ago.
in reply to mcc

I was quite fluent in haskell about 10 years ago. I even wrote a proof of concept web browser in it for fun. Bit nowadays I don't understand the documentation anymore. It's getting more and more complex, which is really unfortunate because I loved this language and it will always be a very special language for me
in reply to mcc

i tried to learn it from Monadius game source code...
in reply to mcc

I agree with your intuition. Haskell is very much sharp edges and "in development by too few people", but a nice foundation to build advanced stuff. Like, having an FFI through a cleaner subset of C compilation IR, native codegen without other compilers, minimalist IR (core) that still typechecks like the most user facing source code... it's documentation, polish, usability and more people in front of it that keep it from getting more people in front of it, maybe sometimes by choice. 😅
in reply to Marcel(le)

@mu @takeoutweight One thing I really did notice… the error messages repeatedly failed to highlight "where the actual problem was", and it made me think about how *extremely good* the Rust error messages are. And I don't think that's solely because Rust has different type information, I think it's because the Rust devs view "the error could have been more helpful than it was" as a critical issue. I wonder if Haskell would be easier with a Rust-like level of attention paid to good GHC errors.
in reply to mcc

@takeoutweight A little bit of irony that Haskell spawned Elm, which was an inspiration to better error messages in Rust, but Haskell still did not catch up. There are initiatives, but iiuc the nature of GHC being easy to change by smaller research efforts made it also harder for good error reporting. I assume that with enough effort GHC error messages could become nicer, just there's more people using Rust and Haskell tends to draw researchy folks who are up for some bleeding edge.
in reply to mcc

"Amb" means "with" in Catalan so I keep reading your posts that way. Works pretty well tbh.

mcc reshared this.

in reply to Chris Ford

@cford Interesting! It's supposed to be short for "Ambiguous".

mcc reshared this.

in reply to mcc

@cford

I’ve also seen it referred to as the “angelic operator”, as an angel will swoop in to make sure the right choice was made even retroactively.

I’ve often wondered — and please understand that I know fuckall about physics really — if quantum coherence works something like amb behind the scenes.

#physics

in reply to mcc

The way I normally think of monads is as something that wraps a value in some way, but where you can still operate on that wrapped thing using functions that don't know about monads.

E.g.: async is a monad, but you can still do normal stuff to an async future by awaiting fist. Or list is a monad, but you can still do normal stuff to a list by mapping scalar functions over the list.

mcc reshared this.

in reply to Cassandra is only carbon now

The one being near and dear to my own previously held heart, of course, is that Qubit is a monad that says "hey, this sent a gate to a quantum processor or read a result out at some point," but you can still act classically on results that you get and can still make classical decisions about what gate to apply next.
in reply to mcc

It is correct that category theory is about composing functions and this means it works well when you want to reason about composing functions.

But there's also the part of category theory that's about what you *can't* do. For example consider a Haskell function f with signature:

f :: [t] ->

[t]It's a polymorphic function mapping lists to lists. It has to work for t of any type and that means the function *can't* examine the elements in the list. All f can do is rearrange and copy from its argument, and do the same rearrangement whatever types are in the list. This means we can immediately say a lot about f without looking at the code.

This corresponds very well to the notion of a natural transformation in category theory. The definition of a monad involves two natural transformations.

This ability to talk about what you *can't* do maps well to the idea of an interface that hides implementation details from you. I think that's an important reason why category theory is useful here.

Questa voce è stata modificata (5 mesi fa)
in reply to mcc

my intuition of the monad is that they describe fractals, i.e. any data structure that can reasonable nest itself inside it along with some data, like List of List of List

and this nestedness is useful because it gives us fearless composition. like if we have two parsers `p1` and `p2`, you can nest them in `Parser(p1, p2).join`, like the turtle of the turtle all the way down. `IO` I think is a mini-program-as-data.
eed3si9n.com/monads-are-fracta…

in reply to mcc

I feel like it's more interesting to study what an individual thingy that happens to be a monad does, on a case-by-case basis, than caring about what the super-abstract concept of a monad *is* in general.

Some monads are hacks that let you express something procedural/sequential in a purely functional language. OK, cool, that's useful!

But are monads somehow fundamentally about *sequentiality*? I dunno and I'm reluctant to look into it, because I have doubts about this being interesting.

in reply to mcc

Realtalk: this one-letter-variable-name thing is one of the things that pushed me out of academia.

Read some professor's code. Realized not only was this fine, this was considered standard.

Decided I did not want to deal with this for the next forty years.

mcc reshared this.

in reply to mcc

In both Haskell and ML, it can help to declare types even when it's not required. Doing so acts as a static assertion that the value is indeed that type.

I try to do this for all top-level declarations, at a minimum. This really helps narrow down where the error actually happens.

in reply to mcc

Agree, Haskell's error messages are some of the worst I've ever had to deal with. I think Elm, despite having a similar level of inference, did error messages quite well.
in reply to mcc

return puts a value in the monadic context - it would make a `LineParser a` when you return any `a`. L.decimal is already a LineParser so you don't need that.
in reply to mcc

I'm not a Haskell person, but did you know that an MCC is the South African name for Champagne? 🥂😜

I would think the recommended version would be older because the latest is still in testing... can you think of a better way to release (and publicize, and test) new features?

Anyways love how you're posting about this, I probably wouldn't get any responses if I did, but in going to do it anyway when I learn something new.

in reply to mcc

I wish hls would deprioritize GHC imports in suggestions
in reply to voyd

@voyd I wonder if they'd be receptive to a filed feature request. It would make a lot of sense.
@voyd
in reply to mcc

I'll have a look tomorrow. I tried to make some changes in HLS at one point but got stuck pretty quickly.
in reply to Aliaksei Chareshneu

@alexchareshneu I am writing a program in Haskell because I do not know Haskell. I think it's worth knowing things just to know them.
in reply to mcc

As a Haskell enjoyer, I have no qualms in saying that neither cabal nor stack *feel* good to use, and I fight each of them at different junctures.
in reply to 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
in reply to mcc

I suspect monads are a prank and most of the people claiming to understand them are not in on it
in reply to abadidea

@0xabad1dea For me the confusing part was that all the explanations mistakenly suggest that IO is made functional by monads. It is not; to my understanding IO is special cased in GHC and not actually functional. That it is a monad is also important, but not "the magic sauce".
in reply to GD2

@gd2 @0xabad1dea My understanding is monads are in this case a mechanism for *representing* IO functionally. The model allows it to make everything *except* the IO special casing functional, because your code remains pure transformations which are directed by the special-cased IO ordering.
in reply to mcc

I'm not sure I completely understand them, but what I learned is: You can't have side effects, so if you need to do something which inherently has a side effect (e.g. print something to a terminal, or make a network request), you fix it by putting the side effect in a box which you can pass around. The box can be treated like a normal value from the outside. You can transform the stuff inside the box to something else, but it always has to stay inside the box.
in reply to mcc

Are you open on feedback wrt do / monadic code?
in reply to cλémentd

@clementd In about an hour I will be but right now I'm just trying to find my way around the reference docs and don't have headspace to make sense of whatever you say. Thanks for asking
in reply to mcc

every time I start reading about category theory I get a paragraph deep and my eyes glaze over and I can't. keep. going.

is there a baseline proficiency one can get to with Haskell (or another functional language?) where the category theory stops making your eyes glaze and you experience a spark of recognition?

or do you have to buckle down and do the reading first in order to "get it"

in reply to Scott Cheloha

@scottcheloha I do not know category theory and I am not having problems with Haskell. I think the thing that would help more to learn is "point free programming". Or just say fuck Haskell and go learn an ML (like Rust…)

Incidentally, this is the video that made category theory make sense to me in a way other things before had not. web.archive.org/web/2025010716… I still don't know what it's good for.

in reply to mcc

I like having my code in a cabal project and then I can just specify all those flags in my `.cabal` file and only do `cabal run name -- clioptions`. All these intermediate build steps get taken care of.
in reply to λTotoro

@lambdatotoro Thanks. Can a cabalproject define multiple executables in one project?
in reply to mcc

Absolutely! At work I have a project with three executables and two test suites all in one.
in reply to mcc

To run your program, you can just do
```
runhaskell A.hs
```

If you want the binary, you can do
```
ghc A.hs
```

Which should create a binary A;
```
./A
```

in reply to mcc

chars in Haskell are actually Unicode values. By "char oriented", I assume the documentation is referring to the fact that strings are just linked lists of chars. But for any serious use it is recommended to use the Text datatype which are a more efficient representation of Unicode strings
in reply to mcc

the overcautious approach this language has towards causally chained operations with side effects is what put me off.

from my own compiler building i know that it is *so easy* on the implementation side to first chain ops based on an implicit time order, then later remove all obvious dependencies that don't actually matter (which is easy to prove), which leaves only the monadic dependencies. all optimizing imperative compilers implement this informally in some way.

in reply to mcc

I hesitantly admit I am curious to learn haskell but I have a countdown that has to expire first and that countdown resets any time someone is weird to me about haskell. I've also got one of these for undertale.

Oblomov reshared this.

in reply to aeva

I've also got one for Rust but it's barely worth mentioning until all the ex-haskell people move on to some new shiny thing
in reply to aeva

@aeva I am actually partway into a blog post that I would summarize as "Here's how to start with Rust without being required to like Rust". It was partially inspired by your Difficulties engaging with the (admittedly in its specific ways very frustrating) community
@aeva
in reply to mcc

I'm kinda curious as to trying out Rust some day, but there's a bunch of reasons that have kept me away... one of them is the community (and also the desire to rewrite everything).

Others are things that have come from needing to build packages that use rust -like the compiler being so heavy on RAM (and slow) and it being a hassle to make cargo not use the internet during build time (to the point that I gave up on doing that... it is possible, but yeah.)

And also somehow being unable to do proper dynamic linking kinda bothers me...

Oops, didnt really mean to rant, just to say that yes I also have difficulties.

in reply to Urja

@urja The fact that the build tools are so vague about whether they will/won't work in offline mode, and what they *do* if they are allowed to run in online mode, is indeed a frustration.

I find the lack of dynamic linking more forgivable solely because frankly, it's never worked properly for me in C/C++, so I'm not sure I'm giving up something I actually had.

@Urja
in reply to mcc

yeah,I know what you mean. It’s almost as though knowing Haskell means you forget what not knowing Haskell was like.
in reply to Steve Holden

@holdenweb I think one of the hardest and most important problems in computer programming is learning to think outside of ourselves. After all, almost every single one of us is writing software with the goal of a person other than ourselves actually using it.
in reply to mcc

hence the need foff ref user (or market) research. Helps avoid building systems nobody wants that some nob thought would be a great idea. For example😉
in reply to mcc

I love monads (I don't) and the thing I love about them is I believe it is part of the definition, that they cannot be defined in any way that can be understood...

I've seen c++ takes on them, and those are really easy to understand, which I believe means they are, by definition, not monads.

in reply to mcc

FWIW, `do` is a DSL for working with Monads; using `do` is never required, you can always use plain operators. Admittedly, the same code is usually easier to read and understand in the `do` DSL.
Questa voce è stata modificata (5 mesi fa)
Unknown parent

mastodon - Collegamento all'originale
mcc
@TuffyPuff @0xabad1dea My understanding is "monads are generalized Option" is a statement in-the-know Haskell users would agree with.
Unknown parent

Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun My import says " megaparsec ^>=9.7.0 " and I don't know what version I have
Unknown parent

Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun Hm. That's true. But not, afaict, hspace1 🙁
Unknown parent

mastodon - Collegamento all'originale
mcc

@dysfun This did something I do not understand.

gist.github.com/mcclure/a189ef…

Unknown parent

mastodon - Collegamento all'originale
mcc
@froufox @dysfun `cabal run` gets the same error
in reply to mcc

@dysfun `cabal freeze` should make a lockfile for you called "cabal.project.freeze".

mcc reshared this.

Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun But can't I assign the monad to a let if I do not then *call it*?
in reply to mcc

@dysfun That is, I don't want to invoke it on that line. I just want to define a name.
Unknown parent

in reply to samir, in another castle

@samir @dysfun This is great. If this file is present but dist-newstyle is not, will cabal build automatically consult it? Do you recommend I commit this file to version control?
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun This is a very clear response, thank you.
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun okay. so if I Monad there, it won't care the value is thrown away, the value just gets thrown away by default?
in reply to samir, in another castle

@dysfun Also, I think you got the weird output because you ran `--list installed`, not `list --installed`.
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun I'm pretty sure the value is a value tho, not ()
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun Yes, in fact this is why I'm using megaparsec and not something else >_>
Unknown parent

mastodon - Collegamento all'originale
gaytabase

as for the other thing, i'm a bit out of haskell these days, but maybe it works if you remove the _ <- from the left?

if there is no value to bind, you do not need the left arrow. i hypothesise it's upset that you used anyway for some reason

Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun Honestly, I *don't* feel confident about this code the way I do about Rust or Ocaml code. There is too much magic.
in reply to mcc

@dysfun I *think* so but I feel like there’s some caveats that I’m forgetting. Sorry. If they come to me, I’ll let you know.

Yes, I recommend committing it unless you’re building some kind of general-purpose library.

Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun Yes, because megaparsec has readable documentation, and the simpler regex libraries don't.
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun What if I want to enforce formatting (EG, whitespace allowed in some situations and prohibited in others)?
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun Well, then I'll use megaparsec.
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun Then it can at least cap to within 64 bits…!
Unknown parent

mastodon - Collegamento all'originale
mcc

@amy @dysfun Hm.

This is sincerely interesting to me, but also, given that the Megaparsec solution was 30 lines and this is 20 lines, I'm not sure this is a win.

Unknown parent

mastodon - Collegamento all'originale
Amélia Liao
@dysfun i didn't read the puzzle description but here's a low-dependency implementation using lists for backtracking and the Prelude string handling functions (lines words) and some syntax I can't live without gist.github.com/plt-amy/a20616…
Unknown parent

mastodon - Collegamento all'originale
mcc

@saikou Thanks.

I find a "GHC-bignum" and a "integer-gmp" hackage.haskell.org/package/in… is the latter just an older version of the same thing?

Unknown parent

mastodon - Collegamento all'originale
mcc
@saikou Thank you very much. I haven't got the hang of navigating the doc sites yet.
in reply to mcc

hackage.haskell.org/package/in… says that it's deprecated in favor of the former, so I assume so
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun @darkling The thing that worried me about Elm is I heard that the standard library has special abilities the user code can't because the standard library is exempt from certain typing requirements, but this means the language is inherently biased toward webtech because the standard library has a builtin library for that but doesn't have a whole lot else.

mcc reshared this.

in reply to mcc

@dysfun What is Purescript's performance like? Does it approach Haskell?
in reply to mcc

@dysfun As I understand it, it's *very* much oriented towards running inside a browser. I don't think I'd even think of using it in another context.
Unknown parent

mastodon - Collegamento all'originale
mcc
@dysfun @darkling *tilts head* I'm interested in generating erlang
Unknown parent

mastodon - Collegamento all'originale
mcc
@jaj @dysfun @darkling It's on my list! However I don't think I'll actually reach Erlang, Elixir, Gleam as part of this AOC project because the fact is AOC problem are all about batch processing and I'm not sure that's what the Erlang-VM family actually excels at.
Unknown parent

mastodon - Collegamento all'originale
Jona Joachim
@dysfun
If you're interested in purescript and erlang, you might be interested in the gleam programming language
@mcc @darkling
in reply to mcc

@dysfun Me too. Although running erlang in the browser would be better. 😀

BEAM on WASM?