- Feature Name:
try_expr
- Start Date: 2018-04-04
- RFC PR: rust-lang/rfcs#2388
- Rust Issue: rust-lang/rust#50412
Summary
RFC 243 left the choice of keyword for catch { .. }
expressions unresolved.
This RFC settles the choice of keyword. Namely, it:
- reserves
try
as a keyword in edition 2018. - replaces
do catch { .. }
withtry { .. }
- does not reserve
catch
as a keyword.
Motivation
This RFC does not motivate catch { .. }
or try { .. }
expressions.
To read the motivation for that, please consult the original catch
RFC.
For reserving a keyword
Whatever keyword is chosen, it can’t be contextual.
As with catch { .. }
, the syntactic form <word> { .. }
where <word>
is replaced with any possible keyword would conflict with a struct named
<word>
as seen in this perfectly legal snippet in Rust 2015,
where <word>
has been substituted for try
:
struct try;
fn main() {
try {
};
}
Aside note:
The snippet above emits the following warning:
warning: type `try` should have a camel case name such as `Try`
which is also the case for catch
.
This warning decreases the risk that someone has defined a type named try
anywhere in the ecosystem which happens to be beneficial to us.
For reserving try
specifically
This is discussed in the rationale for try
.
Guide-level explanation
The keyword try
will be reserved.
This will allow you to write expressions such as:
try {
let x = foo?;
let y = bar?;
// Note: OK-wrapping is assumed here, but it is not the goal of this RFC
// to decide either in favor or against OK-wrapping.
x + y
}
Reference-level explanation
The word try
is reserved as a keyword in the list of keywords
in Rust edition 2018 and later editions.
The keyword try
is used in “try expressions” of the form try { .. }
.
Drawbacks
There are two main drawbacks to the try
keyword.
Association with exception handling - Both a pro and con
I think that there is a belief – one that I have shared from time to time – that it is not helpful to use familiar keywords unless the semantics are a perfect match, the concern being that they will setup an intuition that will lead people astray. I think that is a danger, but it works both ways: those intuitions also help people to understand, particularly in the early days. So it’s a question of “how far along will you get before the differences start to matter” and “how damaging is it if you misunderstand for a while”.
[..]
Rust has a lot of concepts to learn. If we are going to succeed, it’s essential that people can learn them a bit at a time, and that we not throw everything at you at once. I think we should always be on the lookout for places where we can build on intuitions from other languages; it doesn’t have to be a 100% match to be useful.
For some people, the association to try { .. } catch { .. }
in languages such
as Java, and others in the prior-art section, is unhelpful wrt. teachability
because they see the explicit, reified, and manually propagated exceptions in
Rust as something very different than the much more implicit exception handling
stories in Java et al.
However, we make the case that other languages which do have these explicit and
reified exceptions as in Rust also use an exception vocabulary.
Notably, Haskell calls the monad-transformer for adding exceptions ExceptT
.
We also argue that even tho we are propagating exceptions manually, we are following tradition in that other languages have very different formulations of the exception idea.
The benefit of familiarity, even if not a perfect match, as Niko puts it, helps in learning, particularly because Rust is not a language in lack of concepts to learn.
Breakage of the try!
macro
One possible result of introducing try
as a keyword be that the old try!
macro would break. This could potentially be avoided but with great technical
challenges.
With the prospect of breaking try!
, a few notes are in order:
?
was stabilized in 1.13, November 2016, which is roughly 1.4 years since the date this RFC was started.try!
has been “deprecated” since then since:The
?
operator was added to replacetry!
and should be used instead.try!(expr)
can in virtually all instances be automaticallyrustfix
ed automatically toexpr?
.- There are very few questions on Stack Overflow that mention
try!
. - “The Rust Programming Language”, 2nd edition (book) and “Rust by Example”
have both already removed all mentions of
try!
.
So overall I think it’s feasible to reduce the
try!
macro to a historical curiosity to the point it won’t be actively confusing to newbies coming to Rust.
- kornel
However,
- There are still plenty of materials out there which mention
try!
. try!
is essentially the inverse oftry { .. }
.
Purging from the “collective memories of Rustaceans and Rust materials” is not something that easy.
In the RFC author’s opinion however, the sum total benefits of try { .. }
seem to outweigh the drawbacks of the difficulty with purging try!
from
our collective memory.
Inverse semantics of ?
The ?
postfix operator is sometimes referred to as the “try operator”,
and can be seen as having the inverse semantics as try { .. }
.
To many, this is a drawback. To others, this makes the ?
and try { .. }
expression forms more closely related and therefore makes them more findable
in relation to each other.
There is currently some ongoing debate about renaming the ?
operator to
something other than the “try operator”. This could help in mitigating the
effects of picking try
as the keyword.
Rationale and alternatives
Review considerations
Among the considerations when picking a keyword are, ordered by importance:
-
Fidelity to the construct’s actual behavior.
-
Precedent from existing languages
- Popularity of the languages.
- Fidelity to behavior in those languages.
- Familiarity with respect to their analogous constructs.
See the prior art the rationale for try for more discussion on precedent.
-
Brevity.
-
Consistency with related standard library function conventions.
-
Consistency with the naming of the trait used for
?
(theTry
trait). Since theTry
trait is unstable and the naming of the?
operator in communication is still unsettled, this is not regarded as very important. -
Degree / Risk of breakage.
-
Consistency with old learning material.
- Inversely: The extent of the old learning material
That is, (in)consistency with
?
and thetry!()
macro. If the first clause is calledtry
, thentry { }
andtry!()
would have essentially inverse meanings.
Rationale for try
- Fidelity to the construct’s actual behavior: Very high
- Precedent from existing languages: A lot, see prior-art
- Popularity of the languages: Massive accumulated dominance
- Fidelity to behavior in those languages: Very high
- Familiarity with respect to their analogous constructs: Very high
- Brevity / Length: 3
- Consistency with related libstd fn conventions: Consistent
- Consistency with the naming of the trait used for
?
: Consistent - Risk of breakage: High (if we assume
try!
will break, otherwise: Low)- Used in std: No,
std::try!
, but it is technically possible to not break this macro. (unstable:std::intrinsics::try
so irrelevant) - Used as crate? Yes. No reverse dependencies. Described as: “Deprecation warning resistant try macro”
- Usage (sourcegraph): 27 regex:
repogroup:crates case:yes max:400 \b((let|const|type|)\s+try\s+=|(fn|impl|mod|struct|enum|union|trait)\s+try)\b
- Used in std: No,
- Consistency with old learning material: Inconsistent (
try!
)
Review
This is our choice of keyword, because it:
- has a massive dominance in both popular and less known languages and is
sufficiently semantically faithful to what
try
means in those languages. Thus, we can leverage people’s intuitions and not spend too much of our complexity budget. - is consistent with the standard library wrt.
Try
andtry_
prefixed methods. - it is brief.
- it has high fidelity wrt. the concepts it attempts to communicate
(exception boundary for
?
). This high fidelity is from the perspective of a programmers intent, i.e: “I want to try a bunch of stuff in this block”. - it can be further extended with
catch { .. }
handlers if we wish.
Alternative: reserving catch
- Fidelity to the construct’s actual behavior: High
- Precedent from existing languages: Erlang and Tcl, see prior-art
- Brevity / Length: 6
- Consistency with related libstd fn conventions: Somewhat consistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+catch\s+=|(fn|impl|mod|struct|enum|union|trait)\s+catch)\b
- Consistency with old learning material: Untaught
Review
We believe catch
to be a poor choice of keyword, because it:
- is used in few in other languages to demarcate the body which can result in
an exceptional path. Instead, it is almost exclusively used for exception
handlers of the form:
catch(pat) { recover_expr }
. - extending
catch
with handlers will require a different word such ashandler
to getcatch { .. } handler(e) { .. }
semantics if we want. This inversion compared to a lot of other languages will only harm teachability of the language and steal a lot of our strangeness budget. - it is less brief than
try
. - the consistency wrt. methods in the standard library is low -
there’s only
catch_unwind
, but that has to do with panics, notTry
style exceptions.
However, catch
has high fidelity wrt. the operational semantics of “catching”
any exceptions in the try { .. }
block.
Alternative: keeping do catch { .. }
- Fidelity to the construct’s actual behavior: Middle
- Precedent from existing languages:
do
: Haskell, Idriscatch
: Erlang and Tcl, see prior-art
- Brevity / Length: 8
- Consistency with related libstd fn conventions: Tiny bit consistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Impossible (already reserved keyword)
- Used in std: No, the form
$ident $ident
is not a legal identifier. - Used as crate? No, as above.
- Usage (sourcegraph): 0 regex: N/A
- Used in std: No, the form
- Consistency with old learning material: Untaught
An alternative would be to simply use the do catch { ... }
syntax we have
in the nightly compiler. However, this syntax was not in the accepted catch
RFC and was only a temporary fix around catch { .. }
not working.
Alternative: do try { .. }
- Fidelity to the construct’s actual behavior: High
- Precedent from existing languages:
do
: Haskell, Idristry
: A lot, see prior-art
- Popularity of the languages: Massive accumulated dominance
- Fidelity to behavior in those languages: High
- Familiarity with respect to their analogous constructs: High
- Brevity / Length: 6 (including space)
- Consistency with related libstd fn conventions: Moderately consistent
- Consistency with the naming of the trait used for
?
: Moderately consistent - Risk of breakage: Impossible (already reserved keyword)
- Used in std: No, the form
$ident $ident
is not a legal identifier. - Used as crate? No, as above.
- Usage (sourcegraph): 0 regex: N/A
- Used in std: No, the form
- Consistency with old learning material: Untaught
Review
We could in fact decide to keep the do
-prefix but change the suffix to try
.
The benefit here would be two-fold:
-
No keyword
try
would need to be introduced asdo
already is a keyword. Therefore, thetry!
macro would not break. -
An association with monads due to
do
. This can be considered a benfit sincetry
can be seen as sugar for the family of error monads (modulo kinks wrt. imperative flow), and thus, thedo
prefix leads to a path of generality if more monads are introduced.
The drawbacks would be:
-
The wider association with monads can be seen as a drawback for those not familiar with monads.
-
do try { .. }
overtry { .. }
adds a small degree of ergonomics overhead but not much (3 characters including the space). However, the frequency with which thetry { .. }
construct might be used can make the small overhead accumulate to a significant overhead when a large codebase is considered.
Other than this, the argument for do try
over do catch
boils down to an
argument of try
over catch
.
Alternative: using do { .. }
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: Haskell, Idris
- Popularity of the languages: Haskell: Tiobe #42, PYPL #22
- Fidelity to behavior in those languages: Good
- Familiarity with respect to their analogous constructs: Poor
- Brevity / Length: 2
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Impossible (already reserved keyword)
- Consistency with old learning material: Untaught
Review
The keyword do
was probably originally reserved for two use cases:
-
do while { .. }
-
Monadic
do
-notation a la Haskell:stuff = do x <- actionX y <- actionY x z <- actionZ sideEffect finalAction x y z
The which would be translated into the following pseudo-Rust:
let stuff = do { x <- actionX; y <- actionY(x); z <- actionZ; sideEffect; finalAction(x, y, z); };
Or particularly for the
try { .. }
case:let stuff = try { let x = actionX?; let y = actionY(x)?; let z = actionZ?; sideEffect?; finalAction(x, y, z) };
The Haskell version is syntactic sugar for:
stuff = actionX >>= \x -> actionY x >>= \y -> actionZ >>= \z -> sideEffect >> finalAction x y z
or in Rust:
let stuff = actionX.flat_map(|x| // or .and_then(..) actionY(x).flat_map(|y| actionZ.flat_map(|z| sideEffect.flat_map(|_| finalAction(x, y, z) ) ) ) );
In the Haskell version,
>>=
is defined in theMonad
typeclass (trait):{-# LANGUAGE KindSignatures #-} class Applicative m => Monad (m :: * -> *) where return :: a -> m a (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b (>>) = \ma mb -> ma >>= \_ -> mb
And some instances (impls) of
Monad
are:-- | Same as Option<T> data Maybe a = Nothing | Just a instance Monad Maybe where return = Just (Just a) >>= f = f a _ >>= _ = Nothing -- | `struct Norm<T> { value: T, normalized: bool }` data Norm a = Norm a Bool instance Monad Norm where return a = Norm a False (Norm a u) >>= f = let Norm b w = f a in Norm b (u || w)
Considering the latter case of do-notation,
we saw how try { .. }
and do { .. }
relate.
In fact, try { .. }
is special to the Try
(MonadError
) monads.
There are also more forms of monads which you might want to use do { .. }
for.
Among these are: Futures, Iterators
Due to having more monads than Try
-based ones,
using the do { .. }
syntax directly as a replacement for try { .. }
becomes
problematic as it:
- confuses everyone familiar with do-notation and monads.
- is in the way of use for monads in general.
do
is generic and unclear wrt. semantics.
Alternative: reserving trap
- Fidelity to the construct’s actual behavior: Good
- Precedent from existing languages: None
- Brevity / Length: 4
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+trap\s+=|(fn|impl|mod|struct|enum|union|trait)\s+trap)\b
- Consistency with old learning material: Untaught
Review
Arguably, this candidate keyword is a somewhat a good choice.
To trap
an error is sufficiently clear on the “exception boundary” semantics
we wish to communicate.
However, trap
is used as an error handler in at least one language.
It also does not have the familiarity that try
does have and is entirely
inconsistent wrt. naming in the standard library.
Alternative: reserving wrap
- Fidelity to the construct’s actual behavior: Somewhat good
- Precedent from existing languages: None
- Brevity / Length: 4
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+wrap\s+=|(fn|impl|mod|struct|enum|union|trait)\s+wrap)\b
- Consistency with old learning material: Untaught
Review
With wrap { .. }
we can say that it “wraps” the result of the block as a
Result
/ Option
, etc. and it is logically related to .unwrap()
,
which is however a partial function, wherefore the connotation might be bad.
Also, wrap
could be considered too generic as with do
in that it could
fit for any monad.
Alternative: reserving result
- Fidelity to the construct’s actual behavior: Somewhat good
- Precedent from existing languages: None
- Brevity / Length: 6
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very high
- Used in std: Yes for the
{std, core}::result
modules. - Used as crate? Yes. 6 reverse dependencies (transitive closure).
- Usage (sourcegraph): 43+ regex:
repogroup:crates case:yes max:400 \b((let|const|type|)\s+result\s+=|(fn|impl|mod|struct|enum|union|trait)\s+result)\b
- Used in std: Yes for the
- Consistency with old learning material: Untaught
Review
The fidelity of result
is somewhat good due to the association with the
Result
type as well as Try
being a final encoding of Result
.
However, when you consider Option
, the association is less direct,
and thus it does not fit Option
and other types well.
The breakage of the result
module is however quite problematic,
making this particular choice of keyword more or less a non-starter.
Alternative: a smattering of other possible keywords
There are a host of other keywords which have been suggested.
fallible
On an internals thread, fallible
was suggested. However, this keyword lacks the verb-form that
is the convention in Rust. Breaking with this convention should only be done
if there are significant reasons to do so, which do not seem to exist in this
case. It is also considerably longer than try
(+5 character) which matters
for constructions which are oft used.
- Fidelity to the construct’s actual behavior: High
- Precedent from existing languages: None
- Brevity / Length: 8
- Consistency with related libstd fn conventions: Highly inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
- Consistency with old learning material: Untaught
Synonyms of catch
:
Some synonyms of catch
have been suggested:
accept
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 6
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Medium
repogroup:crates case:yes max:400 \b((let|const|type|)\s+accept\s+=|(fn|impl|mod|struct|enum|union|trait)\s+accept)\b
- Consistency with old learning material: Untaught
capture
- Fidelity to the construct’s actual behavior: Good.
- Precedent from existing languages: None
- Brevity / Length: 7
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+capture\s+=|(fn|impl|mod|struct|enum|union|trait)\s+capture)\b
- Consistency with old learning material: Untaught
collect
- Fidelity to the construct’s actual behavior: Very much not at all.
- Precedent from existing languages: None
- Brevity / Length: 7
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very high
- Used in std: Yes (
Iterator::collect
) - Used as crate? Yes, no reverse dependencies.
- Usage (sourcegraph): 35+ regex:
repogroup:crates case:yes max:400 \b((let|const|type|)\s+collect\s+=|(fn|impl|mod|struct|enum|union|trait)\s+collect)\b
- Used in std: Yes (
- Consistency with old learning material: Untaught
recover
- Fidelity to the construct’s actual behavior: Good
- Precedent from existing languages: None
- Brevity / Length: 7
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+recover\s+=|(fn|impl|mod|struct|enum|union|trait)\s+recover)\b
- Consistency with old learning material: Untaught
resolve
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 7
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Low to medium
repogroup:crates case:yes max:400 \b((let|const|type|)\s+resolve\s+=|(fn|impl|mod|struct|enum|union|trait)\s+resolve)\b
- Consistency with old learning material: Untaught
take
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 4
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Huge
- Used in std: Yes,
{Cell, HashSet, Read, Iterator, Option}::take
. - Used as crate? Yes, a lot of reverse dependency (transitive closure).
- Usage (sourcegraph): 62+ regex:
repogroup:crates case:yes max:400 \b((let|const|type|)\s+take\s+=|(fn|impl|mod|struct|enum|union|trait)\s+take)\b
- Used in std: Yes,
- Consistency with old learning material: Untaught
Review
Of these, only recover
and capture
seem reasonable semantically.
But recover
is even more problematic than catch
because it enhances
the feeling of exception-handling instead of exception-boundaries.
However, capture
is reasonable as a substitute for try
,
but it seems obscure and lacks familiarity, which is counted as a strong downside.
and some other keywords:
coalesce
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 8
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Medium (itertools)
repogroup:crates case:yes max:400 \b((let|const|type|)\s+coalesce\s+=|(fn|impl|mod|struct|enum|union|trait)\s+coalesce)\b
- Consistency with old learning material: Untaught
fuse
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 4
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Medium (libstd)
- Used in std: Yes,
Iterator::fuse
. - Used as crate? Yes, 8 reverse dependencies (transitive closure).
- Usage (sourcegraph): 8+ regex:
repogroup:crates case:yes max:400 \b((let|const|type|)\s+fuse\s+=|(fn|impl|mod|struct|enum|union|trait)\s+fuse)\b
- Used in std: Yes,
- Consistency with old learning material: Untaught
unite
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 5
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+unite\s+=|(fn|impl|mod|struct|enum|union|trait)\s+unite)\b
- Consistency with old learning material: Untaught
cohere
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 6
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+cohere\s+=|(fn|impl|mod|struct|enum|union|trait)\s+cohere)\b
- Consistency with old learning material: Untaught
consolidate
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 11
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+consolidate\s+=|(fn|impl|mod|struct|enum|union|trait)\s+consolidate)\b
- Consistency with old learning material: Untaught
unify
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 5
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+take\s+=|(fn|impl|mod|struct|enum|union|trait)\s+take)\b
- Consistency with old learning material: Untaught
combine
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 7
- Consistency with related libstd fn conventions: Inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Medium
repogroup:crates case:yes max:400 \b((let|const|type|)\s+combine\s+=|(fn|impl|mod|struct|enum|union|trait)\s+combine)\b
- Consistency with old learning material: Untaught
resultof
- Fidelity to the construct’s actual behavior: Somewhat
- Precedent from existing languages: None
- Brevity / Length: 8
- Consistency with related libstd fn conventions: Very inconsistent (not verb)
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage: Very low
repogroup:crates case:yes max:400 \b((let|const|type|)\s+resultof\s+=|(fn|impl|mod|struct|enum|union|trait)\s+resultof)\b
- Consistency with old learning material: Untaught
returned
- Fidelity to the construct’s actual behavior: Not at all.
- Precedent from existing languages: None
- Brevity / Length: 8
- Consistency with related libstd fn conventions: Very inconsistent
- Consistency with the naming of the trait used for
?
: Inconsistent - Risk of breakage:
repogroup:crates case:yes max:400 \b((let|const|type|)\s+returned\s+=|(fn|impl|mod|struct|enum|union|trait)\s+returned)\b
- Consistency with old learning material: Untaught
Review
Of these, only resultof
seems to be semantically descriptive and has some support. However, it has three major drawbacks:
-
Length: Compared to
try
, it is 5 characters longer (see reasoning forfallible
). -
Not a word:
resultof
is in fact a concatenation ofresult
andof
. This does not feel like a natural fit for Rust, as we tend to use a_
separator. Furthermore, there are no current keywords in use that are concatenations of two word. -
Result<T, E>
oriented:resultof
is too tied toResult<T, E>
and fits poorly withOption<T>
or other types that implementTry
.
Prior art
All of the languages listed below have a try { .. } <handler_kw> { .. }
concept
(modulo layout syntax / braces) where <handler_kw>
is one of:
catch
, with
, except
, trap
, rescue
.
In total, these are 29 languages and they have massive ~80% dominance according to the TIOBE index and roughly the same with the PYPL index.
- C++
- D
- C#
- Java
- Scala
- Kotlin
- JavaScript
- TypeScript
- ActionScript
- Dart
- Python
- PHP
- Matlab
- Visual Basic
- OCaml
- F#
- Objective C
- Swift
- Delphi
- Julia
- Elixir
- Erlang
- Clojure
- R, modulo minor syntactic difference.
- Powershell
- Tcl
- Apex
- RPG
- ABAP
The syntactic form catch { .. }
seems quite rare and is,
together with trap
, rescue
, except
, only used for handlers.
However, the <kw> { .. }
expression we want to introduce is not a handler,
but rather the body of expression we wish to try
.
There are however a few languages where catch { .. }
is used for the fallible
part and not for the handler, these languages are:
However, the combined popularity of these languages are not significant as
compared to that for try { .. }
.
Unresolved questions
None as of yet.