Newest at the top
| 2026-04-06 11:34:22 +0000 | <ski> | but yes, the implementor of `||' (or whichever callback you're using) |
| 2026-04-06 11:33:54 +0000 | <ski> | there is no `:' is the result of `foldr' |
| 2026-04-06 11:33:44 +0000 | <ski> | not quite that |
| 2026-04-06 11:33:41 +0000 | merijn | (~merijn@host-cl.cgnat-g.v4.dfn.nl) merijn |
| 2026-04-06 11:33:26 +0000 | tromp | (~textual@2001:1c00:340e:2700:795f:6a6f:7cb5:ecd6) (Quit: My iMac has gone to sleep. ZZZzzz…) |
| 2026-04-06 11:29:38 +0000 | <fp`> | and the short-circuiting logic is down to... the implementor of (||)? Or ghc? Or is there even a runtime component? |
| 2026-04-06 11:28:42 +0000 | <fp`> | foldr f acc x:xs = (f x acc) : (foldr f acc xs) |
| 2026-04-06 11:28:41 +0000 | <fp`> | So foldr looks something like this (with an additional boundary condition) |
| 2026-04-06 11:28:22 +0000 | <lambdabot> | True |
| 2026-04-06 11:28:21 +0000 | <ski> | > foldr (||) False (map (> 10) [0 ..]) -- this hands over control to `||', which decides whether to continye with the next recursive `foldr' call or not |
| 2026-04-06 11:26:50 +0000 | <ski> | `foldl' keeps control, until it reaches the end of the list |
| 2026-04-06 11:26:25 +0000 | <ski> | `foldr' hands over control to the callback, and lets it decide if and when to continue with the `foldr' recursive call (being the second parameter passed to the callback) |
| 2026-04-06 11:25:44 +0000 | <lambdabot> | f a (f b (f c z)) |
| 2026-04-06 11:25:42 +0000 | <ski> | > foldr f z [a,b,c] :: Expr |
| 2026-04-06 11:25:40 +0000 | <lambdabot> | f (f (f z a) b) c |
| 2026-04-06 11:25:39 +0000 | <ski> | > foldl f z [a,b,c] :: Expr |
| 2026-04-06 11:25:25 +0000 | <ski> | both `foldl' and `foldr' start "from the left". what differs is the way it "associates" the callback (to the left, like `((z - a) - b) - c', for `foldl (-) z [a,b,c]', vs. to the right, like `a - (b - (c - z))', for `foldr (-) z [a,b,c]'). `foldl' keeps adding to, growing, the accumulator, while `foldr' wraps its recursive calls inside calls to the callback |
| 2026-04-06 11:22:39 +0000 | merijn | (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 255 seconds) |
| 2026-04-06 11:22:33 +0000 | xff0x | (~xff0x@2405:6580:b080:900:388f:e7ec:f5bb:9bd0) |
| 2026-04-06 11:22:03 +0000 | __monty__ | (~toonn@user/toonn) toonn |
| 2026-04-06 11:21:38 +0000 | <ski> | (this is the "direct recursive" version) |
| 2026-04-06 11:21:11 +0000 | <ski> | then basically the same thing would happen, as in the `foldr' version |
| 2026-04-06 11:20:59 +0000 | <ski> | for the last defining equation |
| 2026-04-06 11:20:54 +0000 | <ski> | putStr' (c:cs) = do putChar c; putStr' cs |
| 2026-04-06 11:20:51 +0000 | <ski> | or, if you prefer |
| 2026-04-06 11:20:44 +0000 | <ski> | putStr' (c:cs) = putChar c >> putStr' cs |
| 2026-04-06 11:20:26 +0000 | <ski> | putStr' [ ] = return () |
| 2026-04-06 11:20:16 +0000 | <ski> | btw, do note that if you defined |
| 2026-04-06 11:20:07 +0000 | __monty__ | (~toonn@user/toonn) (Ping timeout: 244 seconds) |
| 2026-04-06 11:18:37 +0000 | <ski> | exactly |
| 2026-04-06 11:18:31 +0000 | <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:08 +0000 | <fp`> | Mm so it's not actually iterating from the right |
| 2026-04-06 11:17:41 +0000 | <ski> | { and now the return () which does nothing } |
| 2026-04-06 11:17:24 +0000 | <ski> | = putChar 'a' >> putChar 'b' >> putChar 'c' >> return () |
| 2026-04-06 11:17:15 +0000 | <ski> | { and now the putChar 'c' happens } |
| 2026-04-06 11:17:04 +0000 | <ski> | = putChar 'a' >> putChar 'b' >> putChar 'c' >> foldr (\c acc -> putChar c >> acc) (return ()) [] |
| 2026-04-06 11:16:53 +0000 | <ski> | { at this point, the putChar 'b' part is executed } |
| 2026-04-06 11:16:34 +0000 | <ski> | = putChar 'a' >> putChar 'b' >> foldr (\c acc -> putChar c >> acc) (return ()) [c'] |
| 2026-04-06 11:16:20 +0000 | <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:15:53 +0000 | merijn | (~merijn@host-cl.cgnat-g.v4.dfn.nl) merijn |
| 2026-04-06 11:14:54 +0000 | <ski> | = putChar 'a' >> foldr (\c acc -> putChar c >> acc) (return ()) ['b','c'] |
| 2026-04-06 11:14:43 +0000 | <ski> | foldr (\c acc -> putChar c >> acc) (return ()) ['a','b','c'] |
| 2026-04-06 11:14:22 +0000 | <ski> | ok, so here's what happens |
| 2026-04-06 11:13:07 +0000 | <fp`> | or foldr (\c acc -> putChar c >> acc) (return ()) "abc" |
| 2026-04-06 11:12:19 +0000 | <fp`> | (\c acc -> putChar c >> acc) |
| 2026-04-06 11:10:41 +0000 | <ski> | (a direct recursion would also be fine) |
| 2026-04-06 11:10:20 +0000 | <ski> | if you define the `foldr' version, i can then explain why it's better here |
| 2026-04-06 11:09:53 +0000 | <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:09:22 +0000 | <ski> | and `>>=' is "bind" |
| 2026-04-06 11:09:12 +0000 | <gentauro> | (Y) |