Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127189 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id D9FB31A00BC for ; Sat, 26 Apr 2025 16:19:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1745684247; bh=3QXmJe7gNjXX4UKJD5qvdpBwQal4seVkyfm5d1IVkCk=; h=Date:From:To:In-Reply-To:References:Subject:From; b=EtVev24+E7f2NieZq4a+qOGZxRNq0K/cfv/Vd7FS+ZgA5Wpa3w82F9+zGX0g9iwvr CHUXY3HAkhuK8g5WNsZ4VA2vsvL5EDpYsyey510C035UONNwyezUPEKuk+2Yg/iRa8 peCuHvWOgJpk5okqEbd3Btp8bFiyJKVmZSXtkHU0u9P7W8jMQhFA1YIVLMbgCc7qEJ SzF2JSzMHKzA+stHs5q6IHG8ooCTPdxt1KvqMKluCOOwSuLuBBV/FMAs1/c7Z8hsVG CfNjVq5e2ESM7uup3hJG98GbL9doLAsGxk743wms8CvnPpNlR93xdFZjSVyLlCAJtl B2FFYh/XmSUfA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id B7B9F180052 for ; Sat, 26 Apr 2025 16:17:24 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-0.9 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,HTML_MESSAGE, RCVD_IN_DNSWL_LOW,SPF_HELO_PASS,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fhigh-b7-smtp.messagingengine.com (fhigh-b7-smtp.messagingengine.com [202.12.124.158]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Sat, 26 Apr 2025 16:17:24 +0000 (UTC) Received: from phl-compute-05.internal (phl-compute-05.phl.internal [10.202.2.45]) by mailfhigh.stl.internal (Postfix) with ESMTP id 8CB042540220 for ; Sat, 26 Apr 2025 12:19:41 -0400 (EDT) Received: from phl-imap-09 ([10.202.2.99]) by phl-compute-05.internal (MEProxy); Sat, 26 Apr 2025 12:19:41 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bottled.codes; h=cc:content-type:content-type:date:date:from:from:in-reply-to :in-reply-to:message-id:mime-version:references:reply-to:subject :subject:to:to; s=fm1; t=1745684381; x=1745770781; bh=y3L/F6Fmiw FTN8p6IiYiygDg5JiM8rWNA129CGGpP20=; b=z/9m/epIQw0AqCyD/CcxMX4SfT SjoBEthEwbNodU3Lo0aoeIoUyoQqiB8hflJyF1Pg+YJI4wo+oIc7U8zELCkmEcnz 0CvDrv9ndW1/LBrUkRM1O1QFcuMuV+H/Vd4vdLc4eDflPysczJgV/gTf5f9wWMhh vJplKOBwEXuWRmpdFBgmpzS7TAO0FMfkjYaAhComtztgNzWUr60AhcMXBWOw/MUz m43IArSPnd6eOqTdy6ODyXN/uOaSggylaQXtk/yjqeuJ9t1VgYrpe66w5RyZw016 UxZxS91aVmiIhwo54Ia8BXjMMiJzeR1EX8qVjOuhCXIkL4EpXHQbRSHWHR4A== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-type:content-type:date:date :feedback-id:feedback-id:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s=fm3; t= 1745684381; x=1745770781; bh=y3L/F6FmiwFTN8p6IiYiygDg5JiM8rWNA12 9CGGpP20=; b=UJU27w0xEOTEjxYg1OP63pW1M4nB5ox8sY4N5r/Dgb6OWcJiqYw vpyUCztGwoz4uUXwo+na0gJW0/AEJK/3SulV1nXi3B++5AzAJeTActXeA5pZlzPR Jqyb75qAQwHkyppWGjlf5upuzZ/K6lQu8EKC2XUfumRQJi0sSBT1VMYr3el3Mwsw Z/NIaDHox+R28mKzzq1B4+NP8TM1+fEEsc2sROR7VXYv56j1fNdvgv7dGJWgoXAY FKHjyMgt5uXoXehlKxu9Bb5B6DMfuNOV/JiZGu9ORddr6lbkMa+tKZHrVxRhTHPC PyIHLZeva9B8lECayG1r0SyJHwDuT7JoDIg== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefvddrtddtgddvheehieeiucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggv pdfurfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucenucfjughrpefogg ffhffvkfgjfhfutgesrgdtreerredtjeenucfhrhhomhepfdftohgsucfnrghnuggvrhhs fdcuoehrohgssegsohhtthhlvggurdgtohguvghsqeenucggtffrrghtthgvrhhnpeefvd eggeekjedvgfeufefhgfejleehvefgieetteevjedvhfdtgfffvdffuefhheenucffohhm rghinhepphgvrghkugdrtghomhdpjhhovgguuhhffhihsghlohhgrdgtohhmnecuvehluh hsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheprhhosgessghothht lhgvugdrtghouggvshdpnhgspghrtghpthhtohepuddpmhhouggvpehsmhhtphhouhhtpd hrtghpthhtohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: ifab94697:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 105B8780069; Sat, 26 Apr 2025 12:19:40 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: Tf8e68f75eb20cb92 Date: Sat, 26 Apr 2025 18:19:19 +0200 To: internals@lists.php.net Message-ID: <7e923206-139a-4442-824e-895b09d563a2@app.fastmail.com> In-Reply-To: <39597a9c-6854-40c6-a529-32b2b178cb27@app.fastmail.com> References: <39597a9c-6854-40c6-a529-32b2b178cb27@app.fastmail.com> Subject: Re: [PHP-DEV] Concept: Lightweight error channels Content-Type: multipart/alternative; boundary=e8ba629ad24c4abe9a86fffb851d303b From: rob@bottled.codes ("Rob Landers") --e8ba629ad24c4abe9a86fffb851d303b Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On Sat, Apr 26, 2025, at 09:17, Larry Garfield wrote: > Hi folks. In several recent RFCs and related discussion, the question= of error handling has come up. Specifically, the problem is: >=20 > * "return null" conflicts with "but sometimes null is a real value" (t= he validity of that position is debatable, but common), and provides no = useful information as to what went wrong. > * Exceptions are very expensive, the hierarchy is confusing, and handl= ing them properly is a major pain. Failing to handle them properly is v= ery easy since you have no way of knowing what exceptions the code you'r= e calling might throw, or its nested calls, etc. That makes them poorly= suited for mundane, predictable error conditions. > * trigger_error() is, well, a mess and not suitable for signaling to a= calling function that something recoverable-in-context went wrong. > * And... that's all we've got as options. >=20 > I've had an idea kicking around in my head for a while, which I know I= 've mentioned before. Given the timing, I want to put it out in its cur= rent unfinished form to see if there's interest in me bothering to finis= h it, or if it doesn't have a snowball's chance in hell of happening so = it's not worth my time to further develop. >=20 > I know I've posted this before, but it's useful for background: >=20 > https://peakd.com/hive-168588/@crell/much-ado-about-null > https://joeduffyblog.com/2016/02/07/the-error-model/ >=20 > From both prior discussions here as well as my understanding of langua= ge design trends, it seems the consensus view is that a Result type (aka= , an Either monad) is the ideal mechanism for robust error handling. Ho= wever, it requires generics to be really viable, which we don't have. I= t's also very clumsy to use in a classic-OOP language (like PHP) without= special dedicated syntax. >=20 > Various languages work around that in various ways. Rust built its wh= ole error system on Result types, and later added the `?` operator to in= dicate "and if this returns an error result, just return it directly", m= aking delegating error handling vastly easier. Kotlin (via its Arrow li= brary) relies on heavy use of chained tail-closures. Go has a conventio= n of a "naked either" using two return values, but doesn't have any spec= ial syntax for it leading to famously annoying boilerplate. Python has = lightweight exceptions so that throwing them willy nilly as a control fl= ow tool is actually OK and Pythonic. >=20 > However, as noted in the "Error Model" article above, and this is key,= a Result type is isomorphic to a *checked* exception. A checked except= ion is one where a function must explicitly declare what it can throw, a= nd if it throws something else it's the function's error, and a compile = time error. It also means any "bubbling" of exceptions has to be explic= it at each function step. That's in contrast to unchecked exceptions, a= s PHP has now, which may be thrown from nearly anywhere and will silentl= y bubble up and crash the program if not otherwise handled. >=20 > The key point here is that a happy-path return and an unhappy-but-not-= world-ending-path need to be different. Using the return value for both= (what returning null does) is simply insufficient. =20 >=20 > The "Error Model" article goes into the pros and cons of checked vs un= checked exceptions so I won't belabor the point, except to say that most= arguments against checked exceptions are based on Java's very-broken im= plementation of checked-except-when-it's-not exceptions. But as noted, = what Rust and Go do is checked exceptions, aka a Result type, just spell= ed differently. The advantage of checked exceptions is that we don't ne= ed generics at all, and still get all the benefits. We can also design = syntax around them specifically to make them more ergonomic. >=20 > I am invisioning something like this: >=20 > ``` > function div(int $n, int $d): float raises ZeroDivisor > { > if ($d =3D=3D=3D 0) { > raise new ZeroDivisor(); // This terminates the function. > } > return $n/$d; > } > ``` >=20 > The "raises" declaration specifies a class or interface type that coul= d be "raised". It can be any object; no required Exception hierarchy, n= o backtrace, just a boring old object value. Enum if you feel like it, = or any other object. We could probably allow union or full DNF types th= ere if we wanted, though I worry that it may lead to too confusing of an= API. (To bikeshed later.) Static analysis tools could very easily dete= ct if the code doesn't match up with the declared raises. >=20 > This feature already exists in both Midori (the subject of the "Error = Model" article) and Swift. So it's not a new invention; in fact it's qu= ite old. >=20 > The handling side is where I am still undecided on syntax. Swift uses= essentially try-catch blocks, though I fear that would be too verbose i= n practice and would be confused with existing "heavy" exceptions. Mido= ri did the same. =20 >=20 > Various ideas I've pondered in no particular order: >=20 > ``` > // Suck it up and reuse try-catch >=20 > function test() { // No declared raise, so if it doesn't handle ZeroDi= visor itself, fatal. > try { > $val =3D div(3, 0); > } catch (ZeroDivisor $e) { > print "Nope."; > } > } > ``` >=20 > ``` > // try-catch spelled differently to avoid confusion with exceptions > try { > $val =3D div(3, 0); > } handle (ZeroDivisor $e) { > print "Nope."; > } > ``` >=20 > ``` > // Some kind of suffix block, maybe with a specially named variable? >=20 > $val =3D div(3, 0) else { print $err->message; return 0; } > ``` >=20 > ``` > // A "collapsed" try-catch block. > $val =3D try div(3, 0)=20 > catch (ZeroDivisor $e) { print "Nope"; } > catch (SomethingElse $e) { print "Wat?"; } > ``` >=20 > ``` > // Similar to Rust's ? operator, to make propagating an error easier. >=20 > // The raise here could be the same or wider than what div() raises. > function test(): float raises ZeroDivisor { > $val =3D div(3, 0) reraise; > // use $val safely knowing it was returned and nothing was raised. > } > ``` >=20 > Or other possibilities I've not considered. >=20 > The use cases for a dedicated error channel are many: >=20 > * Any variation of "out of bounds": Could be "record not found in data= base", or "no such array key" or "you tried to get the first item of an = empty list", or many other things along those lines. > * Expected input validation errors. This would cover the URL/URI RFC'= s complex error messages, without the C-style "inout" parameter. > * Chaining validation. A series of validators that can return true (o= r just the value being validated) OR raise an object with the failure re= ason. A wrapping function can collect them all into a single error obje= ct to return to indicate all the various validation failures. > * A transformer chain, which does the same as validation but passes on= the transformed value and raises on the first erroring transformer. >=20 > Exceptions remain as is, for "stop the world" unexpected failures or d= eveloper errors (bugs). But mundane errors, where local resolution is b= oth possible and appropriate, get a dedicated channel and syntax with no= performance overhead. That also naturally becomes a Python-style "bett= er to beg forgiveness than ask permission" approach to error handling if= desired, without all the overhead of exceptions. >=20 >=20 > So that's what I've got so far. My question for the audience is: >=20 > 1. Assuming we could flesh out a comfortable and ergonomic syntax, wou= ld you support this feature, or would you reject it out of hand? >=20 > 2. For engine-devs: Is this even feasible? :-) And if so, anyone want= to join me in developing it? >=20 > 3. And least relevant, I'm very open to suggestions for the syntax, th= ough the main focus right now is question 1 to determine if discussing s= yntax is even worthwhile. >=20 >=20 > --=20 > Larry Garfield > larry@garfieldtech.com >=20 Hey Larry, I=E2=80=99m still digesting this, but I wonder if this problem (errors v= s non-world-ending errors vs happy path) is a problem due to people ofte= n making warnings into exceptions? I feel like many devs/frameworks =E2=80=9Cabuse=E2=80=9D (for lack of a = better word) the error handling system to make everything into a world-e= nding-error when it is not.=20 Even over the last few versions of PHP, more and more warnings have turn= ed into exceptions that maybe ought not be. Overall, this has resulted in a somewhat more inconsistent language than= it was previously. For example, you used to be able to consider an arra= y as a field of infinite nulls. You could iterate over an infinite set o= f keys and get nothing but null. This made it easier to explain to junio= r devs at the time. Now, you have to explain it as a hash-map-but-someti= mes-array-thing that emits warnings when a key doesn=E2=80=99t exist but= will still give you null.=20 We are currently in this weird place between =E2=80=9Cinfinite field of = nulls=E2=80=9D and =E2=80=9Csparse array/hash-map=E2=80=9D but not quite= either one =E2=80=94 depending on what you do with that warning.=20 It would be great if we could pick one and I think it would also solve t= he whole =E2=80=9Cis null an error or a value=E2=80=9D problem.=20 I personally wouldn=E2=80=99t like checked exceptions. I have PTSD from = Java=E2=80=99s, so just hearing that phrase gives me flashbacks of catch= ing dozens of potential exceptions. In Go, yes there is some boilerplate= , but it=E2=80=99s not that bad and it easy to deal with, especially wit= h IDE support.=20 As for a Result type, that=E2=80=99s already possible in user-land; what= does bringing it to the engine get us? Is it just a lightweight excepti= on without a trace to prevent the cost of generation? Why not just have = a pool of exceptions that you can just modify the message? =E2=80=94 Rob --e8ba629ad24c4abe9a86fffb851d303b Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable


