The Wayback Machine - https://web.archive.org/web/20210421214106/https://github.com/scala/scala/pull/4400
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can catch any expression #4400

Merged
merged 2 commits into from Mar 9, 2021
Merged

Can catch any expression #4400

merged 2 commits into from Mar 9, 2021

Conversation

@som-snytt
Copy link
Contributor

@som-snytt som-snytt commented Mar 24, 2015

catch accepts an arbitrary expression, which must conform
to Function[Throwable, ?].

The previous transform was name-based:

scala> try 42 catch new {
     | def isDefinedAt(x: Any) = false
     | def apply(x: Any) = ()
     | }
warning: there was one feature warning; re-run with -feature for details
res0: AnyVal = 42

scala> try 42 catch 22
<console>:8: error: value isDefinedAt is not a member of Int
              try 42 catch 22
                           ^

Now:

scala> try 42 catch 22
                    ^
       error: type mismatch;
        found   : Int(22)
        required: Throwable => ?

scala> def npe: Int = throw null
def npe: Int

scala> val pf: PartialFunction[Throwable, Int] = { case _: NullPointerException => 42 }
val pf: PartialFunction[Throwable,Int] = <function1>

scala> try npe catch pf
val res0: Int = 42

scala> def err: Int = throw new Error()
def err: Int

scala> try err catch pf
java.lang.Error
  at err(<console>:1)
  at liftedTree1$1(<console>:1)
  ... 33 elided

As shown, if the handler is a PartialFunction, it is invoked
selectively, as though by calling:

pf.applyOrElse(e, (t: Throwable) => throw t)

Otherwise, the handler is applied to all thrown exceptions:

scala> def f(t: Throwable) = if (t.isInstanceOf[NullPointerException]) 27 else ???
def f(t: Throwable): Int

scala> try err catch f
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:345)
  at f(<console>:1)
  at $anonfun$res6$1(<console>:1)
  at $anonfun$res6$1$adapted(<console>:1)
  at liftedTree1$1(<console>:1)
  ... 33 elided

Note that applying a partial function literal in the form of a pattern-matching
anonymous function will use applyOrElse. The following example invokes
the apply method because the static type of the handler is not a
PartialFunction:

scala> val g: Throwable => Int = pf
val g: Throwable => Int = <function1>

scala> try err catch g
scala.MatchError: java.lang.Error (of class java.lang.Error)
  at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:344)
  at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:342)
  at $anonfun$1.applyOrElse(<console>:1)
  at $anonfun$1.applyOrElse(<console>:1)
  at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
  at liftedTree1$1(<console>:1)
  ... 33 elided

@scala-jenkins scala-jenkins added this to the 2.12.0-M1 milestone Mar 24, 2015
@som-snytt som-snytt closed this Mar 25, 2015
@SethTisue SethTisue removed this from the 2.12.0-M1 milestone Jul 20, 2015
@som-snytt som-snytt reopened this Jun 7, 2016
@scala-jenkins scala-jenkins added this to the 2.12.0-RC1 milestone Jun 7, 2016
@som-snytt som-snytt force-pushed the som-snytt:issue/5887-rebased branch from 805becc to 880d48e Jun 7, 2016
@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Jun 7, 2016

Rebased. There may be review work pending around hygiene? Actually, it looks like I addressed some comments from a year ago. But there's a bootstrap issue with try relying on library code.

case Match(EmptyTree, cases) => cases
case _ =>
val binder = freshTermName()
val pat = Bind(binder, Typed(Ident(termNames.WILDCARD), Ident(typeNames.Throwable)))

This comment has been minimized.

@retronym

retronym Jun 7, 2016
Member

This appears unhygenic, 😷

This comment has been minimized.

@som-snytt

som-snytt Jun 7, 2016
Author Contributor

I think you mentioned that a year ago... If I can get it to build, I'll polish it.

