Home liberachat/#haskell: Logs Calendar

Logs: liberachat/#haskell

←Prev  Next→ 1,789,412 events total
2026-04-06 10:34:04 <ski> you're not using `acc'
2026-04-06 10:34:31 <ski> so you're discarding all `c's but the last one
2026-04-06 10:34:44 <ski> you need to use `>>' (or `do'-notation)
2026-04-06 10:34:51 <ski> (or `>>=')
2026-04-06 10:35:33 <ski> however, i would suggest using `foldr' (or `foldM', perhaps)
2026-04-06 10:36:15 <ski> fp` : perhaps i could show an example, to make you understand what happens, above
2026-04-06 10:36:39 <ski> putStr' ['a','b','c']
2026-04-06 10:36:54 <ski> = foldl (\acc c -> putChar c) (return ()) ['a','b','c']
2026-04-06 10:37:08 <ski> = foldl (\acc c -> putChar c) (putChar 'a') ['b','c']
2026-04-06 10:37:15 <ski> = foldl (\acc c -> putChar c) (putChar 'b') ['c']
2026-04-06 10:37:19 <ski> = foldl (\acc c -> putChar c) (putChar 'c') []
2026-04-06 10:37:26 <ski> = putChar 'c'
2026-04-06 10:37:45 × merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 244 seconds)
2026-04-06 10:37:46 <fp`> Mm I see
2026-04-06 10:37:53 <ski> and then, only at this point, when `foldl' returns, do we actually start to execute the IO-action : putChar 'c'
2026-04-06 10:38:32 <ski> so, you need to combine `acc' with the `putChar' call
2026-04-06 10:38:52 <ski> otherwise you're just throwing away the old value of the accumulator, each time
2026-04-06 10:40:18 <fp`> Why isn't putChar 'a' evaluated? Is this due to some laziness in evaluation?
2026-04-06 10:40:39 <ski> it is evaluated (at the end, after `foldl' returns). and after that, it is executed
2026-04-06 10:42:20 <ski> (but `IO'-actions (in this case, the `putChar' call has type `IO ()') are of an abstract data type, can't really be printed, so i just left the evaluation trace above at showing the `putChar' call)
2026-04-06 10:42:52 <probie> fp`: Because `putChar 'a'` doesn't do anything until it's "evaluated" in the IO monad
2026-04-06 10:43:03 <ski> until it's *executed*
2026-04-06 10:43:09 <probie> > [putChar 'a', putChar 'b', putChar 'c'] !! 1
2026-04-06 10:43:10 <lambdabot> <IO ()>
2026-04-06 10:43:26 <probie> % [putChar 'a', putChar 'b', putChar 'c'] !! 1
2026-04-06 10:43:26 <yahb2> b
2026-04-06 10:43:30 <ski> evaluation of `IO'-actions only determine *which* action to (later, if at all) to perform. it doesn't actually perform/execute/do the action
2026-04-06 10:44:18 merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl)
2026-04-06 10:44:43 <ski> the only two ways to *execute* (not evaluate) an `IO'-action is to (a) make it the value of `main' (or a part of that value); or (b) enter it into the interactor (like e.g. GHCi, or in this case the yahb2 bot)
2026-04-06 10:48:08 <fp`> Mm so the way to make this folding impl of putStr would be to produce a queue of IO actions
2026-04-06 10:48:19 <ski> (of course, only if your `IO'-action is a part of a branch of execution that's activated in `main' will it be executed)
2026-04-06 10:48:30 <ski> yes, basically
2026-04-06 10:48:53 <ski> but that "queue" can just be a single, compound, `IO'-action, that contains all the `putChar' calls, in sequence
2026-04-06 10:49:02 <ski> and the `>>' operator is good for this
2026-04-06 10:49:13 × merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 248 seconds)
2026-04-06 10:49:15 <ski> % putChar 'a' >> putChar 'b'
2026-04-06 10:49:15 <yahb2> ab
2026-04-06 10:49:20 <ski> @type putChar 'a' >> putChar 'b'
2026-04-06 10:49:21 <lambdabot> IO ()
2026-04-06 10:49:45 <ski> that is a single compound action, that performs those two sub-actions, in that order, when it is executed
2026-04-06 10:50:01 × TimWolla quits (~timwolla@2a01:4f8:150:6153:beef::6667) (Quit: Bye)
2026-04-06 10:50:40 <ski> so, try combining your `putChar c' with the `acc' action (representing everything you're planning to do with all the previous `Char'acters you've seen, so far, in the list), combining them with the `>>' operator
2026-04-06 10:50:51 <ski> you could also use `do', if you prefer
2026-04-06 10:50:59 <ski> % do putChar 'a'; putChar 'b'
2026-04-06 10:50:59 <yahb2> ab
2026-04-06 10:56:48 <fp`> Ok yeah I was able to come up with this after perusing the docs of IO, so the lambda should be (\acc c -> acc >> putChar c)
2026-04-06 10:57:40 <fp`> And I guess if I wanted to make putStrLn', I'd just initialize the acc to putChar '\n'
2026-04-06 10:58:18 TimWolla joins (~timwolla@2a01:4f8:150:6153:beef::6667)
2026-04-06 10:59:03 <ski> yes, that's correct
2026-04-06 10:59:11 <ski> now, try to use `foldr' rather than `foldl'
2026-04-06 10:59:26 <ski> "I'd just initialize the acc to putChar '\n'" -- no
2026-04-06 10:59:32 <ski> that would give you
2026-04-06 10:59:51 <ski> putChar '\n' >> putChar 'a' >> putChar 'b' >> putChar 'c'
2026-04-06 10:59:56 <fp`> Ah yeah
2026-04-06 11:00:05 merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl)
2026-04-06 11:02:03 <fp`> But why foldr? My understanding is that one uses foldr for its laziness, which benefits for infinitely long lists, but surely people don't want to print infinitely long strings
2026-04-06 11:02:31 × fun-safe-math quits (~fun-safe-@97.115.234.213) ()
2026-04-06 11:02:40 <ski> using `foldr', you can do `putStr' in constant space. with `foldl', it's not constant space
2026-04-06 11:03:39 <ski> this is because `foldr' can be incremental (depending on the callback you pass it), can return parts of the result, piecemeal, so that you can start executing sub-actions, before you've determined all the sub-actions
2026-04-06 11:04:26 <ski> with `foldl', it *always* executes to the end (reaching the end of the list), before providing any result back to its caller. it's "bulky". so you can't start performing `IO'-sub-actions here, until you've seen the whole list
2026-04-06 11:04:42 fun-safe-math joins (~fun-safe-@97.115.234.213)
2026-04-06 11:04:53 <ski> let's assume you defined
2026-04-06 11:05:05 <ski> putStr' str = foldl (\acc c -> acc >> putChar c) (return ()) str
2026-04-06 11:05:21 <ski> then the evaluation trace looks like
2026-04-06 11:05:21 × merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 272 seconds)
2026-04-06 11:05:27 <ski> putStr' ['a','b','c']
2026-04-06 11:05:44 <ski> = foldl (\acc c -> acc >> putChar c) (return ()) ['a','b','c']
2026-04-06 11:05:54 <ski> = foldl (\acc c -> acc >> putChar c) (return () >> putChar 'a') ['b','c']
2026-04-06 11:06:02 <ski> = foldl (\acc c -> acc >> putChar c) (return () >> putChar 'a' >> putChar 'b') ['c']
2026-04-06 11:06:09 <ski> = foldl (\acc c -> acc >> putChar c) (return () >> putChar 'a' >> putChar 'b' >> putChar 'c') []
2026-04-06 11:06:15 <ski> = return () >> putChar 'a' >> putChar 'b' >> putChar 'c'
2026-04-06 11:06:30 <ski> and only at this point can we start performing, executing, the `putChar' actions
2026-04-06 11:07:02 <ski> so, you can see that the accumulator keeps growing, as `foldl' continues looping through the list, requiring more and more space to be allocated
2026-04-06 11:09:03 <gentauro> ski: what's the name of the `>>` operator? https://hackage-content.haskell.org/package/base-4.22.0.0/docs/Prelude.html#v:-62--62-
2026-04-06 11:09:08 <ski> "then"
2026-04-06 11:09:12 <gentauro> (Y)
2026-04-06 11:09:22 <ski> and `>>=' is "bind"
2026-04-06 11:09:53 <fp`> But why would foldr be better? Since I'm iterating through the list backward, I can't even start thinking about IO actions until I know what the first thing to print is
2026-04-06 11:10:20 <ski> if you define the `foldr' version, i can then explain why it's better here
2026-04-06 11:10:41 <ski> (a direct recursion would also be fine)
2026-04-06 11:12:19 <fp`> (\c acc -> putChar c >> acc)
2026-04-06 11:13:07 <fp`> or foldr (\c acc -> putChar c >> acc) (return ()) "abc"
2026-04-06 11:14:22 <ski> ok, so here's what happens
2026-04-06 11:14:43 <ski> foldr (\c acc -> putChar c >> acc) (return ()) ['a','b','c']
2026-04-06 11:14:54 <ski> = putChar 'a' >> foldr (\c acc -> putChar c >> acc) (return ()) ['b','c']
2026-04-06 11:15:53 merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl)
2026-04-06 11:16:20 <ski> { at this point, `main' / the interactor sees that the action is an `>>'-action, with first sub-action being putChar 'a' so it performs that part now, and then tries to perform the second one, which triggers the evaluation of it, so that `foldr' continunes one more step. i'll show putChar 'a' still as part of the result value (action), but remember that it has already happened now}
2026-04-06 11:16:34 <ski> = putChar 'a' >> putChar 'b' >> foldr (\c acc -> putChar c >> acc) (return ()) [c']
2026-04-06 11:16:53 <ski> { at this point, the putChar 'b' part is executed }
2026-04-06 11:17:04 <ski> = putChar 'a' >> putChar 'b' >> putChar 'c' >> foldr (\c acc -> putChar c >> acc) (return ()) []
2026-04-06 11:17:15 <ski> { and now the putChar 'c' happens }
2026-04-06 11:17:24 <ski> = putChar 'a' >> putChar 'b' >> putChar 'c' >> return ()
2026-04-06 11:17:41 <ski> { and now the return () which does nothing }
2026-04-06 11:18:08 <fp`> Mm so it's not actually iterating from the right
2026-04-06 11:18:31 <ski> do note that, as soon as the putChar 'a' has been executed, we don't need to keep memory around for it (although i showed it still in all the remaining lines above, for clarity), so we can discard that memory, and similarly for the other actions, so therefore we run in constant space
2026-04-06 11:18:37 <ski> exactly
2026-04-06 11:20:07 × __monty__ quits (~toonn@user/toonn) (Ping timeout: 244 seconds)
2026-04-06 11:20:16 <ski> btw, do note that if you defined
2026-04-06 11:20:26 <ski> putStr' [ ] = return ()
2026-04-06 11:20:44 <ski> putStr' (c:cs) = putChar c >> putStr' cs

All times are in UTC.