[HN Gopher] Hurl: A terrible but cute idea for a language
___________________________________________________________________
Hurl: A terrible but cute idea for a language
Author : todsacerdoti
Score : 139 points
Date : 2023-06-19 17:13 UTC (5 hours ago)
(HTM) web link (ntietz.com)
(TXT) w3m dump (ntietz.com)
| smackjer wrote:
| I think I'm gonna hurl!
| [deleted]
| scrame wrote:
| > Oh, also, functions cannot be recursive (without passing in a
| function to itself), because we won't have the function bound to
| a name in the local context when defining itself.
|
| OP Might want to look into the origins of this website name.
| bfung wrote:
| Fun experiment, but feels like a miss not to then explore the
| Optional type ideas and eliminate 'catch' all together.
| DaveFlater wrote:
| Deja vu, I have been in this place before. Prior art: John Aycock
| and Mike Zastre. An Exceptional Programming Language. Proceedings
| of the 2005 International Conference on Programming Languages and
| Compilers.
| https://pages.cpsc.ucalgary.ca/~aycock/papers/plc05.pdf
| shaftoe444 wrote:
| I love it.
| yccs27 wrote:
| Does it really need the toss/return syntax to loop? You could use
| let loop = func(state, self) { let next = foo(state);
| self(foo); }; loop(start, loop);
| amluto wrote:
| > You can toss it, which works a little differently: it traverses
| the stack until you reach a matching catch block, but then you
| can use the return keyword to go back to where the value was
| tossed from. > > I know, it's cursed using return in this unusual
| way. Again, sorry, I didn't make you keep reading. But, the
| reward is that since you got here, you get to see how we can use
| these to create control flow.
|
| Maybe calling it 'return' is cursed, but this concept isn't
| cursed at all. These are resumable exceptions, and I used to love
| them! Windows supports them natively, although I don't think
| they're widely used. Here's the C/C++ extension for Window's
| native "structured exception handling", and ordinary C++
| exceptions are built on top of it:
|
| https://learn.microsoft.com/en-us/cpp/cpp/structured-excepti...
|
| It supports:
|
| > Recognize the exception but dismiss it
| (EXCEPTION_CONTINUE_EXECUTION).
|
| And as a kernel programmer, I really like resumable exceptions --
| most _hardware_ exceptions are resumable, and the kernel uses
| them extensively. When you get a page fault, that 's an
| exception, and the kernel has a handler, and the control flow,
| um, yeets to the handler, and the handler, well, returns (or
| exits or whatever you want to call it) right back to the
| exception site.
|
| And POSIX signals support this too -- it's even the default
| behavior when a signal handler returns. (POSIX signals suck.)
| lnenad wrote:
| FYI Bitlocker shows ntietz as a virus and won't let me access it.
| ntietz wrote:
| Thanks for letting me know! I couldn't find anything about
| Bitlocker as antivirus but assuming you're talking about
| BitDefender (which also has flagged it). I've submitted a false
| positive report.
| LinuxBender wrote:
| It looks like a few vendors are flagging the URL. [1] Not
| sure why. Nothing sticks out at me on urlscan [2] You may
| have to reach out to those vendors to see what is tickling
| their detectors.
|
| [1] - https://www.virustotal.com/gui/url/d4deec9bca49cdedb57d
| 059d9...
|
| [2] - https://urlscan.io/result/157fd01c-3442-43ae-a563-77ac7
| af2dc...
| ntietz wrote:
| Hmm interesting, if anyone can help me track down why I
| would greatly appreciate it!
| firstlink wrote:
| I trust you already obtained the proper licenses from
| your local authorities before designing and implementing
| a new programming language? And those licenses are on
| file with the major security vendors?
| alexisread wrote:
| I did find the licences in the bottom of a locked filing
| cabinet stuck in a disused lavatory with a sign on the
| door saying 'Beware of the Leopard.
| cogman10 wrote:
| If you squint a little, what OP has invented here is pattern
| matching with algebraic data types.
| remram wrote:
| The cute idea is:
|
| > the only control flow you get is error handling
|
| (and function definition/call, just without returns)
| Joker_vD wrote:
| Or you can go all the way in the other direction (double- or
| triple-barreled CPS) and have functions that can't return but
| only call other functions. let fizzbuzz =
| func(ret, fizzbuzz, x, max) { (x == max)(ret, func()
| { let printed = false; (x %
| 3)(func (t0) { let k0 = func() {
| (x % 5)(func (t1) { let k1 = func() {
| let k2 = func() { (x +
| 1)(func (t2) {
| fizzbuzz(ret, fizzbuzz, t2, max);
| }) };
| (printed == false)(func () {
| print(k2, x); }, k2);
| }; (t1 == 0)(func () {
| print(func() { printed =
| true; k1();
| }, "buzz"); }, k1);
| }); }; (t0 ==
| 0)(func() { print(func() {
| printed = true; k0();
| }, "fizz"); }, k0); });
| }); } fizzbuzz($halt, fizzbuzz, 0, 100);
| abecedarius wrote:
| Raph Levien's Io was along those lines (but conciser).
| (Different language than a newer now better-known Io.)
| quickthrower2 wrote:
| Is this a slightly less esoteric version of the Lambda
| calculus?
| camdenlock wrote:
| Irrelevant, but: trans hands clearly made this. Interesting how
| obvious it has become.
|
| (Also, I'm trans, so according to The Rules, you're not allowed
| to get cranky at me for writing this post. Yes, even you there.
| Stop it.)
| debarshri wrote:
| Not to confuse with Hurl.dev[1], which is a net request
| automation tool
|
| [1]https://hurl.dev/
| lfconsult wrote:
| Right.
| kabdib wrote:
| The Icon language (by the Griswalds, the same couple who brought
| us SNOBOL of decades past) has similar control structures for its
| generators.
| chomp5977 wrote:
| [flagged]
| dcsommer wrote:
| I love this exercise. Great job exploring a concept fully. I find
| this article one of the better arguments I've seen yet against
| including exceptions in a language.
| duped wrote:
| While cute this is really close to call/cc and/or algebraic
| effects as an abstraction for control flow, which isn't as much
| "cute" as it is "a powerful way to express programs in as few
| expressions as possible"
| btown wrote:
| Yep! Everyone should read https://overreacted.io/algebraic-
| effects-for-the-rest-of-us/ for how powerful this can be in
| practice. The caller gets to pause execution on the
| raising/hurling of an event - which makes many crazy things
| possible.
| compscigirl wrote:
| I feel like I must be missing something. It seems to me (just
| from reading this post) that if you need algebraic effects
| you've probably painted yourself into a corner and should
| probably reconsider your program's architecture. It seems
| like a good way obscure what the code is really doing and end
| up with poorly organised spaghetti code. In the post he's
| effectively making a function async while pretending it's
| not, or calling a callback but making the top level code's
| problem instead of encapsulating in the associated type. I'm
| sure there's uses for algebraic effects down the line I
| haven't thought of, but I don't understand why these would be
| desired outcomes?
| duped wrote:
| Look at it less like a first class language feature and
| more like a language feature for implementing others in
| terms of it.
| dvt wrote:
| You're not missing anything. Algebraic effects are
| basically typed gotos+[1][2], and are generally a bad idea
| to use and/or implement. They can be useful in a few very
| niche situations when using typed functional languages.
|
| + With added asynchronicity.
|
| [1] http://community.schemewiki.org/?call-with-current-
| continuat...
|
| [2] Some argue call/cc is, in fact, _worse_ than goto:
| https://okmij.org/ftp/continuations/against-callcc.html
| habitue wrote:
| loops are typed gotos, so are if statements, so, really
| are functions.
|
| Goto isn't bad, it's integral to programming. It's just
| too powerful, so we tame it in various ways. Algebraic
| effects are one of those ways.
| dvt wrote:
| > loops are typed gotos, so are if statements, so, really
| are functions.
|
| All of the above don't break the continuity of a
| program's control flow. Maybe you should be arguing with
| Dijkstra, not me: https://www.cs.utexas.edu/users/EWD/tra
| nscriptions/EWD02xx/E...
|
| Pretty sure we've already litigated that goto/jmp is bad,
| which is why it's almost never used in modern C/C++ code
| unless doing very specific things.
| smt88 wrote:
| > _express programs in as few expressions as possible_
|
| I don't find this appealing on its face. Extreme terseness,
| even when done very elegantly, makes programs very hard to read
| and sometimes also hard to maintain.
|
| I suspect that's one of the reasons Lisps (and functional
| languages in general) haven't caught up in popularity even as
| it becomes much easier to adopt one for any target and in any
| organization.
| Yoric wrote:
| > I don't find this appealing on its face. Extreme terseness,
| even when done very elegantly, makes programs very hard to
| read and sometimes also hard to maintain.
|
| In that case, it's more about minimizing the language, rather
| than the programs. call/cc is a single instruction that is
| sufficiently expressive to implement e.g. exceptions,
| coroutines and lots of stuff for which people typically use
| monad-style embeddings.
|
| Making the language easier to specify is generally a good
| thing, because it makes all forms of static analysis and
| compilation easier (at least theoretically) and because there
| are fewer places to hide bugs in the compiler/interpreter. Of
| course, you move the potential bugs to the libraries, but
| that's generally considered better, because it's easier to
| debug.
|
| > I suspect that's one of the reasons Lisps (and functional
| languages in general) haven't caught up in popularity even as
| it becomes much easier to adopt one for any target and in any
| organization.
|
| Well, to be fair, functional constructs have made it to
| pretty much all mainstream languages these days.
| andyferris wrote:
| I believe the point was you can say the same about goto.
| With goto you can replace if/else, for loops, while loops,
| functions, exceptions, etc, making for a very minimal
| language. Yet structured programming is superior.
|
| (I probably don't know enough about call/cc to know if
| that's a totally fair comparison, but languages being more
| restrictive/less flexible can be good overall in terms of
| aiding understanding/reducing bugs/etc)
| ekidd wrote:
| > _call /cc is a single instruction that is sufficiently
| expressive to implement e.g. exceptions, coroutines and
| lots of stuff for which people typically use monad-style
| embeddings._
|
| The flip side of this is that call/cc prevents you from
| relying on a stack, except in cases where you can do heavy
| static analysis. And you virtually never need the full
| power of call/cc unless you're implementing coroutines.
|
| This is the main reason that so few languages support
| call/cc: stacks are a nice implementation technique (as
| opposed to GCed activation records on the heap), and
| call/cc comes with a heavy price for very situational
| value.
|
| One alternative are "escape" continuations, which can only
| be used during the lifetime of the creating block. These
| are stack friendly.
| duped wrote:
| > Extreme terseness, even when done very elegantly, makes
| programs very hard to read and sometimes also hard to
| maintain.
|
| The advantage to something like call/cc is not that you can
| make code extremely terse but rather that there is one code
| path for analyzing control flow and each variant of control
| flow isn't a special case. It's also not more terse at all,
| but rather more explicit.
|
| You don't need to force users to use it, but it is useful to
| define more useful mechanisms like if/else if, match/switch,
| throw/catch/finally, coroutines, etc in terms of an
| abstraction that doesn't break type checking or
| codegeneration.
|
| All that said "call/cc considered harmful" is an old take
| nerdponx wrote:
| Generalized exception-throwing is pretty much the Common Lisp
| condition system.
| lpghatguy wrote:
| This is incredibly funny and also brilliant: toss/return is an
| implementation of algebraic effects!
| gpderetta wrote:
| call/cc as the _only_ control flow primitive!
| firstlink wrote:
| Algebraic effects use delimited continuations (and this
| appears to match toss/return). Call/cc captures an
| undelimited continuation. Totally different.
| juujian wrote:
| "Hurl" is terribly cute, but I suspect you could be even more
| successful if you had called it "yeet" :)
| Deukhoofd wrote:
| Rust already uses that for a similar feature.
|
| https://doc.rust-lang.org/beta/unstable-book/language-featur...
| Quarrelsome wrote:
| hurl has the enjoyable duality where it can mean throw or
| vomit, where in the case of this language's curious
| construction both meanings apply.
| ntietz wrote:
| This was a consideration in naming the language
| Quarrelsome wrote:
| it is most admirable naming <3.
| SashaSirotkin wrote:
| This is very fun!
|
| I am only making suggestion because it rhymes with "hurl" (and it
| works well metaphorically for the repeating tossing/hurling): you
| could introduce a "whirl" keyword or part of the standard library
| as the way to handle loops.
|
| It could be functionally the same as your loop example but
| removes the self-referential first argument (i.e. whirl(count,
| [1,3])).
| thekiptxt wrote:
| In my university, the slang term for an uninitialized variable
| was "dead squirrel". IIRC, some TA had described uninitialized
| C++ variables as follows: "It could be any value: zero, 100, or
| even a dead squirrel." If hurl ever allows uninitialized
| variables, I would love to see use of an uninitialized variable
| to `hurl` a `squirrel`! :)
| SAI_Peregrinus wrote:
| You can make it more terrible (but still Turing complete) by
| removing `toss` and `return`, since you have conditional catches.
| Just nest enough catches so that when you throw the execution
| will start again and skip over any unnecessary code.
| knome wrote:
| Your 'toss' primitive looks like common lisp's condition+restart,
| using a catch-block syntax in place of a `handler-bind` function.
|
| https://stackoverflow.com/questions/36178141/common-lisp-exc...
| [deleted]
| gmalette wrote:
| A few years back I wrote a language called "exceptional" based on
| some shared ideas. Hurl is super neat and you took things further
| than I did.
|
| https://github.com/exceptional-lang/exceptional
| andai wrote:
| To be fair, you did warn me!
| alexisread wrote:
| Exceptions as the underpinning of such a lang is interesting, but
| I think you might be better off with push-prompt, and an
| implementation of delimited dynamic binding to allow eg. Process
| migration.
|
| Wat is a very small language which does fexprs (vau calculus),
| delimited continuations, delimited dynamic binding, custom
| algebraic effects (try catch, fibres) on top of them, modules,
| types, and has a metacircular VM in a few hundred LOC. It's
| missing a native implementation but it's very impressive.
|
| https://github.com/GiacomoCau/wat-js
|
| Links to the formal research are in the readme.
|
| http://axisofeval.blogspot.com/?m=1 Has several good discussions
| on the topics, and are more accessible than the papers.
| tshaddox wrote:
| > Oh, also, functions cannot be recursive (without passing in a
| function to itself), because we won't have the function bound to
| a name in the local context when defining itself. Fun, right?
|
| The startup accelerator that runs this forum is named after a
| pretty neat thing you can do with a language that only has
| anonymous functions:
|
| https://en.wikipedia.org/wiki/Fixed-point_combinator
| Joker_vD wrote:
| You don't even need that, there is a much more efficient way in
| a language that supports let-bindings (but not letrec):
| let f' = func(f, g, other_args...) { // ...
| g(f, g, whatever...); // ... };
| let g' = func(f, g, other_args...) { // ...
| f(f, g, whatever...); // ... }
| let f = func(other_args...) { return f'(f', g', other_args...);
| } let g = func(other_args...) { return g'(f', g',
| other_args...); }
|
| That's basically just closure conversion, only instead of doing
| it the compiler backend, you do it manually yourself.
___________________________________________________________________
(page generated 2023-06-19 23:00 UTC)