[HN Gopher] High-order Virtual Machine (HVM): Massively parallel... ___________________________________________________________________ High-order Virtual Machine (HVM): Massively parallel, optimal functional runtime Author : Kinrany Score : 266 points Date : 2022-02-05 09:33 UTC (13 hours ago) (HTM) web link (github.com) (TXT) w3m dump (github.com) | dean177 wrote: | Readers will find HOW.md interesting too: | https://github.com/Kindelia/HVM/blob/master/HOW.md | anentropic wrote: | Wow, I was looking for an ELI5 and I never expected to find one | from the author of such a project... | | I actually feel like I kind of understand it now - kudos to | @LightMachine! | amluto wrote: | It says: | | > Notice also how, in some steps, lambdas were applied to | arguments that appeared outside of their bodies. This is all | fine, and, in the end, the result is correct. | | But one of the example steps is this transformation: | dup a b = {x0 x1} dup c d = {y0 y1} (Pair | lx0(ly0(Pair a c)) lx1(ly1(Pair b d))) | ------------------------------------------------ Dup-Sup | dup c d = {y0 y1} (Pair lx0(ly0(Pair x0 c)) | lx1(ly1(Pair x1 d))) | | I think I'm missing a subtlety. This step substitutes the a/b | superposition into the lambdas, but I don't see how it knows | the the x0 goes into the first lambda and the x1 goes into the | second one. After all, in HVM bizarro-land, lambda arguments | have no scope, so the reverse substitution seems equally | plausible and even syntactically valid: (Pair | lx0(ly0(Pair x1 c)) lx1(ly1(Pair x0 d))) | | This substitution produces two magically entangled lambdas, | which seems fascinating but not actually correct. How does the | runtime (and, more generally, the computational system it | implements) know in which direction to resolve a superposition? | LightMachine wrote: | `dup a b = {x0 x1}` will just substitute `a` by `x0` and `b` | by `x1`, no matter where `a` and `b` are. So, if you apply | that, verbatin, to: (Pair lx0(ly0(Pair a | c)) lx1(ly1(Pair b d))) | | You get: (Pair lx0(ly0(Pair x0 c)) | lx1(ly1(Pair x1 d))) | | Does that make sense? | cwillu wrote: | "In other words, think of HVM as Rust, except replacing the | borrow system by a very cheap .clone() operation that can be | used and abused with no mercy. This is the secret sauce! Easy, | right? Well, no. There is still a big problem to be solved: how | do we incrementally clone a lambda? There is a beautiful answer | to this problem that made this all possible. Let's get | technical!" | | Every third paragraph feels like the autogenerated flavour text | gibberish of a kerbal space program mission description. | LightMachine wrote: | I know it needs improvements, there is even a disclaimer in | the top of HOW.md. I'll go over it and improve sentences like | that in a future. | throwaway17_17 wrote: | I don't know that I would go as far as you did, but the | writing style is a little bit to hyperbolic or maybe just to | rhetorical/conversational for my tastes. | mrpf1ster wrote: | I enjoyed it, you can clearly see the author is excited about | explaining his work. | yewenjie wrote: | So many new programming languages coming up with clever features | that makes me wish I could mix and match their core features | while using libraries from more popular languages. | | Up and coming Languages I am excited about - | | 1. Roc - https://www.youtube.com/watch?v=vzfy4EKwG_Y | | 2. Zig - https://ziglang.org/ | | 3. Scopes - https://sr.ht/~duangle/scopes/ | | 4. Gleam - https://gleam.run/ | | 5. Odin - https://github.com/odin-lang/Odin | | 6. Koka - https://github.com/koka-lang/koka | | 7. Unison - https://github.com/unisonweb/unison | sixbrx wrote: | Don't forget Kind (https://github.com/Kindelia/Kind) which is | mentioned in the "How can I Help?" section as a potential | language for targeting HVM. | throwaway17_17 wrote: | I have seen talks and/or papers on all of these except Scopes | and Gleam. Out of the list the only one that does not provide | something I am interested in is Unison. Given that your | feelings for interesting language features seems to be at least | marginally close to mine I am going to check out Scopes and | Gleam just to see what they have that interested you. | | Personally, from a conceptual level, I find that Koka and Roc | provide some of the more interesting developments in PL design. | For anyone wondering, as to Roc (which I don't think has an | implementation or even a full description yet) I am | particularly interested in their concept and implementation of | the 'platform'/'application' layer idea. As to Koka, the papers | the team has generated are almost all excellent. The paper | describing Perseus, the GC/resource tracking system, and the | compiler implementation review are stunning. | joaogui1 wrote: | There is an initial version of Roc, though the repo is | private and you need to ask Richard Feldman for access | laerus wrote: | there is also maybe Dada https://dada-lang.org/welcome.html | arc776 wrote: | Nim - https://nim-lang.org/ | | Lets you mix and match other libraries with their native ABI as | it compiles to C, C++, ObjC and JS + has excellent FFI. | afranchuk wrote: | Really neat. I've been working on a lazy parallel language and so | I read the "HOW" document closely to see the secret sauce. What's | exciting to me is that, while I'm not familiar with the academia | behind their decisions, it seems that I arrived at the same | result with regard to their lazy clones: I realized early in the | language that caching the eval results would make things _really_ | fast, and this caching ends up doing exactly what their lazy | clones achieves! | | Of course one place I don't cache is in function application | because the language is not pure (so we want side effects to | occur again), but this makes me think it might be really good to | derive which functions/subexpressions are pure and allow the | cache to be retained through function calls for those. | | I'm gonna keep a close watch on this. I should probably read more | white papers :) | LightMachine wrote: | Thank you! | [deleted] | carterschonwald wrote: | The fellow behind this has been hammering at experiments around | so called optimal reduction strategies for a while! | | As another commenter's remarks imply; some of the so called | benefits for what are called optimal reduction strategies are | limited to efficiently manipulating data structures that are | represented by lambdas. And that's also probably why this is | Untyped. Makes it easier to embed / support such an encoding | throwaway17_17 wrote: | Agreed, but the reference he makes to the symmetric interaction | calculus blog post seems to at least have basis in sound | logic/semantic principles. I haven't read the whole post, but I | am at least willing to give it a chance. | carterschonwald wrote: | Linear logic inspired ideas are always great | LightMachine wrote: | Author here. | | HVM is the ultimate conclusion to years of optimal evaluation | research. I've been a great enthusiast of optimal runtimes. Until | now, though, my most efficient implementation had barely passed | 50 million rewrites per second. It did beat GHC in cases where | optimality helped, like l-encoded arithmetic, but in more real- | world scenarios, it was still far behind. Thanks to a recent | memory layout breakthrough, though, we managed to reach a peak | performance of _2.5 billion_ rewrites per second, on the same | machine. That 's a ridiculous 50x improvement. | | That is enough for it to enjoy roughly the same performance of | GHC in normal programs, and even outperform it when automatic | parallelism kicks in, if that counts! Of course, there are still | cases where it will perform worse (by 2x at most usually, and | some weird quadratic cases that should be fixed [see issues]), | but remember it is a 1-month prototype versus the largest lazy | functional compiler in the market. I'm confident HVM's current | design is able to scale and become the fastest functional runtime | in the world, because I believe the optimal algorithm is | inherently superior. | | I'm looking for partners! Check the notes at the end of the | repository's README if you want to get involved. | auggierose wrote: | JFYI, the "This book" link on | https://github.com/Kindelia/HVM/blob/master/HOW.md goes to a | research gate page which has the wrong book for download | (something about category theory). | | So, this dup primitive, is that explained in the book as well, | or is this a new addition in your implementation? | LightMachine wrote: | I fixed the link. This is all part of the system described on | the book, HVM is merely a very fast practical implementation. | The additions (thinks like machine integers and constructors) | don't affect the core. | pchiusano wrote: | This is very cool research that I've been loosely following for | a while but I feel the benchmarks you've presented are very | misleading. They are comparing parallelized code to sequential | code and then making a big deal that it's faster. Of course, | you could also write a parallel Haskell version of the same | benchmarks in about the same amount of code, and I'd expect | similar speedups. | | Haskell doesn't parallelize every independent expression | because it's a general purpose language and that's unlikely to | be the correct choice in all contexts. | | I don't have much of an intuition for whether the optimal | evaluation stuff will be useful in practice for the kinds of | programs people actually write. Like I get that it helps a lot | if you're doing multiplication with Church numerals... but can | you give some more compelling examples? | LightMachine wrote: | I didn't want to be misleading, sorry. My reasoning was to | just benchmark identical programs. | | I personally think Haskell's approach to parallelism is | wrong, though, since it demands in-code annotations to work. | The problem is that the decision on whether an expression | should be parallelized doesn't depend on the code itself, but | on where the expression is used. For example, if `fib(42)` is | the topmost node of your program's normal form, you always | want to parallelize its branches (`fib(41)` and `fib(40)`), | but if it is called in a deeply nested list node, you don't. | That information isn't available on the code of `fib`, so | placing "spark" annotations seems conceptually wrong. | | HVM parallelizes by distributing to-be-evaluated redexes of | the normal form among available cores, prioritizing these | close to root. Here is an animation: | https://imgur.com/a/8NtnEa3. That always seems like the right | choice to me. After all, it just returns the same result, | faster. I could be wrong, though. In which case you think | parallelism isn't the correct choice? Is it because you don't | always want to use the entire CPU? Would love to hear your | reasoning. | | > Like I get that it helps a lot if you're doing | multiplication with Church numerals... | | This isn't about multiplication with Church numerals. Don't | you find it compelling to write binary addition as "increment | N times" and have it be as efficient as the add-with-carry | operation? Making mathematically elegant code fast matters, | and HVM does that. See the overview [0] for instance. Also, | this allows us to have all the "deforestation" optimizations | that Haskell applies on List with hardcoded #rewrite pragmas, | for any user-defined datatype, which is also quite useful. | There are certainly many uses that I'm not creative enough to | think of. | | [0] https://github.com/Kindelia/HVM/blob/master/HOW.md#bonus- | abu... | acchow wrote: | This work is mindblowing me to me. Also I love the README! | | You did mention that you pretty much implement pages 14-39 of | The Optimal Implementation of Functional Programming Languages. | Any idea why the authors of the book didn't implement it | themselves? | | Also, what lead you to this recent memory layout breakthrough. | What was the journey of thinking towards this end? | conaclos wrote: | The How page [0] is nicely written! It was a real pleasure to | read :) | | [0] https://github.com/Kindelia/HVM/blob/master/HOW.md | The_rationalist wrote: | How does it compare to https://github.com/beehive-lab/TornadoVM | omginternets wrote: | If I were serious about porting this project to Go, where would | you recommend that I start? | LightMachine wrote: | You mean, the whole project? Or just the runtime? The runtime | has recently been ported to go by Caue F. He posted it in our | Telegram: https://pastebin.com/0X0bbW8b | | Perhaps you could team up? | omginternets wrote: | Oh my lord, this is seriously the best news of the last | decade. You, sir -- along with Caue F. -- have just made my | _decade_!! | | EDIT: is there a license for this? Can I use it? | LightMachine wrote: | This is MIT licensed, so feel free to fork, use, edit and | sell it without even notifying me. Now go ahead and get | rich! | omginternets wrote: | I'll get right on that. :) | | In all seriousness, thank you. I have exactly zero | experience with Rust, but if I can help with anything, | feel free to reach out: @lthibault on GitHub. | | Moreover, I'll likely be spinning this off into a full- | fledged repo of some sort. I'll keep you updated, and am | more than happy to coordinate our efforts. | | Thanks again, and congratulations on this incredible | piece of engineering! | cauefcr wrote: | Hey, this was my port, nice to see people interested. | | Yeah, it still needs a few changes on the HVM rust | compiler to work, but we could figure it out together if | you want. | | Hit me up on @Cauef on telegram, caue.fcr@gmail.com, or | let's talk on the issue pages of HVM! | auggierose wrote: | And another question, is this for purely functional code only, | or does it / could it also apply to mutable data structures. In | particular, is it somehow related to copy-on-write techniques | (like for example used for Swift's data structures), or is this | something entirely different? | LightMachine wrote: | Since HVM is linear, adding some mutability would be | completely fine and would NOT break referential transparency | (because values only exist in one place!). So, we could | actually have "pure mutable arrays". In order to make that | mix up well with lazy cloning, though, we'd need to limit the | size of array to about 16. But larger arrays could be made as | trees of mutable 16-tuples. There was a post about it, but I | can't find it right now. | herbstein wrote: | Based on my understanding on the HOW.md text this relies | heavily on mathematical equality for performance. In its | execution it is essentially constantly inlining operations as | it goes. | tromp wrote: | This looks pretty awesome, and holds much promise for the future. | Optimal beta reduction has always seemed like something of | theoretical interest only, but Victor has been working many years | on making it more practical, which deserves a lot of respect. | | > HVM files look like untyped Haskell. | | Why not make it look exactly like Haskell? So that every valid | Haskell program is automatically a valid Hvm program, while the | reverse need not hold due to hvm being untyped. | | Instead the syntax looks like a mix of Haskell and Lisp, with | parentheses around application, and prefix operators. I prefer | the cleaner Haskell syntax. | | Some of the speedup examples look somewhat artificial: | | The exponential speedup is on 2^n compositions of the identity | function, which has no practical use. It would be nice to show | such a speedup on a nontrivial function. | | The arithmetical example uses a binary representation, but | strangely uses unary implementation of operators, with addition | implemented as repeated increment, and multiplication as repeated | addition. Why not use binary implementation of operators? Does | that not show a good speedup? | divs1210 wrote: | > I prefer the cleaner Haskell syntax. | | My God, this is written so matter-of-factly that it hurts. | | To me, Lisp is awesome BECAUSE of its uniform syntax! | | To me, Lisp is waaaaaay cleaner and always unambiguous! | | I guess what I'm saying is that it's subjective. | JonChesterfield wrote: | Lisp and Haskell are both easier to read than this syntactic | hybrid of the two, but that might be familiarity | salimmadjd wrote: | I totally agree on the syntax, Haskell is far cleaner. | | I'm not sure what the reasoning was here. I hope the author or | others can shed some light. | sfvisser wrote: | My guess is the author just picked the simplest syntax to | parse in order to demonstrate the tech. If it works and shows | to be successful you can always write a GHC (or whatever) | backend that compiles to this runtime. Maybe come up with | some binary IL etc | hexo wrote: | Or write self-hosting GHC backend that works like HVM does. | That would be very neat. | | For me, these projects (such as HVM) are great research | stuff showing us how to do (or not do) things. | capableweb wrote: | > I'm not sure what the reasoning was here. I hope the author | or others can shed some light. | | From my point of view, the author almost made it into | s-expressions, but decided not to go all the way, which (imo) | would have been amazing and the "cleanest" (although I don't | like using the term "clean" for code, it's so subjective). | I'd like to have some light shed over the decision not to go | all the way. | fithisux wrote: | Not very knowledgeable, does it mean it can be a foundation | for a Scheme compiler? | [deleted] | [deleted] | LightMachine wrote: | Changing it to pure s-expressions would be easy though, I | guess just `let` isn't following that rule? I do love the | no-parenthesis lets though. | capableweb wrote: | On a quick scan, it seems to be some more things than | just `let`. One example would be `(Main n) = (Sum (Gen | n))` should be closer to `(define Main (n) (Sum (Gen | n)))` or similar. Not sure the change would be as easy as | you think, but happy to be proven otherwise ;) | | A `let` could assume un-even amount of forms in it and | only evaluate the last one, using the other ones as | bindings. | | Example from your README: (Main n) = | let size = (\* n 1000000) let list = (Range | size Nil) (Fold list lalb(+ a b) 0) | (define Main (n) (let size (\* n 1000000) | list (Range size Nil) (Fold list lalb(+ a b) | 0))) | | Could even get rid of the parenthesis for arguments | (`(n)` => `n`) and treating forms inside `define` the | same as `let`. | JonChesterfield wrote: | Curious, this suggests (lambda a b form) to go along with | (let a 42 form). I've seen let without the extra parens | but not define or lambda. Thanks for the aside. | LightMachine wrote: | > On a quick scan, it seems to be some more things than | just `let`. One example would be `(Main n) = (Sum (Gen | n))` should be closer to `(define Main (n) (Sum (Gen | n)))` or similar. | | Good point. | | > Could even get rid of the parenthesis for arguments | (`(n)` => `n`) and treating forms inside `define` the | same as `let`. | | No, because non-curried functions are a feature. If we | did that, every function would be curried. Which is nice, | but non-curried functions are faster (lots of lambdas and | wasteful copies are avoided using the equational rewrite | notation), so they should be definitely accessible. | capableweb wrote: | Just wanted to clarify that I definitely don't think this | looks like a bad language as-is, I don't want to give the | impression because of this small preference, it's less | valuable (but I'm sure you knew that already). It's | really impressive design, congratulations on getting it | out for the world to play with :) | klysm wrote: | Well we gotta have _some_ word for subjective quality. | dkarl wrote: | We do have "elegant," which to me means "intellectually | ergonomic." Something can be "beautiful" while being | totally intimidating and impossible for a human to work | with, it can be "easy" despite wasteful complexity | because it you can confidently work through the cruft, | but "elegant" implies both beauty and ease, fitting | naturally to our human mental capabilities without | unnecessary detail. | LightMachine wrote: | The reason for the syntax is that HVM aims to be a low level | compile target, which sounds confusing because closures make | it look very high level, but it should be seen as LLVM IR. It | isn't meant for direct human use on the long term. Ideally it | will be the compile target of languages like Haskell, Lean, | Idris, Agda, Kind (my language) and others. As such, I just | went for a syntax that simplifies and speeds up parsing, | while still keeping it usable if you need to manually edit: | there is no backtracking on this syntax. | conaclos wrote: | Is there some pages that compare Kind and Idris 2? | LightMachine wrote: | There aren't. Kind is more stable, is more JS-looking and | less Haskell-looking, which makes it more practical IMO, | has a super fast JS compiler and you can write React-like | apps on it easily, but some work is needed to improve the | type-checker performance when certain syntax sugars are | used. The core's performance is superb, though. | | Idris2, I can't comment much. All I know is that I envy | its unification algorithm and synthesis features. Its | syntax is more haskellish, if you like that. It seems | less mature in some aspects, though. Very promising | language overall. | LightMachine wrote: | VictorTaelin here. It won't show a speedup because you already | optimized it to a low level algorithm manually! The point of | that benchmark is to stress-test the cost of overwhelming | abstraction. What is really notable is that addition by | repeated increment on HVM performs as well as addition with a | carry bit. The former is an elegant one-liner, the later is a | 8-cases error prone recursive definition. The point is to show | how you can often write highly abstract, elegant mathematical | definitions, and it will perform as well as a manually crafted | version, and that is very cool. | tromp wrote: | Oh, you can write addition as repeated increment, and you get | the efficiency of addition in binary? | | Wow; that certainly is impressive. | LightMachine wrote: | Yes! The fact HVM can apply a function 2^N times in N steps | is still mind-blowing to me. I think solutions to important | problems might come from exploiting this fact. Keep in mind | you need to use some techniques for that to work. In | special, there are 3 important rules. I've just written an | overview here: | https://github.com/Kindelia/HVM/blob/master/HOW.md#bonus- | abu... | chriswarbo wrote: | > Why not make it look exactly like Haskell? So that every | valid Haskell program is automatically a valid Hvm program, | while the reverse need not hold due to hvm being untyped. | | > I prefer the cleaner Haskell syntax. | | "Haskell syntax" is a pretty bad design in my experience. | Firstly, it isn't just one thing: there's Haskell 98 and | Haskell 2010, but most "Haskell" code uses alternative syntax | from GHC extensions (lambda case, GADTs, type applications, | view patterns, tuple sections, etc.). (Frustratingly, those | extensions are often applied implicitly, via a separate .cabal | file, which makes parsing even harder; plus lots of "Haskell | code" is actually written as input for a pre-processor, in | which case all bets are off!) | | GHC alone includes two separate representations of Haskell's | syntax (one used by the compiler, one used by TemplateHaskell). | Other projects tend to avoid both of them, in favour of | packages like haskell-src-exts. | | I've worked on projects which manipulate Haskell code, and | getting it to parse was by far the hardest step; once I'd | managed to dump it to an s-expression representation the rest | was easy! | jsnell wrote: | This had a Show HN a few days ago: | https://news.ycombinator.com/item?id=30152714 | CyberRabbi wrote: | A problem with Haskell and with this is that they are inherently | only suitable for batch processing. You cannot build interactive | systems that have real-time requirements with these languages | because how computation is done is not generally under control of | the programmer. | | People attempt to program interactive systems with Haskell but | then they spend most of their time adapting their program to | GHC's particular evaluation strategy. Usually the optimized | program ends up convoluted and nothing like how Haskell code | should look, additionally still with no guarantees on the run | time. | LightMachine wrote: | That is really not true, but I do agree Haskell passes that | impression, for the wrong reasons. I'm working hard to fix | these misconceptions on HVM, Kind-Lang and related projects! | CyberRabbi wrote: | It is true and it's not a bug. It's a feature. It's an | inherent property of the lazy pure functional programming | model, not any particular language. I can't remember where he | said this but SPJ himself said that was a design goal of | Haskell. In C you must specify both how and where | computations take place. In Java you don't have to worry | about the where (because memory allocation is automatic) but | you still how to worry about the how. Haskell goes one step | further and abstracts both time and space away from the | programmer. Touting this as a benefit of the language but | then denying the negative consequences is attempting to have | your cake and eat it too. | | And for the record Java and other garbage collected languages | are not generally suitable for interactive applications | either. Anyone who has ever waited on a GC pause can attest | to that. This is the exact reason Rust exists and why people | continue to use C/C++ despite being inconvenient languages to | use. | LightMachine wrote: | I do disagree with SPJ here, though. On HVM, you can have | complete control over how the resources (both space and | time) are used in your programs and algorithms. That is why | it outputs the memory (space) and rewrites (time) cost of | each program you run, and it is so precise you can even | make formulas of how much memory and how many rewrites your | functions will demand. You can absolutely have tight memory | control in HVM, just like in C. Being lazy, high-level, | etc. is NOT incompatible with that, I'd argue. | tromp wrote: | Would you say DOOM is interactive with real-time requirements? | | https://github.com/rainbyte/frag | CyberRabbi wrote: | The vast majority of the code in that repository is in the IO | monad and uses carefully placed "!" eager evaluation | annotations. Both facts only serve to reinforce my point that | a programmer must have control over the way in which | computations proceed to build interactive applications. | Haskell does allow some level of control over this as I said | but it ends up not looking like Haskell, it looks like C with | funny syntax. | travv0 wrote: | > The vast majority of the code in that repository is in | the IO monad and uses carefully placed "!" eager evaluation | annotations. | | Looking through that repository, I believe we have | extremely different definitions of "vast majority." | CyberRabbi wrote: | Nearly everything in Render.hs is in the IO monad. That | is a fact, not a subjective opinion. I point out those | routines because they are the heart of the application. | jwlato wrote: | It's hard to build systems with hard real-time requirements | with GHC Haskell. | | The vast majority of systems people build do not have hard | real-time requirements. People have built video games, real- | time audio, and all sorts of other soft-real-time systems with | Haskell | CyberRabbi wrote: | > soft-real-time systems | | No they aren't soft real time systems either, most | applications in general a don't satisfy soft real time | requirements. | | Most people hack it but hacking it with Haskell requires | rewriting your pure functional code into a form that strongly | resembles procedural languages like C. This defeats the | purpose of a lazy pure functional programming language. | mountainriver wrote: | Really awesome, any thoughts on autodiff? | gbrown_ wrote: | I see the author is replying here so I just wanted to say I | enjoyed following the Formality posts on your blog and updates on | GitHub and hope there's time for more of that in the future. | Wishing you all the best with this project! | | As an aside it may be a convenient reference if you laid out the | history of Formality/Kind as I think a large portion of your | target audience already knew about Formality. The rename gets a | little lost in IMHO, at first glance at least. | [deleted] | fulafel wrote: | The submission title says "optimal", the README says "beta- | optimal", what do they mean in this context and are they true? | voxl wrote: | Optimal beta reduction is a very technical term. In effect it | means any "family" of lambda terms will all be reduced "at | once." What is a family? Go take two semesters in rewriting | theory and then perhaps our two heads together can pin it down | exactly. | | In laymen's terms, it means that you're doing a variant of | call-by-need, except you can be absolutely sure you don't copy | applications without need. | | I am personally skeptical it's a useful idea at all. Machine | computation is what we really care about (how fast can you push | instructions through the pipeline) and graph reduction systems | were already all the rage in the 80s. We gave up on them | because they didn't interact with the cache well. | | So, this is the "motherload" of all graph reduction strategies, | yet it will get absolutely trounced in settings like numerical | computation. | Azsy wrote: | As a counter argument ( and i'm not 100% sure it holds up ). | | The cache failures was because the cache was to small. | Whereas now we care about CPU OP data dependencies. | opnitro wrote: | "Optimal" in this context means it never repeats the work of | beta reduction when it doesn't have to. Ie, evaluating `(\x -> | x + x) (2 * 2)` only evaluates `2 * 2` once. | rowanG077 wrote: | Why is this special? Doesn't that mean GHC is beta optimal | since afaik it stores thunks to computations. | opnitro wrote: | GHC is not beta optimal, due to some complications around | what happens when a closures are created. (This is `HOW.md` | claims) | fritzo wrote: | Great work Victor, it's so encouraging to see Asperti & | Guerrini's research receive engineering attention! Anton | Salikhmetov also had some nice implementations [1] a few years | ago. As I understand, even Andrea Asperti didn't believe his | approach was practical [2] :) | | Any idea how hard it would be to extend with a nondeterminism | operator, as in Plotkin's parallel or? (fork a computation and | terminate if either branch terminates) | | [1] https://dblp.org/pid/49/4772.html | | [2] https://arxiv.org/abs/1701.04240 | LightMachine wrote: | Andrea Asperti's only mistake was thinking he was wrong. | Labo333 wrote: | Can someone point me to a reference explaining what beta | reduction is? | tromp wrote: | https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B2-reducti... | | Basically, beta-reduction is the single primitive that performs | all computation in the lambda calculus. When you apply the | function mapping x to M, to argument N, the result is M with | all occurences of x replaced by N. | melony wrote: | Isn't that the process of compilation? | agounaris wrote: | Is there any practical example on this? Whats a common use | casE? | chriswarbo wrote: | > Whats a common use case? | | If we expand the discussion from "lambda calculus" to | "pretty much every programming language", then "beta | reduction" becomes "calling a function with an argument" | | > Is there any practical example on this? | | Given the above, you'll be able to find beta-reduction in | practically all software ever written ;) | opnitro wrote: | Any evaluation of a functional-based language is going to | use this. Here's a simple example: | | Let's say I have a function `add1` `add1 = \x -> x + 1` (A | function that takes a value `x` and then computes `x + 1` | And then I write the program: `add1 2` | | If we want to evaluate this program, first we substitute | the variable to get `add1 2` -> `(\x -> x + 1) 2` | | Then we perform "beta reduction" to get: `(\x -> x + 1) 2` | -> `2 + 1` | | Then it's simple arithmetic: `2 + 1` -> `3` | | "The point" here is to have formal rules for defining what | a computation means. This helps with writing proofs about | what programs do, or what is possible for programs to do. | | For example, say I have a simple program with static types. | I might want to prove that if the programs type checks, | type errors will never occur when you run the program. In | order to do this proof, I have to have some formal notion | of what it means to "run the program". For a functional | language, beta reduction is one of those formal rules. | tsimionescu wrote: | I will note though that in the actual lambda calculus | there is no "+", "2" or "1" - it's functions all the way. | | 1 is typically defined in the Church encoding as \f -> \x | -> f x. Addition is \m -> \n -> \f -> \x -> m f (n f x). | Finding the Church encoding of 2 is left as an exceecise. | | We can then use beta reduction to compute 1 + 1 by | replacing m and n with 1: 1 = \f -> \x -> | f x (\m -> \n -> \f -> \x -> m f (n f x)) 1 1 | =>beta \f -> \x -> 1 f (1 f x) \f -> \x | -> 1 f x = (\f -> \x -> f x) f x =>beta 1 f x = f x | \f -> \x -> 1 f (1 f x) <=> \f -> \x -> 1 f (f x) | (\f -> \x -> f x) f (f x) =>beta \f -> \x -> f f x | - the Church encoding of 2. | | This is how computation using beta reduction only looks | like. | opnitro wrote: | Yup, was just trying to give an example that utilized | primitives to make it easier. | tsimionescu wrote: | Yes yes, I wanted to add to your example, not to say that | you said anything wrong! ___________________________________________________________________ (page generated 2022-02-05 23:00 UTC)