On Sat, Apr 26, 2025, at 09:17, Larry Garfield wrote:<= /div>
Hi folks. = In several recent RFCs and related discussion, the question of error ha= ndling has come up.  Specifically, the problem is:

* "return null" conflicts with "but sometimes null is a real va= lue" (the validity of that position is debatable, but common), and provi= des no useful information as to what went wrong.
* Exceptions = are very expensive, the hierarchy is confusing, and handling them proper= ly is a major pain.  Failing to handle them properly is very easy s= ince you have no way of knowing what exceptions the code you're calling = might throw, or its nested calls, etc.  That makes them poorly suit= ed for mundane, predictable error conditions.
* trigger_error(= ) is, well, a mess and not suitable for signaling to a calling function = that something recoverable-in-context went wrong.
* And... tha= t's all we've got as options.

I've had an idea = kicking around in my head for a while, which I know I've mentioned befor= e.  Given the timing, I want to put it out in its current unfinishe= d form to see if there's interest in me bothering to finish it, or if it= doesn't have a snowball's chance in hell of happening so it's not worth= my time to further develop.

I know I've posted= this before, but it's useful for background:

<= a href=3D"https://peakd.com/hive-168588/@crell/much-ado-about-null">http= s://peakd.com/hive-168588/@crell/much-ado-about-null