import gen.CODE.REF
val doit = Apply(
Select(
Apply(REF(currentRun.runDefinitions.catchIdentityMethod), catchExpr :: Nil),

This comment has been minimized.

@retronym

retronym Jun 7, 2016
Member

The parser needs to be threadsafe, so this would need to be eagerly initialized. We eagerly initialize all class/module symbols referenced in the parser in forceSymbolsUsedByParser. See 760df98 for the corresponding test to drive the change.

I would probably suggest selecting the method by name (Select(ScalaRuntimeModule, nme.catchIdentity)) here to avoid finding the right spot to initialize the lazy val in RunDefinitions in the same manner as forceSymbolsUsedByParser. It is probably safest not to use Run at all from the parser.

This comment has been minimized.

@som-snytt

som-snytt Jun 7, 2016
Author Contributor

Thanks, I remember I hadn't understood this yet.

@retronym
Copy link
Member

@retronym retronym commented Jun 7, 2016

Would it be possible to submit the part of this that fixes parsing of try { 1; 1 } + 1 catch from the part that changes how catch expressions are translated? I can't remember the context for the latter part, and I'm a bit hesitant about having catch sometimes translating into something heavyweight (an applyOrElse call with a lambda as an argument).

@SethTisue
Copy link
Member

@SethTisue SethTisue commented Jun 18, 2019

7-year itch, scratched.

@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Sep 3, 2020

7 years later.

@som-snytt som-snytt reopened this Sep 3, 2020
@scala-jenkins scala-jenkins added this to the 2.12.13 milestone Sep 3, 2020
@som-snytt som-snytt force-pushed the som-snytt:issue/5887-rebased branch from 880d48e to 5d8b91f Sep 3, 2020
@som-snytt som-snytt changed the base branch from 2.12.x to 2.13.x Sep 3, 2020
@som-snytt som-snytt modified the milestones: 2.12.13, 2.13.4 Sep 3, 2020
@som-snytt som-snytt changed the title SI-5887: Can try any expression Can catch any expression Sep 3, 2020
@som-snytt som-snytt marked this pull request as draft Sep 3, 2020
@som-snytt som-snytt force-pushed the som-snytt:issue/5887-rebased branch from 5d8b91f to 9638fc1 Sep 3, 2020
@dwijnand
Copy link
Member

@dwijnand dwijnand commented Sep 10, 2020

:head_scratch: is this addressing scala/bug#5887?

@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Sep 10, 2020

@dwijnand this is the catch part. It doesn't typecheck correctly yet, mea culpa. Dotty requires a Function instead of PartialFunction, but this is an improvement over the status quo.

@dwijnand dwijnand removed this from the 2.13.4 milestone Oct 16, 2020
@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Dec 22, 2020

My previous joke about Try is relevant. That is, prefer Try to try for this sort of safety.

I'll add a lint if that will assuage our collective conscience, but I think the catch function is quite niche.

The use case for linting would be, when I write the handler, they never made it easy for me to specify the type:

val handler: PartialFunction[Throwable, String] = { case _ => "hello, world" }

val danger_? = (t: Throwable) => "goodbye, cruel world"

try e catch ???

Using handler doesn't warn. Or rather, defining it does not warn.

@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Dec 22, 2020

Gah, lost my comment edit when I clicked on the validate failure! which was spurious, of course.

To recap, now it issues the usual warning on catch with total function.

My comment also complained about stretching this PR into a long tail.

To answer Seth's question, the usual tractable syntax is the block of case clauses. Anyone doing something else syntactically will know what they're doing.

I wanted to make the case that in a Scalable language, I should be able to say try f() catch println in REPL. But I don't want to get into a philosophical debate about it either.

This change goes to regularity.

@som-snytt

This comment has been hidden.

@SethTisue
Copy link
Member

@SethTisue SethTisue commented Jan 6, 2021

@lrytz care to weigh in about the design issues here?

@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Jan 6, 2021

Closing because I don't care any more.

@lrytz
Copy link
Member

@lrytz lrytz commented Jan 7, 2021

Allowing Function instead of requiring PartialFunction only encourages catching Throwable, which is pretty much never a good idea (because of e.g. OutOfMemoryError). What desirable code pattern does this enable?

This is a good point.

Opened lampepfl/dotty#11019, also that Scala 3 doesn't have the "catches all Throwables" warning at all.

@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Jan 8, 2021

The good point was addressed in the follow-up.

@lrytz
Copy link
Member

@lrytz lrytz commented Jan 8, 2021

👍, I noticed the warning. So it would be a new, handy feature for yolo-hacking, but discouraged from the beginning for larger projects.

@SethTisue
Copy link
Member

@SethTisue SethTisue commented Jan 8, 2021

(To be clear, I'm not opposed, as long as it warns. But can't get excited about it either.)

som-snytt added 2 commits Feb 15, 2015
This commit takes an arbitrary expression to `catch`.

The expression is required to conform to `Function[Throwable, ?]`.
The previous transform was name-based.

If the handler is a `PartialFunction`, it is invoked conditionally.

More behavior tests for catch expression.
@som-snytt som-snytt reopened this Feb 5, 2021
@scala-jenkins scala-jenkins added this to the 2.13.5 milestone Feb 5, 2021
@som-snytt som-snytt force-pushed the som-snytt:issue/5887-rebased branch from 6144f5e to aa708e3 Feb 5, 2021
Copy link
Member

@dwijnand dwijnand left a comment

Thanks, Som!

@lrytz
Copy link
Member

@lrytz lrytz commented Feb 8, 2021

I'm slightly torn; it's generally good to align Scala 2 and 3, and I also think having the warning is right. But then we introduce a new feature that gives a warning as soon and as long as you use it. Does that make sense?

Are we certain that the change in dotty was fully intentioned?

@lrytz lrytz requested a review from SethTisue Feb 8, 2021
@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Feb 8, 2021

We can adjust later to the status quo established by the dotty ticket.

It's not like everyone will start writing try e catch f everywhere. Probably no one notices the difference.

But it's much more regular than the previous state of affairs.

I had an example of a feature that was introduced and immediately deprecated, but I don't remember it off the cuff.

For the record, now I believe the compiler should emit no warnings at all, because someone will always complain either that the warning is too strict or too lax. Leave it to 3rd party tooling to express preferences and unwarranted fears about style.

I re-opened this PR because it's an obvious good.

@SethTisue SethTisue modified the milestones: 2.13.5, 2.13.6 Feb 9, 2021
@SethTisue
Copy link
Member

@SethTisue SethTisue commented Mar 9, 2021

there hasn't been any response on lampepfl/dotty#11019

Lukas and I are both on the fence, but Dale likes it, and Som is still behind this even after having had six years to think about it (😬), so let's merge

@SethTisue SethTisue merged commit 4e3bd2b into scala:2.13.x Mar 9, 2021
4 checks passed
4 checks passed
Travis CI - Pull Request Build Passed
Details
cla @som-snytt signed the Scala CLA. Thanks!
Details
combined All previous commits successful.
validate-main [14078] SUCCESS. Took 52 min.
Details
@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Mar 9, 2021

I'm going to delete this branch and go back to sleep.

I'm grateful there was no need to come up with an option name like for splain.

I'm sad Seth is not excited.

I see there were a few previous summations. My footnote would be to echo my previous comment: really, people on the fence want to say, Don't use try, use Try instead. Why don't we say that?

Because of the pun, "on the fens", I have learned from wikipedia that a fen is a type of mire, but less acidic than a bog. Please keep this in mind when saying casually, "I'm bogged down," "in a mire," etc, let alone, "on the fens." Let's use language precisely.

@som-snytt som-snytt deleted the som-snytt:issue/5887-rebased branch Mar 9, 2021
@SethTisue
Copy link
Member

@SethTisue SethTisue commented Mar 9, 2021

a fen is a type of mire, but less acidic than a bog

as for me, just yesterday I learned what “muskeg” is

@som-snytt
Copy link
Contributor Author

@som-snytt som-snytt commented Mar 9, 2021

This PR was definitely muskegged.

No relation to "musk egg," "Muskogee," or "beer keg." The keg hauled by moose on rescue missions would be moose keg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.