top | item 40480056

Hurl, the Exceptional Language

315 points| todsacerdoti | 1 year ago |hurl.wtf

132 comments

order
[+] z3t4|1 year ago|reply
For anyone designing a programming language, enforce namespace to includes/imports! and if possible, don't allow top level side effects.

    let foo = include "lib/foo.hurl"
    foo.init()
it's much easier to reason about then, for example:

    include "lib/foo.hurl" // side effects
    baz(buz) // function and variable that I have no idea if they are in the standard library, or included *somewhere*

That way it's much easier
[+] remram|1 year ago|reply
Preferably enforce that the namespace matches the include/import statement, if the statement doesn't use an explicit name binding...

import "foo/bar" should make foo.* OR bar.* available, not bazz.*. I'm looking at you, Go.

[+] Wonton56|1 year ago|reply
I do not disagree, but I use IntelliJ for work and it shows clearly where some reference is imported from and let you navigate to it with a shortcut. VSCode does similar things with plugins and LSP, just much worse. I cannot work in VSCode because navigating code is so slow. Is this suggestion only useful when you don’t have such tools? It seems impossible to me that people can live without them, at least in a professional setting.
[+] BoppreH|1 year ago|reply
This also allows one to pass parameters to `foo.init()`, something you cannot do with naked imports.
[+] moomin|1 year ago|reply
I mean, it’s a language based around exceptions for flow control, I think the “easy to reason about” ship has sailed.

(Don’t confuse this with me thinking this project is worthless, I think it’s art.)

[+] lhfasufdowedfs|1 year ago|reply
Agreed 100%.

I forked Ruby to have require that didn't clobber the symbol table but then lost interest in Ruby itself because the ecosystem seems unhinged on shared global mutable state.

[+] manusachi|1 year ago|reply
Agree!

For that very reason in Elixir `import` is discouraged in favor of `alias`

[+] sergiotapia|1 year ago|reply
This is precisely why I stopped using Nim. I was going crazy trying to remember what functions were called etc. I could do from x import nil but it felt like fighting the language.
[+] packetlost|1 year ago|reply
This is the chief problem with Python and most Lisps
[+] djha-skin|1 year ago|reply
I mean, for anyone designing a programming language, don't use exceptions as the chief means of control flow.

Critiquing a joke design is of dubious usefulness, at best :)

[+] zeroCalories|1 year ago|reply
I've always kinda hated exceptions as it makes the contract between a caller and a callee hard to determine, and makes your code highly coupled. I prefer the Go or Rust style of handling it through return values. Briefly skimming the language, I'm not sure if there is anything that fixes that?

I think this kind of model could be cool if your IDE could dynamically determine all uncaught exceptions for a function, and lets you jump to each possible place where the exception could be thrown. Not sure how you handle coupling though. This seems like it would result in an insanely volatile control flow graph.

[+] mbmjertan|1 year ago|reply
This is what IntelliJ does for Java. A problem is reported whenever you have a function that throws exceptions and isn’t caught in a caller anywhere in the project, and you can jump to implementation or calls easily.

However, exceptions that a function can throw are part of the function signature in Java unless they extend RuntimeException (and in that case your program won’t compile if you throw an exception without adding it to the signature). While the circumstances in Java make it much easier for IDEs to report uncaught exceptions, it’s a solvable problem for non-runtime exceptions using static analysis.

On the other hand, returning standardised Ok/Err-wrapped values seems like a simpler approach, both in terms of tooling support and developer convenience.

[+] otabdeveloper4|1 year ago|reply
> ...as it makes the contract between a caller and a callee hard to determine, and makes your code highly coupled. I prefer the Go or Rust style of handling it through return values.

There is literally (literally!) no difference at all between throwing and exception and returning it as a variable. Except for the fact that in the exception passing style you have to write the boilerplate by hand, instead of letting the compiler do it for you.

Why anybody with a sane and functioning brain would want to do that by hand in 2024 I will never understand.

[+] NBJack|1 year ago|reply
If go didn't naturally eat error context, I'd like it more. But in the time I've used it, it makes errors much, much more painful to root cause without a debugger.
[+] kibwen|1 year ago|reply
> Now let's see an example with toss. This is used mostly for passing multiple values out of the function. You don't really need it, but it's cute.

Not useful? You've implemented resumable generators! Of course, getting them to do anything except resume immediately might be... exciting. :P Just need to structure your entire codebase as an inside-out stack of `toss`es...

[+] int_19h|1 year ago|reply
Not quite, since with resumable generators you can resume at any later point in the program, while here "return" must be lexically scoped to the handler (whereas in e.g. Python you can call next() wherever).

This is really more like passing a callback through a side channel. "toss" is invoking said callback, and "return" is, well, returning from it.

[+] 1propionyl|1 year ago|reply
Exactly my first thought after reading that.

