Logs: freenode/#haskell
| 2020-10-05 02:47:54 | <SirClueless> | ski Yeah, tail recursion is another important one. Lots of languages end up *requiring* tail recursion as a result, because in practice it affects correctness even if you are a pure functional language for all formal purposes |
| 2020-10-05 02:48:14 | <ski> | GHC tends to try to not apply "optimizations" which worsen space or time complexity |
| 2020-10-05 02:48:39 | × | Samos quits (~Kira@201.192.165.173) (Quit: WeeChat 2.9) |
| 2020-10-05 02:48:43 | <dsal> | tail recursion is a "how" rather than a "what" |
| 2020-10-05 02:48:49 | <ski> | yes |
| 2020-10-05 02:49:10 | <SirClueless> | Sure, but it doesn't make guarantees that it *will* make certain optimizations, you just have to assume when it will or not |
| 2020-10-05 02:49:22 | <ski> | (e.g. CSE can cause worse space usage, so GHC doesn't do much of it) |
| 2020-10-05 02:49:54 | <SirClueless> | Whether or not you have tail recursion means that certain computations will either complete successfully in a reasonable amount of memory, or blow the stack of every machine ever produced |
| 2020-10-05 02:50:34 | <SirClueless> | Sure it's just "how" not "what" but at some point "how" becomes "this is no longer computable" and every software engineer in existence has to start caring |
| 2020-10-05 02:50:40 | <ski> | i think, as a first approximation, it's quite safe to assume that GHC does by-need / lazy reduction. one can also think of this as graph reduction (with e.g. shared nodes caching results) |
| 2020-10-05 02:51:17 | <ski> | iirc PFDS' analysis of algorithms take this into account |
| 2020-10-05 02:51:22 | <ski> | @where PFDS |
| 2020-10-05 02:51:22 | <lambdabot> | http://www.amazon.com/Purely-Functional-Structures-Chris-Okasaki/dp/0521663504 |
| 2020-10-05 02:52:25 | <ski> | anyway, even with "proper tail recursion" (which is what Scheme calls it), that doesn't exactly mandate how you'll implement it. it doesn't have to be TCO, e.g. .. |
| 2020-10-05 02:52:57 | lagothrix | is now known as Guest19440 |
| 2020-10-05 02:52:57 | × | Guest19440 quits (~lagothrix@unaffiliated/lagothrix) (Killed (tolkien.freenode.net (Nickname regained by services))) |
| 2020-10-05 02:53:03 | → | lagothrix joins (~lagothrix@unaffiliated/lagothrix) |
| 2020-10-05 02:53:55 | <dolio> | Yeah, scheme just says it's bounded, right? |
| 2020-10-05 02:54:37 | <ski> | yep |
| 2020-10-05 02:54:54 | <ski> | must support an unbounded number of active tail calls in bounded space |
| 2020-10-05 02:55:18 | <dolio> | Bounded by what? |
| 2020-10-05 02:55:20 | → | zacts joins (~zacts@dragora/developer/zacts) |
| 2020-10-05 02:57:17 | <dolio> | Doesn't seem like it says. |
| 2020-10-05 02:59:13 | <ski> | iirc, Chicken just uses the C stack, with CPS code, making it ever deeper, never returning in C, instead GCing the "stack" (which acts like a heap), compacting it, when necessary |
| 2020-10-05 02:59:50 | <SirClueless> | I guess I'm too much of a systems programmer to enjoy Haskell :D "You can assume the graph is reduced in a reasonably efficient way" is missing an axis of correctness that matters to me just as much as actually delivering the correct result of the computation |
| 2020-10-05 02:59:58 | ski | . o O ( "CONS Should Not CONS Its Arguments, Part II: Cheney on the M.T.A." by Henry G. Baker in 1995-09 at <https://web.archive.org/web/20200223051632/http://home.pipeline.com:80/~hbaker1/CheneyMTA.html> ) |
| 2020-10-05 03:00:01 | × | torax quits (~torax@185.163.110.116) () |
| 2020-10-05 03:00:46 | <ski> | (and, MIT Scheme remembers the last 100 calls (including tail calls), for debugging purposes) |
| 2020-10-05 03:01:44 | <dolio> | If you care about performance, then implementation details matter, not just the language specification. |
| 2020-10-05 03:02:14 | × | zaquest quits (~notzaques@5.128.210.178) (Quit: Leaving) |
| 2020-10-05 03:02:32 | <quintasan> | holy smokes |
| 2020-10-05 03:02:46 | <quintasan> | I think I'm starting to get it but at the same time I'm not getting it |
| 2020-10-05 03:03:10 | ski | . o O ( "PCLSRing: Keeping Process State Modular" by Alan Bawden at <http://fare.tunes.org/tmp/emergent/pclsr.htm> ; <https://en.wikipedia.org/wiki/PCLSRing> ; "PCLSRING in Semantics" by Robert Harper in 2016-07-11 at <https://existentialtype.wordpress.com/tag/pclsring/> ) |
| 2020-10-05 03:03:14 | <SirClueless> | ^^ Well sure, but for example, using a language whose definition is "it must behave the same as this abstract machine defined here" which is how C or C++ work, means that unless the implementation expands primitive machine ops into non-constant evaluations, you can make guarantees about runtime |
| 2020-10-05 03:03:17 | → | zaquest joins (~notzaques@5.128.210.178) |
| 2020-10-05 03:04:19 | <quintasan> | If I get IO (Int32, Text) and have a record Tea then I can - `fmap (uncurry Tea) $ functionThatGivesIO`. How do I do this for IO [(Int32, Text)] to get IO [Tea]? |
| 2020-10-05 03:05:17 | <koz_> | quintasan: You fmap twice. |
| 2020-10-05 03:05:41 | <dolio> | I don't believe that C and C++ specify enough in the language specification to guarantee good performance. |
| 2020-10-05 03:05:55 | × | brandly quits (~brandly@c-73-68-15-46.hsd1.ma.comcast.net) (Ping timeout: 240 seconds) |
| 2020-10-05 03:06:08 | <koz_> | dolio: Since UB is a thing, they _can't_. |
| 2020-10-05 03:06:42 | <quintasan> | wait, so I just fmap . fmap (uncurry Tea) $ functionThatGivesIOArray ? |
| 2020-10-05 03:06:53 | <koz_> | quintasan: Not quite. |
| 2020-10-05 03:07:05 | <koz_> | But you're on the right track. |
| 2020-10-05 03:07:09 | <koz_> | Consider the type of fmap: |
| 2020-10-05 03:07:11 | <koz_> | :t fmap |
| 2020-10-05 03:07:13 | <lambdabot> | Functor f => (a -> b) -> f a -> f b |
| 2020-10-05 03:07:29 | ski | . o O ( "Exceptional Syntax" by Nick Benton,Andrew Kennedy in 2001-07 at <https://www.microsoft.com/en-us/research/publication/exceptional-syntax/>,<http://lambda-the-ultimate.org/node/1193>,.. ) |
| 2020-10-05 03:07:30 | <koz_> | Now, if f ~ IO, you get (a -> b) -> IO a -> IO b |
| 2020-10-05 03:07:38 | ski | . o O ( ..,<https://web.archive.org/web/20161027043544/http://mjambon.com/extend-ocaml-syntax.html#lettry>,<https://blog.janestreet.com/pattern-matching-and-exception-handling-unite/> ) |
| 2020-10-05 03:08:01 | <SirClueless> | koz_ they can't guarantee runtime of programs that contain UB, for sure, but other programs they might be able to (with a lot of assumptions about the machine you're executing on) |
| 2020-10-05 03:08:06 | <koz_> | Now, in your case, a ~ [(In32, Text)], and the b you want is [Tea]. |
| 2020-10-05 03:08:11 | <koz_> | SirClueless: There's also IB. |
| 2020-10-05 03:08:24 | <koz_> | So no, even if we assume no UB, the standard cannot guarantee very much at all. |
| 2020-10-05 03:08:34 | <koz_> | s/In32/Int32/ |
| 2020-10-05 03:08:46 | <koz_> | So therefore, your function has to have that type. |
| 2020-10-05 03:08:58 | <koz_> | Now, you have something that types that way - fmap (uncurry Tea). |
| 2020-10-05 03:08:58 | <quintasan> | So ideally I'd write a function for [(Int32, Text)] -> [Tea] and fmap this? |
| 2020-10-05 03:09:06 | <koz_> | You can, but in this case, you don't need to. |
| 2020-10-05 03:09:24 | <koz_> | You would have fmap (fmap (uncurry Tea)). |
| 2020-10-05 03:10:02 | <ski> | quintasan : `fmap (map (uncurry Tea)) actionThatGivesIOList',`(fmap . map) (uncurry Tea) actionThatGivesIOList',`(fmap . map . uncurry) Tea actionThatGivesIOList',`map (uncurry Tea) <$> actionThatGivesIOList',`(uncurry Tea <$>) <$> actionThatGivesIOList' |
| 2020-10-05 03:10:21 | × | bloodstalker quits (~bloodstal@46.166.187.154) (Read error: Connection reset by peer) |
| 2020-10-05 03:10:29 | <koz_> | That last one is a step too far IMHO. |
| 2020-10-05 03:10:49 | <SirClueless> | It sounds like they can guarantee more than Haskell can though -- if only because they can't really express lazy evaluation and whether or not its result is immediately available |
| 2020-10-05 03:11:08 | <quintasan> | I still have no idea what <$> and <*> do but I decided that won't stop me from making this work. |
| 2020-10-05 03:11:20 | <koz_> | quintasan: <$> is just infix fmap. |
| 2020-10-05 03:11:22 | <quintasan> | koz_, ski: thanks |
| 2020-10-05 03:12:13 | <koz_> | SirClueless: I don't know how much you could guarantee about the performance of a C program given the standard. |
| 2020-10-05 03:12:21 | <koz_> | You first must ensure that neither IB nor UB applies. |
| 2020-10-05 03:12:23 | → | aarvar joins (~foewfoiew@50.35.43.33) |
| 2020-10-05 03:12:54 | <koz_> | Then after that point, you gotta deal with the fact that the standard makes few promises about, say, type widths. |
| 2020-10-05 03:13:05 | <koz_> | Which might _significantly_ alter, for instance, how many times a loop will execute. |
| 2020-10-05 03:13:54 | <koz_> | There's probably _thousands_ of other small things there too. |
| 2020-10-05 03:14:44 | <koz_> | Maybe it's _different_ to the Haskell case, but 'more'? I doubt it. |
| 2020-10-05 03:14:54 | <ski> | SirClueless : those last links were about interaction of tail calls with exception handlers (not having the dynamic extent of the handler include the tail call) |
| 2020-10-05 03:15:13 | <ski> | this suggests it may be a good idea, having `catchBind :: Exception e => IO a -> (e -> IO b) -> (a -> IO b) -> IO b)' / `handleBind :: Exception e => (e -> IO b) -> (a -> IO b) -> (IO a -> IO b)', and not just `catch :: Exception e => IO a -> (e -> IO a) -> IO a',`handle :: Exception e => (e -> IO a) -> (IO a -> IO a)',`try :: Exception e => IO a -> IO (Either e a)' |
| 2020-10-05 03:15:55 | × | drbean quits (~drbean@TC210-63-209-23.static.apol.com.tw) (Read error: Connection reset by peer) |
| 2020-10-05 03:18:14 | <ski> | (and perhaps some custom syntax for this, like the `let'-`try' above, which OCaml now supports as pattern-matching <https://caml.inria.fr/pub/docs/manual-ocaml/coreexamples.html#s%3Aexceptions>) |
| 2020-10-05 03:19:07 | × | justsomeguy quits (~justsomeg@unaffiliated/--/x-3805311) () |
| 2020-10-05 03:19:09 | <SirClueless> | Maybe I just already understand all the tradeoffs in C better? Like, I can't think of any way to sneak exponential runtime into a C program -- it would take some *very* obscure interactions. But you can certainly sneak some UB into a C program and that's perhaps worse. |
| 2020-10-05 03:19:30 | × | perrier-jouet quits (~perrier-j@modemcable012.251-130-66.mc.videotron.ca) (Quit: WeeChat 2.9) |
| 2020-10-05 03:20:08 | → | perrier-jouet joins (~perrier-j@modemcable012.251-130-66.mc.videotron.ca) |
| 2020-10-05 03:20:49 | → | Stanley00 joins (~stanley00@unaffiliated/stanley00) |
| 2020-10-05 03:21:29 | → | snakemasterflex joins (~snakemast@213.100.206.23) |
| 2020-10-05 03:21:39 | → | tekojo joins (~tekojo@s91904426.blix.com) |
| 2020-10-05 03:21:47 | ski | . o O ( "A Guide to Undefined Behavior in C and C++" by John Regehr, "Part 1" in 2010-07-09 at <https://blog.regehr.org/archives/213>,"Part 2" in 2010-07-23 at <https://blog.regehr.org/archives/226>,"Part 3" in 2010-07-30 at <https://blog.regehr.org/archives/232> ) |
| 2020-10-05 03:22:29 | <koz_> | SirClueless: Do you understand C-as-the-standard-calls-it? Or C-as-your-favourite-compiler-implements-it? |
| 2020-10-05 03:22:36 | <koz_> | Those may, or may not, be the same thing. |
| 2020-10-05 03:23:39 | <SirClueless> | I try, as far as possible, to understand C (and more often C++) as the standard guarantees it. Obviously the C++ standard is like 10k pages or something stupid so there's no way to claim I understand it all. |
| 2020-10-05 03:25:16 | <c_wraith> | The standard guarantees so little in C. I don't think it's actually possible to write non-trivial code that's portable across all spec-compliant C compilers. |
| 2020-10-05 03:25:31 | <SirClueless> | But there are ways to guarantee some things even in standard C++ that you can't really in Haskell afaict. For example the ability to interpret bytes in an IO device DMA buffer as data without any copying. |
| 2020-10-05 03:25:38 | <koz_> | c_wraith: I remember that one C99 draft made memmove impossible to write. |
| 2020-10-05 03:25:55 | × | snakemasterflex quits (~snakemast@213.100.206.23) (Ping timeout: 240 seconds) |
| 2020-10-05 03:27:00 | <c_wraith> | SirClueless: sure you can... but it's going to be awkward to use because Haskell forces you to understand that the backing bytes could change at any time. |
| 2020-10-05 03:29:01 | <SirClueless> | Yes, but using C++ allows you to rely on memory guarantees provided by the device itself. FWIW C++ forces you to understand too that backing bytes can change at any time (it's just less strict about writing programs that fail to acknowledge that fact). This is basically the purpose of the volatile keyword. |
| 2020-10-05 03:29:19 | <c_wraith> | right. The point of Haskell is that you can't fail to acknowledge it. |
| 2020-10-05 03:29:46 | × | zacts quits (~zacts@dragora/developer/zacts) (Ping timeout: 256 seconds) |
| 2020-10-05 03:30:14 | <dolio> | DMA of an IO device is in the C++ standard? |
| 2020-10-05 03:33:08 | <SirClueless> | Yes. In that there are standard-compliant ways that a device vendor can provide you with a buffer that you can use in productive ways. When you do that you rely on the standard, the linux kernel, the x64 ISA, a certain pointer addressing mechanism, etc. but that's all stuff that's well defined even if it's not portable. |
| 2020-10-05 03:34:08 | → | polyrain joins (~polyrain@2001:8003:e501:6901:a41a:145a:3fce:c107) |
| 2020-10-05 03:35:35 | <dolio> | So that's actually a no. |
All times are in UTC.