[HN Gopher] Hurl, the Exceptional Language
       ___________________________________________________________________
        
       Hurl, the Exceptional Language
        
       Author : todsacerdoti
       Score  : 250 points
       Date   : 2024-05-26 05:57 UTC (17 hours ago)
        
 (HTM) web link (hurl.wtf)
 (TXT) w3m dump (hurl.wtf)
        
       | kibwen wrote:
       | _> 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...
        
         | adastra22 wrote:
         | 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.
        
           | grumpyprole wrote:
           | OCaml 5 essentially has it, as "effect handlers". It's how
           | lightweight concurrency is implementated.
        
         | 1propionyl wrote:
         | 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).
        
         | DeathArrow wrote:
         | >You've implemented resumable generators!
         | 
         | Like yield in C#?
        
         | kleiba wrote:
         | Right. Lots of fun ahead debugging a larger codebase :-)
        
         | int_19h wrote:
         | 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.
        
         | boromisp wrote:
         | It's more like stack based event propagation.
        
         | Vivtek wrote:
         | That was my first thought, too. Nice, tiny language with
         | generators? Neat!
        
       | hgyjnbdet wrote:
       | 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 wrote:
         | 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.
        
           | thegeekpirate wrote:
           | Everyone understands that, but when the project name you're
           | copying is popular enough (12k stars meets this threshold,
           | personally) and is still actively used and developed, a name
           | change should be more seriously considered as to avoid
           | confusion (which I exhibited, fwiw).
        
             | polytely wrote:
             | Well it's a totally different category of thing (in
             | multiple ways) so I can't imagine people getting confused
             | about this.
        
           | falcor84 wrote:
           | I have a proposal: let's accept as a given that all single
           | words have been taken, and require all new languages/tools to
           | be named with either multiple words, or by including a
           | special character.
           | 
           | Note that C# (aka C Sharp) was way ahead of its time by
           | including both options.
        
             | lylejantzi3rd wrote:
             | If I ever create a language, I'm calling it clang for
             | maximum naming conflict. Am I talking about the c language?
             | The compiler? or this newfangled language written by a
             | completely unqualified web dev? -\\_(tsu)_/-
        
               | bee_rider wrote:
               | How about clang:a markup language to describe things
               | which could be made by blacksmiths.
        
           | aredox wrote:
           | lol
           | 
           | This industry pumps out new model languages every week, but
           | is unable to generate new names with simple Markov chains?
           | 
           | Anyone giving a project a name that is already taken is a
           | clown
        
           | persnickety wrote:
           | Why do names have to come from the English dictionary? Google
           | can't (couldn't) be found in one.
        
             | tgv wrote:
             | The English dictionary was an example. English names are
             | also rather popular among devs. But there aren't over 100M
             | unique words and names in the world, and most of them
             | would, simply put, suck as names for projects.
        
               | persnickety wrote:
               | 100000000 is 8 significant places. If you build words by
               | choosing out of 10 viable following letters out of an
               | alphabet, you'll hit that in 8 characters. That's not an
               | especially long word. (I don't actually know if that's a
               | good heuristic, but at least it gives an idea.)
               | 
               | Maybe a lot of them suck as project names, but name
               | collisions also suck. I'd rather have unique names rather
               | than beautiful but hard to find ones. Collisions are fine
               | if they happen across geographical and cultural
               | boundaries, but the software culture, for better or
               | worse, is pretty global.
        
           | FrenchyJiby wrote:
           | Note that the specific naming of hurl.dev is not random: hurl
           | is a wrapper for curl, where the requests are stored in a
           | plaintext file.
           | 
           | So hurl.dev didn't roll dice and got hurl, but consciously
           | got a close sounding word.
           | 
           | In that sense, that makes one claim over the other a little
           | more valid, in my mind, though you're right that clashes will
           | have to occur.
           | 
           | I did get confused extra hard by the url = hurl.wtf, when
           | hurl.dev is so close yet about different topic.
        
         | samatman wrote:
         | Programming languages live in a separate namespace involved by
         | passing the 'lang' attribute to your search engine of choice.
        
       | helix278 wrote:
       | 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 wrote:
         | 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?
        
           | ctenb wrote:
           | More general than the toss keyword even. If you're interested
           | in concrete examples, you might have a look at the koka
           | documentation
        
             | throw156754228 wrote:
             | As mentioned in another comment here, the unwind is
             | expensive. That is, searching back through the stack
             | finding the original call site that triggered the
             | exception. How do algebraic effects handle that performance
             | hit?
        
           | fwip wrote:
           | You can think of algebraic effects kind of like:
           | 
           | From callsite Foo, I call out to a function with a known
           | name, Bar, with one or more parameters. The runtime (or
           | sufficiently smart compiler) searches upwards in scope, for a
           | handler Baz that provides the function with that name. Baz's
           | Bar is then called, with both the provided parameters, and,
           | crucially, a function that is "the rest of Foo."
           | 
           | So, an implementation of Exception with effects would ignore
           | the resume, and look something like:
           | define_effect Exception {           // only one function
           | provided by Exception, but could have more
           | throw(String)         }         define_handler
           | printExceptions {           throw(msg, resume_func): {
           | println(msg) }         }         define_func getPage(url) {
           | request = http.get(url)           if not request.ok {
           | throw("Could not download") }           return page         }
           | // main entrypoint         withHandlers printExceptions {
           | page = getPage("https://cheese.com")
           | println(page.text)         }
           | 
           | But you could also write an "on error resume next" handler
           | for Exception. Exceptions thrown with this handler would be
           | equivalent to toss. (in real life you'd probably write a
           | different effect, rather than re-using the Exception/throw
           | effect):                   define_handler onErrorResumeNext {
           | throw(msg, resume_func): {              println(msg)
           | // YOLO, call it anyways             withHandlers
           | onErrorResumeNext {               resume_func()             }
           | }         }         withHandlers onErrorResumeNext {
           | page = getPage("https://cheese.com")
           | println(page.text) // Probably uninitialized - a better  :P
           | }
           | 
           | Breaking away from the Exception example, two cool examples
           | are:                   // Stream - potentially infinite
           | and/or asynchronous iterables         // basically just like
           | python generators         define_effect Stream {
           | emit(item)         }         define_handler
           | toList(accumulator) {           emit(item, resume_func): {
           | if item == nil {               return accumulator
           | } else {               accumulator.append(item)
           | withHandlers toList { resume_func() }             }
           | }         }         define_handler take(how_many) {
           | emit(item, resume_func) {             if how_many == 0 {
           | emit nil             } else {               withHandlers
           | take(how_many-1) { resume_func() }             }           }
           | }         define_func fib_stream() {           a, b = 0, 1
           | loop {             emit a             a, b = b, a+b
           | }         }         // Usage         first_five =
           | withHandlers toList {           withHandlers take(5) {
           | fib()           }         }         println("The first five
           | fibonacci numbers are", first_five)
           | 
           | And this one I'm just going to lift from the Unison
           | documentation [1]:                   Each.toList do
           | a = Each.range 0 5           -- beginning of resume_func f_A
           | b = each [1, 2, 3]           -- beginning of resume_func f_B
           | guard (a < b)           -- beginning of resume_func f_C
           | (a, b)         -- yields         [(0, 1), (0, 2), (0, 3), (1,
           | 2), (1, 3), (2, 3)]
           | 
           | Note that this has the same semantics as:
           | results = []         for (a = 0; a < 5 ; a++) {  // for each
           | a, call f_A(a)           foreach b in [1, 2, 3] {  // for
           | each b, call f_B(b)             if not a<b { continue } // if
           | a<b, call f_C(a, b)             results.append((a, b))
           | }         }
           | 
           | And it is possible to do this, because these resumable
           | functions can be resumed an arbitrary number of times - it's
           | up to the handler. They also can pass parameters back to the
           | resume_func.
           | 
           | Exceptions/hurl = exactly 0 resumptions toss = exactly 1
           | resumption effects = 0..many resumptions
           | 
           | [1]: https://share.unison-
           | lang.org/@unison/base/code/releases/3.5...
        
       | masklinn wrote:
       | Hurl seems very close to having a conditions system (a la
       | Smalltalk / CL): unwinding and resuming are just two possible
       | restarts (https://gigamonkeys.com/book/beyond-exception-handling-
       | condi...).
        
       | kgeist wrote:
       | '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 wrote:
         | 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.
        
           | masklinn wrote:
           | It's a very restricted form of conditions, since it only
           | allows resuming.
           | 
           | A conditions system would subsume both `toss` and `hurl`
           | (because you can have an unwinding restart), as well as
           | provide more flexibility: the condition can provide multiple
           | user-defined restarts and the handler can pick between them
           | (statically or dynamically), as well as feed content back
           | into the signalling function.
           | 
           | For instance a classic CL restart is `use-value` which is the
           | conventional name of restarts feeding a default value in case
           | of missing or invalid item.
           | 
           | So let's say that you have a hashmap type, on lookup failure
           | it could trigger a condition with a `use-value` handler to
           | feed it a default value (possibly dynamically constructed),
           | or abort/unwind, it could also have a restart to insert-and-
           | return the value (so would behave as a
           | `defaultdict`/`putIfAbsent`)
        
         | mjbrusso wrote:
         | This is similar `Resume Next` in VB
        
         | tylerhou wrote:
         | Check out Koka, a "legit" language with an algebraic effect
         | system.
         | 
         | https://koka-lang.github.io/koka/doc/book.html#why-handlers
        
       | z3t4 wrote:
       | 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
        
         | moomin wrote:
         | 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.)
        
           | infogulch wrote:
           | I agree, side-effecting imports add to the spooky action at a
           | distance aesthetic.
        
           | karma_pharmer wrote:
           | i think you mean to to say "has sunk"
        
           | groestl wrote:
           | > I mean, it's a language based around exceptions for flow
           | control, I think the "easy to reason about" ship has sailed.
           | 
           | Sometimes I wonder if I'm exceptionally (haha) talented as I
           | personally find the impact of exceptions on flow control
           | pretty easy to grasp. But based on my understanding of other
           | advanced computer language concepts, which is pretty lacking
           | in some regards, I come to the conclusion that it can't be
           | too hard, and people make a lot of fuss about it for no
           | particular reason.
        
             | eyelidlessness wrote:
             | It's largely difficult because either:
             | 
             | - you're working in a language that doesn't have checked
             | exceptions, so the set of potential errors and the set of
             | potentially error-raising calls is infinite but unknowable
             | 
             | - you're working in a language that has checked exceptions,
             | and you hate that it makes you do work, so you catch-
             | rethrow runtime errors that recreate the first scenario
             | 
             | - you're working in a language that has checked exceptions,
             | but someone else did the second scenario so you're in the
             | first scenario anyway
        
               | xboxnolifes wrote:
               | The unchecked exception example doesn't seem any
               | different than using a dynamically typed language and
               | reading return values, and exceptions seem to get
               | significantly more hate than those.
        
               | eyelidlessness wrote:
               | Because even in a dynamically typed language you can
               | generally go look at what the function returns. You can't
               | look at what it throws without walking the entire call
               | stack and inspecting the source code of the runtime.
        
               | groestl wrote:
               | Other programmers tend to be bad at reliably cleaning up
               | resources such as file handles, locks etc, so I need to
               | inspect the whole invocation tree anyways to have an
               | understanding of what runtime implications I've summoned
               | by invoking other people's code.
               | 
               | As for myself, I've lived through the hell that are
               | checked exceptions in Java. You learn that
               | compositionality and checked exceptions are at odds when
               | you try to insert a remoting layer into an application
               | that has grown without IOExceptions. Then you learn that
               | it's actually not necessary to know the set of possible
               | errors, just make sure that you're not a bad programmer
               | as in my first paragraph, and everyone will be fine. This
               | is also something that you can learn from Exceptional
               | C++.
        
               | fiddlerwoaroof wrote:
               | Yeah, I've never understood the complaints about
               | exceptions either: most of the time you want the
               | exceptions to just bubble up anyway because, in that
               | case, you only have to think about the contracts of the
               | functions you interact with and not about the unusual
               | states you might be in. Return-type or return-value based
               | error handling has always seemed to me to be
               | significantly worse.
        
             | retrac wrote:
             | Flow control involving recursion is already well into the
             | weeds. Recursion _and_ exceptions is probably a nightmare
             | for someone not fond of ML or Lisp.
        
               | groestl wrote:
               | But why? I don't get it. You call something. It can
               | break. It will break. Treat it as such wrt to resources
               | you've allocated. You can ignore error details here.
               | 
               | At the highest level of your application (and at a few
               | critical places, executors, retrying strategies etc)
               | handle all the exceptions you know of, and implement a
               | sane default for everything you don't know.
               | 
               | Done.
        
           | andypants wrote:
           | The imported files should really hurl their exported
           | functions, and the importer needs to catch it into a
           | variable.
        
             | fmbb wrote:
             | Oh, dependency injection?
        
               | kdmccormick wrote:
               | Ha!
        
               | cobbal wrote:
               | Maybe even dependency ejection.
        
             | ntietz wrote:
             | That's a great idea. I'll have to do that for the next
             | version.
        
         | BoppreH wrote:
         | This also allows one to pass parameters to `foo.init()`,
         | something you cannot do with naked imports.
        
           | Joker_vD wrote:
           | import foo(42, FULL_OF_EELS) as foo              -:1:8:
           | E0012: Initializer of module "foo" has 3 arguments, 2 were
           | provided.
        
             | endofreach wrote:
             | In case we're still in the design phase of the language,
             | named arguments would help around that (coupled with a good
             | IDE)... but yeah, probably i agree with how i interpret
             | your comment.
             | 
             | Shouldn't be like how parent proposed imports work. Would
             | lead to too much pain.
        
         | remram wrote:
         | 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.
        
           | mikepurvis wrote:
           | Python has this in a slightly different spot. Most pypi
           | packages have the name and module aligned but it's only a
           | matter of convention, and there are some common deviations
           | like the pyyaml package providing the yaml module.
        
             | remram wrote:
             | Ah yes, Python's _package manager_ has it wrong also. But
             | at least Python _the language_ is clear, so you can know
             | "import foo" or "from bar import foo" creates a name "foo"
             | in your file. Go has no such limitation. Imagine doing
             | "import pyyaml" and "yaml" is the name defined...
        
               | jakewins wrote:
               | Wait I'm so confused by this - this is the opposite of
               | how I thought it worked? Go import creates exactly the
               | symbol you mention in the import statement, like "import
               | fmt" creates a symbol "fmt"?
               | 
               | Can you give an example of what you mean?
        
               | sweeter wrote:
               | Yea, this is correct.                   ```go
               | import (            "net/http"            "os"
               | "github.com/anacrolix/torrent"            tstor
               | "github.com/anacrolix/torrent/storage"         )
               | 
               | ```
               | 
               | would import as: http, os, torrent and tstor. That guy is
               | mixed up.
        
           | d-z-m wrote:
           | This is normally how it works, no?
        
           | amscanne wrote:
           | > I'm looking at you, Go.
           | 
           | I'm so confused by this. Unless you use a dot import, isn't
           | this just bar.*?
        
             | remram wrote:
             | I'm not sure what you mean. The name binding created is
             | whatever `package <...>` is in the target package's file,
             | which can be anything.
        
               | amscanne wrote:
               | Ah got it. I think there's such a strong convention
               | there, that it would be exceedingly rare to see in
               | pratice. It's probably allowed just for compat reasons at
               | this point -- but I think your point is don't make that
               | mistake in the first place, not that Go is filled with
               | flagrant violations of the convention.
        
         | Wonton56 wrote:
         | 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.
        
           | kwhitefoot wrote:
           | > I use IntelliJ for work and it shows clearly where some
           | reference
           | 
           | Useful when writing the code but not much use when reading
           | the code.
        
           | sspiff wrote:
           | Let's not write software and especially programming languages
           | which assume or depend on users having access to advanced
           | tools that require a monthly subscription.
        
         | packetlost wrote:
         | This is the chief problem with Python and most Lisps
        
         | manusachi wrote:
         | Agree!
         | 
         | For that very reason in Elixir `import` is discouraged in favor
         | of `alias`
        
           | __jonas wrote:
           | I enjoy Elixir but this situation is quite unfortunate, there
           | is alias, import require and use for referencing / pulling in
           | code external to a file in some way or another, plus the
           | possibility of calling a function from another module
           | directly by name without an import statement using the module
           | name - and the most annoying part of it is that none of these
           | give an indication of which file the other module is from,
           | which is like 50% of the utility of an import statement for
           | me.
           | 
           | Instead there is this pattern of naming modules based on the
           | file path they are located in, which is not enforced.
        
         | lhfasufdowedfs wrote:
         | 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.
        
         | djha-skin wrote:
         | 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 :)
        
       | Aeolun wrote:
       | 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 :)
        
       | MidhaelBollox wrote:
       | You have reached the maximum number of changes allowed. Please
       | subscribe to make more changes.
        
       | zeroCalories wrote:
       | 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 wrote:
         | 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.
        
           | DarkNova6 wrote:
           | I think once Java has finished up exception switch-case it
           | will be a model followed by other languages. Being able to
           | catch exceptions at both, method and transaction boundaries
           | will be a boon for readable control-flow.
        
             | throw156754228 wrote:
             | Algebraic effects is going in completely the opposite
             | direction.
        
             | mrkeen wrote:
             | > and transaction boundaries
             | 
             | What are transaction boundaries? Is Java getting
             | transactions?
        
         | otabdeveloper4 wrote:
         | > ...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.
        
           | ratscylla wrote:
           | I think people like it because the control flow of a given
           | program is more obvious when you write it that way. No one
           | can "throw Foo" three libraries down from their caller as an
           | "API". See https://go.dev/blog/errors-are-values
        
           | psd1 wrote:
           | As an implementation detail, exceptions are usually much more
           | expensive than just popping the stack, as computation is
           | needed for each frame you traverse.
           | 
           | Having both throw and return is like regex: now you have two
           | problems.
        
             | tylerhou wrote:
             | Exceptions are less expensive in the unexceptional case.
             | Consuming a Result value requires a branch to see whether
             | it holds Error. That is not necessary if the function
             | returns a value directly (and throws an exception on
             | error).
        
           | humzashahid98 wrote:
           | I prefer to build result-passing, no-exception-throwing
           | systems out of an exception-throwing core where the language
           | itself may throw exceptions but the thing I build from the
           | language always returns result.
           | 
           | Elm is an extreme case where indexing into an array returns a
           | result type instead of an exception when the index is out of
           | bounds, unlike most languages including Haskell.
           | 
           | Maybe my program logic is intended to always access only
           | valid indices in an array, but here I'm given a result type
           | where I have nothing to do in the error case since my code is
           | never intended to reach that case. I would rather let the
           | language throw an out-of-bounds exception here to tell me
           | that my implementation is incorrect while I'm testing.
           | 
           | Same with libraries in a language. It really depends on the
           | use cases of the library you're writing whether results or
           | exceptions are better. The most convenient thing for the user
           | of a library would be to provide both exception-throwing and
           | result-passing alternatives. This is what the OCaml standard
           | library does as well.
        
           | shepherdjerred wrote:
           | I dislike Go, but I understand why people like most of its
           | decisions.
           | 
           | I cannot fathom why people think it does error handling well,
           | though. The codebase I work in has _so_ many errors that are
           | completely ignored, and errors are a lot harder to track
           | down.
           | 
           | These problems can be solved by writing better code, but the
           | problem is that it's, of course, hard to write good code.
           | 
           | Java's exception handling has problems, but at least it gives
           | you nice stack traces and you can't forget to propagate an
           | exception.
        
           | kdmccormick wrote:
           | Unless you're using exclusively Java-style checked
           | exceptions, then there literally (literally!) is huge
           | difference. That is:                   def main():
           | try:                 my_a = a()             except ExA, ExB:
           | my_a = None             ...                       def a() ->
           | A:             ...             my_b = b()             ...
           | def b() -> B:             ...
           | 
           | If b changes its signature to return type C, then it is a
           | type error in a. main doesn't need to worry about what b
           | returns; only a does.
           | 
           | BUT if b begins raising ExC instead of ExB, then that will
           | break main at runtime. That is, main needs to be aware of
           | what b could raise, even though it doesn't directly call it.
        
         | NBJack wrote:
         | 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.
        
       | ceving wrote:
       | Is it implemented using CPS conversion?
        
         | baq wrote:
         | It certainly _looks_ like continuation passing everywhere
         | manually.
        
       | junon wrote:
       | Gay Agenda License 1.0 had me laughing.
        
         | emersion wrote:
         | https://git.sr.ht/~ntietz/hurl-lang/tree/main/item/LICENSE/G...
        
           | NoahKAndrews wrote:
           | This is fantastic
        
           | andrewshadura wrote:
           | Unfortunately, it's not open source, but it's still hilarious
           | :)
        
             | zzo38computer wrote:
             | Although it is not open source, this program can also be
             | used with AGPL3 which is a open source license, so it is
             | good enough.
        
       | DaveFlater wrote:
       | See: https://news.ycombinator.com/item?id=36393673
        
       | tombert wrote:
       | Just rebrand this as "algebraic effects" and suddenly every
       | academic will pretend it's revolutionary.
        
         | withoutboats3 wrote:
         | 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.
        
       | cushpush wrote:
       | Don't just throw it, hurl it
        
       | zbentley wrote:
       | 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 wrote:
         | > 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.
        
       | jokethrowaway wrote:
       | 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.
        
       | tempodox wrote:
       | 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?
        
       | brundolf wrote:
       | Top-tier wordplay
        
       ___________________________________________________________________
       (page generated 2024-05-26 23:00 UTC)