Part of me wonders if it's a bit of a joke, panning a genuinely useful feature (in any other language) as disposable and silly (in this one).

[+] boromisp|1 year ago|reply
It's more like stack based event propagation.
[+] DeathArrow|1 year ago|reply
>You've implemented resumable generators!

Like yield in C#?

[+] Vivtek|1 year ago|reply
That was my first thought, too. Nice, tiny language with generators? Neat!
[+] kleiba|1 year ago|reply
Right. Lots of fun ahead debugging a larger codebase :-)
[+] adastra22|1 year ago|reply
Yeah that made me chuckle. I wish the languages I use had resumable exception handling. This is a ridiculously good, and extremely useful feature. Great for API callbacks, among other things.
[+] Aeolun|1 year ago|reply
Not related to the project as such, but I am firmly of the opinion that the world would be a better place if more things used the .wtf extension for their domain :)
[+] helix278|1 year ago|reply
This sounds like a weaker form of algebraic effects, but it is still cool to see such a language and see what you can do with it.
[+] levzettelin|1 year ago|reply
I never understood "algebraic effects". But I understand the Hurl docs. Are "algebraic effects" basically the "toss" keyword from Hurl? If so, how are "algebraic effects" stronger than the "toss" keyword?
[+] jokethrowaway|1 year ago|reply
Nice thought experiment. I absolutely hate exceptions and I'd like a language without exception.

They're the goto of our time.

When we have Maybe/Option and Effect/Result, there is really no reason to throw exceptions and having to mentally track where that is being handled.

I'm a bit worried about algebraic effects becoming more popular (and influencing frontend JS - which is already way too complex) because it's promoting throwing "exceptions" to control the flow. All of this to avoid the coloring problem in async/sync? Absolutely not worth it imho.

[+] zbentley|1 year ago|reply
Wow, I hate this. But it’s … oddly almost kinda elegant? In a very hard to mentally model way, but even so.

Speaking more seriously than is perhaps warranted, I’d slightly prefer it if there were syntactically different “catch” constructs for resumable and nonresumable exceptions, which would remove syntactic ambiguity around whether “return” was sending control flow back to the thrower of the nearest immediate exception or not.

Also, the stdlib shouldn’t have chickened out with regular value-returning functions. Just because the dogfood gives you heartburn doesn’t mean you shouldn’t eat it :)

[+] zzo38computer|1 year ago|reply
> Also, the stdlib shouldn’t have chickened out with regular value-returning functions. Just because the dogfood gives you heartburn doesn’t mean you shouldn’t eat it :)

Yes, although an alternative would be to make the syntax if you call the function where a value is expected, then it will automatically catch.

[+] tempodox|1 year ago|reply
Did I get that right that you can catch what was hurled but not what was tossed? That will take some getting used to.

Also, I am concerned about how much Hurl I can write before people start calling me a tosser?

[+] kgeist|1 year ago|reply
'Toss' sounds like an interesting language construct: it walks the stack to find an exception handler and then walks back to where it was to resume execution as if nothing happened. It looks like you can inject additional behavior at runtime using this construct. Usually in object-oriented code you do dependency injection using services's constructors, but 'toss' allows to do it using "toss handlers"?
[+] bradrn|1 year ago|reply
This is quite similar to the Common Lisp conditions system… which I actually don’t know much about, but I do know that it lets you inject behaviour at runtime like this does.
[+] mjbrusso|1 year ago|reply
This is similar `Resume Next` in VB
[+] ceving|1 year ago|reply
Is it implemented using CPS conversion?
[+] baq|1 year ago|reply
It certainly looks like continuation passing everywhere manually.
[+] frithsun|1 year ago|reply
Shame that you guys don't actually believe in this, as it's actually the way.
[+] Alifatisk|1 year ago|reply
I knew I recognized that name, it’s from hurl.dev
[+] MidhaelBollox|1 year ago|reply
You have reached the maximum number of changes allowed. Please subscribe to make more changes.
[+] hgyjnbdet|1 year ago|reply
Naming conflict with Hurl[0] a command line tool that runs HTTP requests defined in a simple plain text format.

[0] https://hurl.dev/docs/manual.html

[+] tgv|1 year ago|reply
Multiple products sharing a name is unavoidable. Github had 90,000 unique repositories in its first year. If each had to have a unique name, it would almost have exhausted the English dictionary. In 2018, it reached 100 million repositories.
[+] samatman|1 year ago|reply
Programming languages live in a separate namespace involved by passing the 'lang' attribute to your search engine of choice.
[+] tombert|1 year ago|reply
Just rebrand this as “algebraic effects” and suddenly every academic will pretend it’s revolutionary.
[+] withoutboats3|1 year ago|reply
Also my thought. It's very interesting how the designers of this language, presumably unaware of algebraic effects, write about it as if it a terrible joke when this is actually one of the trendiest ideas in PL.