Fr= om both prior discussions here as well as my understanding of language d= esign trends, it seems the consensus view is that a Result type (aka, an= Either monad) is the ideal mechanism for robust error handling.  H= owever, it requires generics to be really viable, which we don't have.&n= bsp; It's also very clumsy to use in a classic-OOP language (like PHP) w= ithout special dedicated syntax.

Various langua= ges work around that in various ways.  Rust built its whole error s= ystem on Result types, and later added the `?` operator to indicate "and= if this returns an error result, just return it directly", making deleg= ating error handling vastly easier.  Kotlin (via its Arrow library)= relies on heavy use of chained tail-closures.  Go has a convention= of a "naked either" using two return values, but doesn't have any speci= al syntax for it leading to famously annoying boilerplate.  Python = has lightweight exceptions so that throwing them willy nilly as a contro= l flow tool is actually OK and Pythonic.

Howeve= r, as noted in the "Error Model" article above, and this is key, a Resul= t type is isomorphic to a *checked* exception.  A checked exception= is one where a function must explicitly declare what it can throw, and = if it throws something else it's the function's error, and a compile tim= e error.  It also means any "bubbling" of exceptions has to be expl= icit at each function step.  That's in contrast to unchecked except= ions, as PHP has now, which may be thrown from nearly anywhere and will = silently bubble up and crash the program if not otherwise handled.
=

