Newest at the top
| 2026-05-28 20:31:56 +0000 | takuan | (~takuan@d8D86B9E9.access.telenet.be) (Ping timeout: 266 seconds) |
| 2026-05-28 20:31:03 +0000 | merijn | (~merijn@host-cl.cgnat-g.v4.dfn.nl) merijn |
| 2026-05-28 20:27:44 +0000 | pavonia | (~user@user/siracusa) siracusa |
| 2026-05-28 20:22:37 +0000 | <int-e> | Right. You get a fixed point combinator. You don't get sharing though. (yfix (1:) produces an infinite heap object) |
| 2026-05-28 20:21:38 +0000 | peterbecich | (~Thunderbi@71.84.33.135) peterbecich |
| 2026-05-28 20:13:33 +0000 | <mauke> | https://en.wikipedia.org/wiki/Fixed-point_combinator#Type_for_the_Y_combinator |
| 2026-05-28 20:12:18 +0000 | <c_wraith> | it still requires type recursion to work. The next trick is to reimplement type-level fix |
| 2026-05-28 20:12:17 +0000 | tomsmeding | . o O ( https://downloads.haskell.org/ghc/latest/docs/users_guide/bugs.html#bugs-in-ghc ) |
| 2026-05-28 20:11:41 +0000 | <tomsmeding> | oh, I've seen this before. The NOINLINE is the juicy part :) |
| 2026-05-28 20:10:36 +0000 | <Leary> | tomsmeding: https://gist.github.com/LSLeary/4c7f3ab7622d991cfa22c36efbaf0674 |
| 2026-05-28 20:09:24 +0000 | poscat0x04 | (~poscat@user/poscat) poscat |
| 2026-05-28 20:09:18 +0000 | poscat | (~poscat@user/poscat) (Read error: Connection reset by peer) |
| 2026-05-28 20:07:26 +0000 | <tomsmeding> | can you? If you don't have value recursion, how are you going to loop? |
| 2026-05-28 20:02:42 +0000 | <mauke> | can't you define 'fix' using a newtype? |
| 2026-05-28 20:02:17 +0000 | Sinbad | (~peter@0x5857e667-catv-dyn.rev.komnet.hu) |
| 2026-05-28 19:45:24 +0000 | weary-traveler | (~user@user/user363627) (Ping timeout: 244 seconds) |
| 2026-05-28 19:43:25 +0000 | <int-e> | It's still relevant semantically I guess. Explains why it doesn't matter which of the mutually recursive values you evaluate first. |
| 2026-05-28 19:42:42 +0000 | <int-e> | jaror: That bisection isn't attractive as an actual translation because it would duplicate computations. |
| 2026-05-28 19:37:05 +0000 | <jaror> | Should be just fine even with typed Core |
| 2026-05-28 19:36:14 +0000 | <jaror> | I think this is called Bekić's bisection lemma: https://en.wikipedia.org/wiki/Beki%C4%87's_theorem |
| 2026-05-28 19:35:05 +0000 | <int-e> | Yeah in practice that desugaring is more about translating mutual recusion into a single fixed point. And GHC doesn't even do that; STG has a recursive let (and at the Cmm level that recusion is built into thunks with mutual references on the heap) |
| 2026-05-28 19:33:44 +0000 | <lortabac> | probably impossible with a typed Core |
| 2026-05-28 19:27:08 +0000 | <int-e> | lortabac: This is the hard part of the question whether `fix` and `letrec` can be defined in terms of each other. |
| 2026-05-28 19:27:03 +0000 | TimWolla | (~timwolla@2a01:4f8:150:6153:beef::6667) TimWolla |
| 2026-05-28 19:26:42 +0000 | <geekosaur> | (and IIRC `fix` is defined in terms of `let`?) |
| 2026-05-28 19:25:59 +0000 | <geekosaur> | the Haskell-level `fix` isn't, but `let` is defined as `letrec` in Haskell so must have `fix` semantics |
| 2026-05-28 19:25:45 +0000 | <lortabac> | interesting |
| 2026-05-28 19:24:46 +0000 | <int-e> | yes |
| 2026-05-28 19:24:32 +0000 | <lortabac> | int-e: "where fix is the least fixpoint operator" -> it seems to assume that fix is a primitive |
| 2026-05-28 19:24:27 +0000 | peterbecich | (~Thunderbi@71.84.33.135) (Ping timeout: 265 seconds) |
| 2026-05-28 19:22:24 +0000 | <int-e> | lortabac: check out the (semi-formal) translation in https://www.haskell.org/onlinereport/exps.html#sect3.12 |
| 2026-05-28 19:21:59 +0000 | <c_wraith> | for the purpose of being an intermediate representation... yeah, you could unify them at that point. |
| 2026-05-28 19:21:07 +0000 | ouilemur | (~jgmerritt@user/ouilemur) ouilemur |
| 2026-05-28 19:20:37 +0000 | <lortabac> | ah ok, I see what you mean |
| 2026-05-28 19:20:27 +0000 | <lortabac> | desugaring happens later |
| 2026-05-28 19:20:26 +0000 | <c_wraith> | their type checking behavior is part of the semantics of the construct. |
| 2026-05-28 19:20:04 +0000 | <lortabac> | I may be missing something. The typechecker knows whether it's a 'let' or a lambda and can decide whether to generalize or not |
| 2026-05-28 19:20:00 +0000 | <c_wraith> | I'd argue that if you need special rules for type checking them differently, you haven't implemented one in terms of the other. |
| 2026-05-28 19:19:52 +0000 | TimWolla | (~timwolla@2a01:4f8:150:6153:beef::6667) (Quit: Bye) |
| 2026-05-28 19:19:37 +0000 | ouilemur | (~jgmerritt@user/ouilemur) (Ping timeout: 272 seconds) |
| 2026-05-28 19:18:45 +0000 | <c_wraith> | the thing is, Haskell requires both of those behaviors. (as specified) |
| 2026-05-28 19:18:27 +0000 | <lambdabot> | In the expression: (f 1 :: Int, f 2 :: Double) |
| 2026-05-28 19:18:27 +0000 | <lambdabot> | In the expression: f 2 :: Double |
| 2026-05-28 19:18:27 +0000 | <lambdabot> | Couldn't match expected type ‘Double’ with actual type ‘Int’ |
| 2026-05-28 19:18:26 +0000 | <c_wraith> | > (\f -> (f 1 :: Int, f 2 :: Double)) (+1) |
| 2026-05-28 19:18:08 +0000 | <lortabac> | the real issue is that you need at least one of 'fix' or 'let' as a primitive |
| 2026-05-28 19:17:43 +0000 | <lortabac> | this doesn't sound like a hard blocker |
| 2026-05-28 19:17:30 +0000 | <lortabac> | can't you typecheck before desugaring? |
| 2026-05-28 19:17:19 +0000 | <lambdabot> | (2,3.0) |
| 2026-05-28 19:17:18 +0000 | <c_wraith> | > let f = (+1) in (f 1 :: Int, f 2 :: Double) |