Logs: liberachat/#haskell
| 2026-04-06 11:20:51 | <ski> | or, if you prefer |
| 2026-04-06 11:20:54 | <ski> | putStr' (c:cs) = do putChar c; putStr' cs |
| 2026-04-06 11:20:59 | <ski> | for the last defining equation |
| 2026-04-06 11:21:11 | <ski> | then basically the same thing would happen, as in the `foldr' version |
| 2026-04-06 11:21:38 | <ski> | (this is the "direct recursive" version) |
| 2026-04-06 11:22:03 | → | __monty__ joins (~toonn@user/toonn) |
| 2026-04-06 11:22:33 | → | xff0x joins (~xff0x@2405:6580:b080:900:388f:e7ec:f5bb:9bd0) |
| 2026-04-06 11:22:39 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 255 seconds) |
| 2026-04-06 11:25:25 | <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:25:39 | <ski> | > foldl f z [a,b,c] :: Expr |
| 2026-04-06 11:25:40 | <lambdabot> | f (f (f z a) b) c |
| 2026-04-06 11:25:42 | <ski> | > foldr f z [a,b,c] :: Expr |
| 2026-04-06 11:25:44 | <lambdabot> | f a (f b (f c z)) |
| 2026-04-06 11:26:25 | <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:26:50 | <ski> | `foldl' keeps control, until it reaches the end of the list |
| 2026-04-06 11:28:21 | <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:28:22 | <lambdabot> | True |
| 2026-04-06 11:28:41 | <fp`> | So foldr looks something like this (with an additional boundary condition) |
| 2026-04-06 11:28:42 | <fp`> | foldr f acc x:xs = (f x acc) : (foldr f acc xs) |
| 2026-04-06 11:29:38 | <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:33:26 | × | tromp quits (~textual@2001:1c00:340e:2700:795f:6a6f:7cb5:ecd6) (Quit: My iMac has gone to sleep. ZZZzzz…) |
| 2026-04-06 11:33:41 | → | merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl) |
| 2026-04-06 11:33:44 | <ski> | not quite that |
| 2026-04-06 11:33:54 | <ski> | there is no `:' is the result of `foldr' |
| 2026-04-06 11:34:22 | <ski> | but yes, the implementor of `||' (or whichever callback you're using) |
| 2026-04-06 11:34:26 | <ski> | @src (||) |
| 2026-04-06 11:34:27 | <lambdabot> | True || _ = True |
| 2026-04-06 11:34:27 | <lambdabot> | False || x = x |
| 2026-04-06 11:34:43 | <ski> | that short-circuits, does not evaluate the right parameter, in case the left is `True' |
| 2026-04-06 11:39:03 | <fp`> | So the `_' is what causes the short circuiting? |
| 2026-04-06 11:39:19 | <ski> | the short-circuiting is not built-in, is just a consequence of how `||' is defined, and how lazy evaluation works |
| 2026-04-06 11:39:24 | <ski> | yes, in this case |
| 2026-04-06 11:39:27 | <ski> | but if it had said |
| 2026-04-06 11:39:33 | <ski> | True || x = True |
| 2026-04-06 11:39:42 | <ski> | it would also have ignored `x', still short-circuiting |
| 2026-04-06 11:40:03 | <ski> | what matters is whether the parameter is used/demanded/forced, not really whether it's named |
| 2026-04-06 11:40:34 | <fp`> | I see |
| 2026-04-06 11:40:45 | <ski> | of course, if it's not named, then you're not forcing it (unless you use a strictness annotation, e.g. `!_'), so it's not forced unless someone else is forcing it, elsewhere |
| 2026-04-06 11:41:43 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 264 seconds) |
| 2026-04-06 11:41:46 | <ski> | (you could pass the same list to two different places. one might use `_' (not forcing), while the other might force it, naming) |
| 2026-04-06 11:44:28 | × | TimWolla quits (~timwolla@2a01:4f8:150:6153:beef::6667) (Quit: Bye) |
| 2026-04-06 11:45:08 | <fp`> | Also, where does lambdabot's Expr type come from? That would be quite useful to me |
| 2026-04-06 11:45:16 | <ski> | @src any |
| 2026-04-06 11:45:16 | <lambdabot> | any p = or . map p |
| 2026-04-06 11:45:17 | <ski> | @src or |
| 2026-04-06 11:45:17 | <lambdabot> | or = foldr (||) False |
| 2026-04-06 11:45:27 | → | merijn joins (~merijn@62.45.136.136) |
| 2026-04-06 11:45:32 | <ski> | so, the example above is the same as `any (> 10) [0 ..]' |
| 2026-04-06 11:47:09 | <ski> | @hackage simple-reflect |
| 2026-04-06 11:47:09 | <lambdabot> | https://hackage.haskell.org/package/simple-reflect |
| 2026-04-06 11:47:12 | <ski> | fp` ^ |
| 2026-04-06 11:50:54 | <fp`> | Cool. Thank you so much for all the help! |
| 2026-04-06 11:51:36 | <ski> | np |
| 2026-04-06 11:52:09 | × | merijn quits (~merijn@62.45.136.136) (Ping timeout: 248 seconds) |
| 2026-04-06 11:57:16 | → | TimWolla joins (~timwolla@2a01:4f8:150:6153:beef::6667) |
| 2026-04-06 12:01:21 | × | craunts795335385 quits (~craunts@152.32.99.2) (Quit: The Lounge - https://thelounge.chat) |
| 2026-04-06 12:02:34 | × | TimWolla quits (~timwolla@2a01:4f8:150:6153:beef::6667) (Remote host closed the connection) |
| 2026-04-06 12:03:21 | → | merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl) |
| 2026-04-06 12:08:29 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 252 seconds) |
| 2026-04-06 12:08:48 | → | TimWolla joins (~timwolla@2a01:4f8:150:6153:beef::6667) |
| 2026-04-06 12:10:08 | → | raelie joins (~raelie@user/raelie) |
| 2026-04-06 12:13:33 | → | craunts795335385 joins (~craunts@152.32.99.2) |
| 2026-04-06 12:14:46 | × | Pozyomka quits (~pyon@user/pyon) (Quit: brb) |
| 2026-04-06 12:19:08 | → | merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl) |
| 2026-04-06 12:24:01 | × | m quits (~travltux@user/travltux) (Quit: WeeChat 4.7.2) |
| 2026-04-06 12:24:28 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 256 seconds) |
| 2026-04-06 12:26:35 | → | m2 joins (~travltux@user/travltux) |
| 2026-04-06 12:35:11 | → | merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl) |
| 2026-04-06 12:39:43 | → | Pozyomka joins (~pyon@user/pyon) |
| 2026-04-06 12:40:10 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 245 seconds) |
| 2026-04-06 12:40:31 | × | chromoblob quits (~chromoblo@user/chromob1ot1c) (Ping timeout: 264 seconds) |
| 2026-04-06 12:43:56 | → | sonny joins (~sonny@bras-base-london140cw-grc-17-142-113-177-150.dsl.bell.ca) |
| 2026-04-06 12:44:37 | Pixi` | is now known as Pixi |
| 2026-04-06 12:44:51 | × | rainbyte quits (~rainbyte@181.47.219.3) (Read error: Connection reset by peer) |
| 2026-04-06 12:45:42 | → | rainbyte joins (~rainbyte@181.47.219.3) |
| 2026-04-06 12:46:20 | → | merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl) |
| 2026-04-06 12:47:30 | × | Lord_of_Life quits (~Lord@user/lord-of-life/x-2819915) (Excess Flood) |
| 2026-04-06 12:48:53 | → | Lord_of_Life joins (~Lord@user/lord-of-life/x-2819915) |
| 2026-04-06 12:50:25 | → | tromp joins (~textual@2001:1c00:340e:2700:8cf8:7bb7:a0e:7cfa) |
| 2026-04-06 12:51:30 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 248 seconds) |
| 2026-04-06 12:59:08 | × | oats quits (~oats@user/oats) (Read error: Connection reset by peer) |
| 2026-04-06 12:59:22 | → | oats joins (~oats@user/oats) |
| 2026-04-06 13:02:09 | → | merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl) |
| 2026-04-06 13:05:30 | <fp`> | So in the sense that `putChar 'c'` is evaluated to <IO action> and that is eventually executed to produce a 'c' in the tty, who does the executing? Is this a metaphor for how GHC produces code, or is there a runtime that is literally turning IO actions into syscalls? |
| 2026-04-06 13:06:26 | × | tromp quits (~textual@2001:1c00:340e:2700:8cf8:7bb7:a0e:7cfa) (Quit: My iMac has gone to sleep. ZZZzzz…) |
| 2026-04-06 13:07:31 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 264 seconds) |
| 2026-04-06 13:11:30 | <probie> | fp`: Something of type `IO a` is modelled by GHC as function from the current state of the universe, to a pair containing the new state of the universe, and a value of type `a`. As part of generating this "new state of the universe", IO actions happen |
| 2026-04-06 13:14:37 | <probie> | e.g. something of type `Char -> IO ()` is really `Char -> Universe -> ((), Universe)`, and the interface of the IO monad prevents you from using that `Universe` twice. In reality it's actually `Char -> State# RealWorld -> (# State# RealWorld, a #)` |
| 2026-04-06 13:16:44 | <probie> | `State# RealWorld` is a special 0-bit type that's just a token (since you can't actually pass the universe around), and the `(# ..., ... #)` is an "unboxed tuple", which you can think of as a strict tuple with no runtime overhead |
| 2026-04-06 13:17:51 | → | merijn joins (~merijn@host-cl.cgnat-g.v4.dfn.nl) |
| 2026-04-06 13:23:17 | × | merijn quits (~merijn@host-cl.cgnat-g.v4.dfn.nl) (Ping timeout: 252 seconds) |
| 2026-04-06 13:25:01 | → | Square2 joins (~Square@user/square) |
| 2026-04-06 13:25:04 | <ski> | fp` : yes, you can think of it as a run-time that interprets the `IO'-action values, actually performing them |
| 2026-04-06 13:25:52 | <ski> | (for efficiency, it does get compiled, by GHC, to code that directly performs the indicated actions, rather than interpreting data structures) |
| 2026-04-06 13:26:31 | <ski> | if you want to, you could imagine a data type something like |
| 2026-04-06 13:26:56 | <ski> | data MyIO :: * -> * |
| 2026-04-06 13:26:58 | <ski> | where |
| 2026-04-06 13:27:04 | <ski> | Return :: a -> MyIO a |
| 2026-04-06 13:27:17 | <ski> | Bind :: MyIO a -> (a -> MyIO b) -> MyIO b |
| 2026-04-06 13:27:38 | <ski> | OpenFile :: FilePath -> IOMode -> MyIO Handle |
All times are in UTC.