The key point here is that a happy-path return and a= n unhappy-but-not-world-ending-path need to be different.  Using th= e return value for both (what returning null does) is simply insufficien= t.  

The "Error Model" article goes i= nto the pros and cons of checked vs unchecked exceptions so I won't bela= bor the point, except to say that most arguments against checked excepti= ons are based on Java's very-broken implementation of checked-except-whe= n-it's-not exceptions.  But as noted, what Rust and Go do is checke= d exceptions, aka a Result type, just spelled differently.  The adv= antage of checked exceptions is that we don't need generics at all, and = still get all the benefits.  We can also design syntax around them = specifically to make them more ergonomic.

I am = invisioning something like this:

```
= function div(int $n, int $d): float raises ZeroDivisor
{
=
  if ($d =3D=3D=3D 0) {
    raise new= ZeroDivisor();  // This terminates the function.
  = }
  return $n/$d;
}
```
The "raises" declaration specifies a class or interface type= that could be "raised".  It can be any object; no required Excepti= on hierarchy, no backtrace, just a boring old object value.  Enum i= f you feel like it, or any other object.  We could probably allow u= nion or full DNF types there if we wanted, though I worry that it may le= ad to too confusing of an API. (To bikeshed later.)  Static analysi= s tools could very easily detect if the code doesn't match up with the d= eclared raises.

