20 ms·
Are “jets” a good idea? (2017)
- _bxg1 8y agoThis is the problem I have with Lisp, but exponentially worse. Languages like this try and stretch ever-closer to an imaginary "perfectly pure language". It's an interesting exercise, but in the process they get further and further away from concepts that are palatable to the human mind, and end up esoteric and minimalist beyond usefulness. We sometimes forget that programming languages are not designed for computers, but for humans who want to express ideas to computers.
- bunderbunder 8y agoDo you mean Scheme, specifically? Because I have a hard time seeing that in lisp in general. Racket's small but fairly pragmatic, Clojure is pretty big, and Common Lisp is frankly a bit of a kitchen sink language.
- vinceguidry 8y agoNot your parent, but I personally didn't find S-expression syntax to be superior. Treating code as data and vice versa is metaprogramming, and it should be something you know how to do when you need it, easy enough to where you're not really going out of your way to do it when you do need it, but weird enough so that you can notice it and immediately see that you are going to need to put on your thinking cap in order to proceed further down the road of understanding this program, when you come across it. I've yet to see a better approach than Ruby's.
- taeric 8y agoAmusingly, I've yet to hit as much frustration as I have when using a few ruby packages at work. Holy hell is it confusing to figure out where things came from, sometimes. I don't necessarily care for the data as code, most of the time; but I do like that most everything has a sensible way to serialize to a file. Even better, unless I am wanting to get optimized, the round trip can take care of itself. Curious what the syntax is for a basic list of key value pairs? Well, it is the same as any list of pairs. Which is the same as any list, unless you are trying to get fancy. This freedom really is liberating, at times. Yes, some folks have made macros so that they have a syntax to have a different literal for different styles of maps/tables/etc. By and large, life is easier if you don't use those and just stick to the minimal syntax necessary to do this. Which really helps with your concern. When I can see what the transformation is, I can literally substitute into my mental model what the macro is doing, so that I don't have to juggle a bunch of crap around. And all of this, oddly enough, lands you into not caring if you are calling to a function or a macro. I have more trust in calling things in most lisps I've worked with, because I can understand both ways in terms of themselves easily enough. With ruby, I'm often wary of using a new method in a source file, because I really can't trust all of the interactions that are assumed for it to work out.
- NegativeLatency 8y agoTo be fair there’s a lot of poorly designed hard to read ruby meta programming out there. I find that the language is flexible enough that I never really reach for meta programming when tackling problems.
- taeric 8y agoI've always been amused by how quickly and deeply some people reach for meta programming. To the point that I really just don't get why.
- vinceguidry 8y agoI'm not sure how much of our respective experiences is due to familiarity. For example, I have REPL power tools and a really good understanding of Ruby's object model to help me figure out what something's doing. So that almost never concerns me all that much. Whenever I pull in a new gem, I usually budget a few minutes to wrap my head around the semantics of how the gem is expected to be used. Occasionally the documentation isn't good enough and I need to source dive. What I never have to deal with is new syntax. No matter how Ruby is being used, the syntax is always the same comforting, pleasing-to-read near-prose. What changes when you use metaprogramming in Ruby is semantics. You can't add new syntax to the language unless you actually change the language. Like you, I find people misusing metaprogramming. For me usually this is fairly easy to detect. If I look for the source of a method and it's dynamically generated, then showing the source will take me to where it was generated. I won't pretend to have chosen Ruby over Lisp because I liked the language better. I chose it because I figured it would help me get a job better. But as time went on I found myself at home with Ruby. I don't think it would take me all that long to get used to any given Lisp. But I don't believe it actually gives anyone superpowers, at least, not superpowers you can't have with Ruby. I have yet to hear a lisper actually articulate a solid advantage for code as data. When there isn't any functional reason to choose between alternatives, all there's left is the aesthetic. Which is where all those parenthesis finally become important.
- taeric 8y ago
- _bxg1 8y agoSome Lisps indeed do away with the "minimalist" aspect when it comes to functionality, although I was mainly referring to syntax.
- Retra 8y agoThere aren't any concepts here that are "palatable to the human mind." It's ok to have a bias, but don't be so foolish to pretend it is universal.
- _bxg1 8y agoThere are some very abstract common threads in how the mind conceptualizes the world. See Gestalt Psychology: https://en.m.wikipedia.org/wiki/Gestalt_psychology https://en.m.wikipedia.org/wiki/Gestalt_psychology There's also this; a fascinating study that investigated the ways people intuitively try to articulate algorithms when they haven't been biased by experience with existing programming paradigms: https://dl.acm.org/citation.cfm?id=373422 https://dl.acm.org/citation.cfm?id=373422
- Retra 8y ago>There are some very abstract common threads in how the mind conceptualizes the world. Of course, this means nothing pertaining to a preferred language, formal or natural, for encoding it such notions. >a fascinating study that investigated the ways people intuitively try to articulate algorithms Try to articulate algorithms. And generally fail, because they don't have any experience expressing complex systems in their shoddy, careless notation. Naturally, when the physicist sits down to express her algorithm using Lie theory and tensor calculus, she's really a moron because one should obviously grab an inexperience lay person and have them dictate your notation for you. The natural consequence of this is that you'll see things you couldn't have seen before and discover many novel and interesting structures in the language, through a process of lay magic.
- _bxg1 8y agoThere's no need to be rude. Just as using a more computer-compatible language (assembly, at the extreme) more optimally utilizes the computer's available resources, using languages with cognition-compatible paradigms more optimally utilize the brain's available resources, reducing cognitive load and enabling anyone - intelligent engineer or layperson - to express and mentally parse greater and more complex concepts than they otherwise would have been able to. The power of the Lisp syntax comes from the fact that everything looks the same, but this also makes it harder for a human to parse. For example, using different symbols to wrap function arguments than you do to wrap procedural blocks gives the brain something to latch on to, reducing the cognition necessary to differentiate between the two. This is also why code editors make the same types of tokens the same color. Two big takeaways from the linked study were that event-based programming and set-based operations are intuitive. These aren't layperson schlock, these are real paradigms. Every notation, every language, is made-up. There's nothing fundamentally more true about one or the other; each offers different metaphors for expressing complex concepts. The question is what those metaphors should be, and in my experience many programmers don't think about general human cognition when deciding which to prefer.
- wmf 8y agoThese really minimal languages aren't intended for programmers; they're compiler targets for higher-level languages. So the debate about jets is really about how best to implement language runtimes.
- _bxg1 8y agoI wasn't aware of that, thanks for the clarification
- arethuza 8y agoArguably, that "perfectly pure language" has existed longer than programming has - lambda calculus.
- paradroid 8y agoThis is basically asm or microcode at a higher level.
- theoh 8y agoIt's the opposite.
- paradroid 8y agoThat's a stretch. If the only dimension you care about is whether or not the language spec is guaranteed to be invariant, sure, but that's essentially nitpicking. One more layer of abstraction gets you an asm->asm transpiler that never changes.
- theoh 8y agoTake microcode: one human-visible instruction gets implemented by many microcoded instructions. With jets, a string of many very simple human-visible instructions gets translated to just a few actual machine instructions. So it's the opposite in the sense that a microcoded system expands each instruction you write, while a jet system contracts strings of the programmer's instructions into more efficient code. As one of the comments says: "The point is that jets create a particularly vicious abstraction inversion whereby a programmer must simultaneously think inside the object language and a kind of metalanguage." To take advantage of jets, the programmer has to get the shape of the code right as well as the semantics. It's a bit like writing poetry, with the extra requirements of rhyme and meter in addition to the semantic requirements of straight prose. You have to keep an eye on what jets might match the code you write if you are going to get the required performance. ASM and microcode have none of this intricacy.
- paradroid 8y agoWhat's the best way to say this.. It's a masochistic method of programming where you insert a randomly generated layer of abstraction between the hardware and the programmer, and tell the programmer to make sure to add enough hints to his code to make sure that it runs quickly. The benefits are unclear.
- refulgentis 8y agoDid this end up going anywhere interesting? As written up, it sounds like an attempt to simplify programming that would just turn into a "turtles all the way down" problem.
- akvadrako 8y agoThey haven't made a lot of public gains yet but the project has only recently raised enough money to grow beyond a few developers.
- core-questions 8y ago> Urbit's Nock, which, other than the jet concept, I honestly think is either a strange art project or snake oil, so I won't discuss this further. Urbit is very real, though whether it will go anywhere or not is a different question. You can run the code today, the language is.... weird as, but there it is, running and doing what it does.
- icebraining 8y agoSnake oil != vaporware.
- nv-vn 8y agoUnrelated, but have LTU's site admins just disappeared? I signed up for an account probably over a year ago and it still hasn't been verified.
- nerpderp83 8y agoSame. Maybe you just have to hijack them at ICFP.
- naasking 8y agoThere were some issues with the signup process over the past year. You should email them directly: http://lambda-the-ultimate.org/node/5535 http://lambda-the-ultimate.org/node/5535
- ychen306 8y agoI think the standard term is Idiom Recognition.
- valarauca1 8y ago"Are optimizing compilers a good idea?" Look if you replace 1 representation with another that is _correct_ (to within the human author's reason), but faster. You have the identical discussion, and an answer (yes).
- koala_man 8y agoWell, there's another aspect: "Is it OK to design a language that will be unusable without an optimizing compiler?" Running any normal language without an optimizing compiler is totally fine and routinely done. Here we're talking about languages that might be thousands or millions of times slower.
- setr 8y agoCompared to a optimizing compiler, a fresh implementation for a language would probably also be thousands/millions of times slower, unless the codebase implements the optimizations itself. And then you end up speeding up the compiler to reach a decent state, to speed up the code But time to reach initial compiler is much faster with a jet-based one (there's barely anything to the language). and then you end up adding jets to reach a decent state I'm not sure there's a significant difference in this regard. The main question, I think, is which is easier/simpler/faster to optimize into a proper state, and in the case of compiler optimization being better, if its sufficiently better to beat the ease of initial implementation for a jet-dependent language
- koala_man 8y agoIt would be great if optimizers made code 1000x or 1000000x faster, but apart from some edge cases like the removal of ineffectual loops, disabling optimization in any compiler shows that ~10x is more realistic. My day job is writing compilers (for traditional languages), and I have no doubt that it's significantly easier/simpler/faster to implement the jet based one. However, the jets only apply to known algorithms, so it's really more like an optimized library than a compiler technique. Code that uses the "library" will be really fast with little effort, but anything that requires functionality not directly found in the library will get the 1000-1000000x overhead, while the traditional compiler will run it all with ~10x overhead. I totally agree that it's the perfect fit for long-lived, highly heterogenous, correctness first applications like blockchain, but I don't see how it'll be useful for general purpose languages.
- derefr 8y agoIf you assume the program is being passed through a JIT (a thing that is already common for interpreted code, and which also tries to guarantee that it doesn't change the resulting program's semantics), then one can think of jets as simply explicit hints built into a JIT regarding how to optimize particular known idioms well known in the corpus of programs the JIT was designed to work with. This is exactly how GPU drivers get gradually more optimized for existing games: the driver's shader compiler actually has an ever-growing library of shader-source-code patterns, with hardware-vendor-optimized replacement sections.
- redleggedfrog 8y agoWhat about debugging? Without an IDE and/or a stepping debugger you're in for a lot of no fun.
- nils-m-holm 8y agoAre you referring to debugging in insanely simple languages or to debugging in general? Because I have written code in a text editor and debugged with PRINT (or whatever the language offers) for decades. It was a lot of fun!
- kjeetgill 8y agoThis is already a standard piece of how JITs work today. See the discussion of Java intrinsics and Array.fill() here [0]. > An intrinsic function is applied as a method substitution [...] and the compiler will replace it's apparent source-level implementation with a pre-cooked implementation. In some cases intrinsics are sort of compilation cheats [...] > The intrinsics are all set up in vmSymbols.hpp and if you look, you'll see Arrays.fill is NOT on the list. [...] Because it is something like an intrinsic... [...] What is it then? Arrays.fill is a code pattern substitution kind of compiler shortcut, basically looking for this kind of loop: ``` for (int i = 0; i < arrayB.length; i++) { arrayB[i] = b; } ``` And replacing it with a call into the JVM memset implementation. [0]: http://psy-lob-saw.blogspot.com/2015/04/on-arraysfill-intrinsics-superword-and.html http://psy-lob-saw.blogspot.com/2015/04/on-arraysfill-intrin...
- shub 8y agoThat has bugged me since the first time I read about Nock. JITs use intrinsics to work around inefficiency of generated code when the maintenance burden of the intrinsic is worth the performance boost, and the grand plan is to make a VM so terrible that you need a shitload of intrinsics for programs to terminate before the heat death of the universe? Years later I still haven't gotten past that initial bafflement. It just makes no sense at all.
- JeremyBanks 8y agoHave you taken a math course at any point in your life?
- lmm 8y ago> JITs use intrinsics to work around inefficiency of generated code when the maintenance burden of the intrinsic is worth the performance boost, and the grand plan is to make a VM so terrible that you need a shitload of intrinsics for programs to terminate before the heat death of the universe? Put it this way: the JVM is perhaps the most sophisticated JIT on the planet, maybe a million lines of code, the product of decades of programming research and billions of programmer-hours, and it still needs intrinsics to improve performance. So how about we skip the billions of programmer hours and just use lots of intrinsics? I don't know if it's going to work, but it's interesting enough to be worth a try.
- ianbicking 8y agoIt occurs to me that ASM.js (before WebAssembly) was full of jets. That is, they wrote weird idioms that the interpreter could understand and optimize, while technically keeping the semantics of the language itself (JavaScript). The most visible example was someVar|0, which was a kind of way to assert that the expression would be an integer. Not really a type declaration, since it didn't ensure someVar would be a number, only that the expression was integer. In the examples though, clearly the goal is to have some provability in the core language, make that provability feasible with a small language, and keep that as the language becomes further optimized. It seems subtly different than an optimizing compiler.
- purity_resigns 8y agoBoth BuckleScript and PureScript use the someVar|0 trick.
- theoh 8y agoSomebody mentioned "failure of full abstraction" on HN a few months ago (actually they said something a bit different), which I tracked down to a late 90s paper by Martin Abadi. It refers to a situation where the object code is exploitable because it doesn't fully reflect the semantics of the source code. One use of jets, in the secure code setting, is to provide a core language that is so simple that it can obviously be compiled in a "fully abstract" way. That is my understanding, anyway. The notion of fully abstract compilation seems to be quite hot, and it comes originally from Abadi's paper: http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-154.pdf http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-154.pdf
- cousin_it 8y agoThe language specification should guarantee that when you implement an algorithm from your algorithm book, you get the same time and space complexity as described in the book, with reasonably small constants. You should be able to hit your big-O target without knowing which "jets" get special treatment by the compiler. That's all I ask for, and it's not too much to ask.