This feature already exists in = both Midori (the subject of the "Error Model" article) and Swift.  = So it's not a new invention; in fact it's quite old.

The handling side is where I am still undecided on syntax.  S= wift uses essentially try-catch blocks, though I fear that would be too = verbose in practice and would be confused with existing "heavy" exceptio= ns.  Midori did the same.  

Vari= ous ideas I've pondered in no particular order:

```
// Suck it up and reuse try-catch

function test() { // No declared raise, so if it doesn't handle ZeroDi= visor itself, fatal.
  try {
   = $val =3D div(3, 0);
  } catch (ZeroDivisor $e) {
    print "Nope.";
  }
}
```

```
// try-catch spelled di= fferently to avoid confusion with exceptions
try {
&= nbsp; $val =3D div(3, 0);
} handle (ZeroDivisor $e) {
  print "Nope.";
}
```

```
// Some kind of suffix block, maybe with a specially na= med variable?

$val =3D div(3, 0) else { print $= err->message; return 0; }
```

```<= /div>
// A "collapsed" try-catch block.
$val =3D try div(3= , 0) 
  catch (ZeroDivisor $e) { print "Nope"; }
  catch (SomethingElse $e) { print "Wat?"; }
```

```
// Similar to Rust's ? operator, t= o make propagating an error easier.

// The rais= e here could be the same or wider than what div() raises.
func= tion test(): float raises ZeroDivisor {
  $val =3D div(3,= 0) reraise;
  // use $val safely knowing it was returned= and nothing was raised.
}
```

<= div>Or other possibilities I've not considered.

The use cases for a dedicated error channel are many:

* Any variation of "out of bounds": Could be "record not found i= n database", or "no such array key" or "you tried to get the first item = of an empty list", or many other things along those lines.
* E= xpected input validation errors.  This would cover the URL/URI RFC'= s complex error messages, without the C-style "inout" parameter.
* Chaining validation.  A series of validators that can return t= rue (or just the value being validated) OR raise an object with the fail= ure reason.  A wrapping function can collect them all into a single= error object to return to indicate all the various validation failures.=
* A transformer chain, which does the same as validation but = passes on the transformed value and raises on the first erroring transfo= rmer.

Exceptions remain as is, for "stop the wo= rld" unexpected failures or developer errors (bugs).  But mundane e= rrors, where local resolution is both possible and appropriate, get a de= dicated channel and syntax with no performance overhead.  That also= naturally becomes a Python-style "better to beg forgiveness than ask pe= rmission" approach to error handling if desired, without all the overhea= d of exceptions.


So that's what = I've got so far.  My question for the audience is:

1. Assuming we could flesh out a comfortable and ergonomic synt= ax, would you support this feature, or would you reject it out of hand?<= /div>

2. For engine-devs: Is this even feasible? :-)&= nbsp; And if so, anyone want to join me in developing it?

=
3. And least relevant, I'm very open to suggestions for the s= yntax, though the main focus right now is question 1 to determine if dis= cussing syntax is even worthwhile.


-- 
  Larry Garfield


Hey Larry,

I=E2=80=99m still digesting this, but I wonder if this problem (= errors vs non-world-ending errors vs happy path) is a problem due to peo= ple often making warnings into exceptions?

I fe= el like many devs/frameworks =E2=80=9Cabuse=E2=80=9D (for lack of a bett= er word) the error handling system to make everything into a world-endin= g-error when it is not. 

Even over the las= t few versions of PHP, more and more warnings have turned into exception= s that maybe ought not be.

Overall, this has re= sulted in a somewhat more inconsistent language than it was previously. = For example, you used to be able to consider an array as a field of infi= nite nulls. You could iterate over an infinite set of keys and get nothi= ng but null. This made it easier to explain to junior devs at the time. = Now, you have to explain it as a hash-map-but-sometimes-array-thing that= emits warnings when a key doesn=E2=80=99t exist but will still give you= null. 

We are currently in this weird pla= ce between =E2=80=9Cinfinite field of nulls=E2=80=9D and =E2=80=9Csparse= array/hash-map=E2=80=9D but not quite either one =E2=80=94 depending on= what you do with that warning. 

It would = be great if we could pick one and I think it would also solve the whole = =E2=80=9Cis null an error or a value=E2=80=9D problem. 
<= br>
I personally wouldn=E2=80=99t like checked exceptions. I h= ave PTSD from Java=E2=80=99s, so just hearing that phrase gives me flash= backs of catching dozens of potential exceptions. In Go, yes there is so= me boilerplate, but it=E2=80=99s not that bad and it easy to deal with, = especially with IDE support. 

As for a Res= ult type, that=E2=80=99s already possible in user-land; what does bringi= ng it to the engine get us? Is it just a lightweight exception without a= trace to prevent the cost of generation? Why not just have a pool of ex= ceptions that you can just modify the message?

=E2=80=94 Rob
--e8ba629ad24c4abe9a86fffb851d303b--