From 28513a338952b03138e1030c96bc4bad37e181fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Wa=C5=9Bko?= Date: Mon, 11 Jul 2022 10:26:44 +0200 Subject: [PATCH 1/2] Allow filtering caught error type in `Error.catch` (#3574) More and more often I need a way to only recover a specific type of a dataflow error (in a similar manner as with panics). So the API for `Error.catch` has been amended to more closely resemble `Panic.catch`, allowing to handle only specific types of dataflow errors, passing others through unchanged. The default is `Any`, meaning all errors are caught by default, and the behaviour of `x.catch` remains unchanged. --- CHANGELOG.md | 2 + .../Standard/Base/0.0.0-dev/src/Data/Any.enso | 36 ++++++--- .../Base/0.0.0-dev/src/Data/Json.enso | 4 +- .../0.0.0-dev/src/Data/Json/Internal.enso | 2 +- .../Standard/Base/0.0.0-dev/src/Data/Map.enso | 2 +- .../Base/0.0.0-dev/src/Data/Time.enso | 8 +- .../Base/0.0.0-dev/src/Data/Time/Date.enso | 16 ++-- .../0.0.0-dev/src/Data/Time/Time_Of_Day.enso | 9 +-- .../Base/0.0.0-dev/src/Error/Common.enso | 76 +++++++++++++++---- .../lib/Standard/Base/0.0.0-dev/src/Meta.enso | 2 +- .../Base/0.0.0-dev/src/Network/Http.enso | 12 +-- .../0.0.0-dev/src/Network/Http/Request.enso | 3 +- .../Base/0.0.0-dev/src/Network/Internal.enso | 17 ----- .../Base/0.0.0-dev/src/Network/Uri.enso | 5 +- .../System/File/Existing_File_Behavior.enso | 4 +- .../Standard/Base/0.0.0-dev/src/Warning.enso | 2 +- .../Standard/Image/0.0.0-dev/src/Codecs.enso | 12 +-- .../Image/0.0.0-dev/src/Data/Image.enso | 8 +- .../Table/0.0.0-dev/src/Data/Column.enso | 4 +- .../Table/0.0.0-dev/src/Data/Table.enso | 2 +- .../src/Internal/Aggregate_Column_Helper.enso | 4 +- .../src/Internal/Delimited_Reader.enso | 4 +- .../src/Internal/Parse_Values_Helper.enso | 2 +- .../Standard/Table/0.0.0-dev/src/Main.enso | 4 +- .../lib/Standard/Test/0.0.0-dev/src/Main.enso | 11 +-- .../Visualization/0.0.0-dev/src/Helpers.enso | 6 +- .../0.0.0-dev/src/Table/Visualization.enso | 2 +- .../test/instrument/RuntimeServerTest.scala | 7 +- .../test/semantic/DataflowErrorsTest.scala | 2 +- test/Tests/src/Data/Json_Spec.enso | 2 +- test/Tests/src/Data/Time/Date_Spec.enso | 10 +-- .../Tests/src/Data/Time/Time_Of_Day_Spec.enso | 10 +-- test/Tests/src/Data/Time/Time_Spec.enso | 6 +- test/Tests/src/Data/Vector_Spec.enso | 2 +- test/Tests/src/Semantic/Conversion_Spec.enso | 4 +- test/Tests/src/Semantic/Error_Spec.enso | 19 ++++- .../Base/0.0.0-dev/src/Error/Common.enso | 6 +- .../lib/Standard/Base/0.0.0-dev/src/Main.enso | 3 + 38 files changed, 194 insertions(+), 136 deletions(-) delete mode 100644 distribution/lib/Standard/Base/0.0.0-dev/src/Network/Internal.enso diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b916df667..6bf432e4ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -149,6 +149,7 @@ - [Added `File_Format.Excel` support to `Table.write` for new files.][3551] - [Added append support for `File_Format.Excel`.][3558] - [Added support for custom encodings in `File_Format.Delimited` writing.][3564] +- [Allow filtering caught error type in `Error.catch`.][3574] [debug-shortcuts]: https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug @@ -237,6 +238,7 @@ [3552]: https://github.com/enso-org/enso/pull/3552 [3558]: https://github.com/enso-org/enso/pull/3558 [3564]: https://github.com/enso-org/enso/pull/3564 +[3574]: https://github.com/enso-org/enso/pull/3574 #### Enso Compiler diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Any.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Any.enso index f0881e98cb..f18fc244b4 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Any.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Any.enso @@ -1,5 +1,7 @@ from Standard.Base import all +from Standard.Base.Error.Common import dataflow_error_handler + # The type that subsumes all types. type Any @@ -18,7 +20,7 @@ type Any Arguments: - handler: The function to call on this if it is an error value. catch_primitive : (Error -> Any) -> Any - catch_primitive handler = @Builtin_Method "Any.catch" + catch_primitive handler = @Builtin_Method "Any.catch_primitive" ## Generic conversion of an arbitrary Enso value to a corresponding textual representation. @@ -246,23 +248,37 @@ type Any if_nothing : Any -> Any if_nothing ~_ = self - ## Executes the provided handler on an error, or returns a non-error value - unchanged. + ## Executes the provided handler on an error, or returns the value unchanged. Arguments: - - handler: The function to call on this if it is an error value. By default - this is identity. + - error_type: The type of error to handle. Defaults to `Any` to handle + all errors. + - handler: The function to call on this if it is an error value of a + matching type. By default this is identity. > Example - Catching an erroneous value and getting the length of its message. + Catching an `Illegal_Argument_Error` and returning its message. from Standard.Base import all example_catch = - error = Error.throw "My message" - error.catch (err -> err.length) - catch : (Error -> Any) -> Any - catch (handler = x->x) = self.catch_primitive handler + error = Error.throw (Illegal_Argument_Error "My message") + error.catch Illegal_Argument_Error (err -> err.message) + + > Example + Catching any dataflow error and turning it into a regular value. + + from Standard.Base import all + + example_catch = + error = Error.throw 42 + error.catch == 42 + catch : Any -> (Error -> Any) -> Any + catch (error_type = Any) (handler = x->x) = + self.catch_primitive error_value-> + case error_value.is_a error_type of + True -> handler error_value + False -> self ## Transforms an error. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso index 6d7c0ac774..3663d8416a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso @@ -38,8 +38,8 @@ from_pairs contents = example_parse = Json.parse '{ "a": 1 }' parse : Text -> Json ! Parse_Error parse json_text = - Panic.catch Polyglot_Error (Internal.parse_helper json_text) caught_panic-> - Error.throw (Parse_Error caught_panic.payload.cause.getMessage) + Panic.catch_java Any (Internal.parse_helper json_text) java_exception-> + Error.throw (Parse_Error java_exception.getMessage) ## Represents a JSON structure. type Json diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json/Internal.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json/Internal.enso index 70812ca4da..c5448f892a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json/Internal.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json/Internal.enso @@ -308,7 +308,7 @@ into_helper fmt json = case fmt of fnames = cons.fields ffmts = m.fields field_values = fnames.zip ffmts n-> inner_fmt-> - fjson = json_fields . get n . catch _-> + fjson = json_fields . get n . catch Any _-> Panic.throw (Missing_Field_Error json fmt n) into_helper inner_fmt fjson cons.new field_values diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Map.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Map.enso index 9366fb829c..7896829a24 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Map.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Map.enso @@ -217,7 +217,7 @@ type Map example_get_or_else = Examples.map.get_or_else 2 "zero" get_or_else : Any -> Any -> Any get_or_else key ~other = - self.get key . catch (_ -> other) + self.get key . catch No_Value_For_Key_Error (_ -> other) ## Transforms the map's keys and values to create a new map. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time.enso index 9b1b50daef..f9c9892f7c 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time.enso @@ -56,9 +56,8 @@ now = Time ZonedDateTime.now example_new = Time.new 1986 8 5 new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Time ! Time_Error new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) (zone = Zone.system) = - Panic.recover Any (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) . catch e-> case e of - Polyglot_Error err -> Error.throw (Time_Error err.getMessage) - x -> x + Panic.catch_java Any (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) java_exception-> + Error.throw (Time_Error java_exception.getMessage) ## ALIAS Time from Text @@ -122,8 +121,7 @@ new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond import Standard.Base.Data.Time - example_parse = Time.parse "2020-10-01" . catch e-> case e of - Time.Error _ -> Time.now + example_parse = Time.parse "2020-10-01" . catch Time_Error (_->Time.now) > Example Parse "2020-05-06 04:30:20" as Time diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index 185525eb48..4c52cbb05d 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -60,7 +60,14 @@ today = now new : Integer -> Integer -> Integer -> Date ! Time.Time_Error new year (month = 1) (day = 1) = - Panic.recover Any (LocalDate.of year month day) . catch e-> case e of + ## TODO This is left using the old error handling approach, because + magically, changing this to the `catch_java` (which is now preferred way + of catching Polyglot_Errors) lead to the "should format local date using + provided pattern" test failing because it called the `LocalDate.format` + instead of Enso format. Hopefully this will be fixed with + https://github.com/enso-org/enso/pull/3559 + Then this should be switched to use `Panic.catch_java`. + Panic.recover Any (LocalDate.of year month day) . catch Any e-> case e of Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage) x -> x @@ -107,8 +114,8 @@ new year (month = 1) (day = 1) = import Standard.Base.Data.Time.Date import Standard.Base.Data.Time - example_parse_err = Date.parse "my birthday" . catch e-> case e of - Time.Time_Error _ -> Date.new 2000 1 1 + example_parse_err = Date.parse "my birthday" . catch Time.Time_Error _-> + Date.new 2000 1 1 > Example Parse "1999-1-1" as Date using a custom format. @@ -125,8 +132,7 @@ new year (month = 1) (day = 1) = example_parse_err = date = Date.parse "1999-1-1" "yyyy-MM-dd" - date.catch e-> case e of - Time.Time_Error _ -> Date.new 2000 1 1 + date.catch Time.Time_Error (_->Date.new 2000 1 1) parse : Text -> (Text | Nothing) -> Date ! Time.Time_Error parse text pattern=Nothing = result = Panic.recover Any <| case pattern of diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index d21200a4e7..9b05986550 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -48,9 +48,8 @@ now = Time_Of_Day LocalTime.now example_epoch = Time_Of_Day.new hour=9 minute=30 new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day ! Time.Time_Error new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) = - Panic.recover Any (Time_Of_Day (LocalTime.of hour minute second nanosecond)) . catch e-> case e of - Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage) - x -> x + Panic.catch_java Any (Time_Of_Day (LocalTime.of hour minute second nanosecond)) java_exception-> + Error.throw (Time.Time_Error java_exception.getMessage) ## Obtains an instance of `Time_Of_Day` from a text such as "10:15". @@ -97,8 +96,8 @@ new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) = import Standard.Base.Data.Time import Standard.Base.Data.Time.Time_Of_Day - example_parse = Time_Of_Day.parse "half twelve" . catch e-> case e of - Time.Time_Error _ -> Time_Of_Day.new + example_parse = Time_Of_Day.parse "half twelve" . catch Time.Time_Error _-> + Time_Of_Day.new > Example Parse "04:30:20" as Time_Of_Day. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso index d368f0e7ad..09af7617dd 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso @@ -61,24 +61,39 @@ type Error Returns a human-readable text representing this error. to_display_text : Text - to_display_text = "Error: " + (self.catch .to_display_text) + to_display_text = "Error: " + (self.catch Any .to_display_text) - ## Executes the provided handler on a dataflow error, or returns a non-error - value unchanged. + ## Executes the provided handler on an error, or returns the value unchanged. Arguments: - - handler: The function to call on this if it is an error value. By default - this is identity. + - error_type: The type of error to handle. Defaults to `Any` to handle + all errors. + - handler: The function to call on this if it is an error value of a + matching type. By default this is identity. > Example - Catching an erroneous value and getting the length of its message. + Catching an `Illegal_Argument_Error` and returning its message. - import Standard.Examples + from Standard.Base import all example_catch = - Examples.throw_error.catch (err -> err.message.length) - catch : (Error -> Any) -> Any - catch (handler = x->x) = self.catch_primitive handler + error = Error.throw (Illegal_Argument_Error "My message") + error.catch Illegal_Argument_Error (err -> err.message) + + > Example + Catching any dataflow error and turning it into a regular value. + + from Standard.Base import all + + example_catch = + error = Error.throw 42 + error.catch == 42 + catch : Any -> (Error -> Any) -> Any + catch (error_type = Any) (handler = x->x) = + self.catch_primitive error_value-> + case error_value.is_a error_type of + True -> handler error_value + False -> self ## UNSTABLE @@ -91,7 +106,7 @@ type Error example_display = Examples.throw_error.to_default_visualization_data to_default_visualization_data : Text - to_default_visualization_data = self.catch .to_default_visualization_data + to_default_visualization_data = self.catch Any .to_default_visualization_data ## UNSTABLE @@ -106,8 +121,9 @@ type Error to_json : Json.Object to_json = error_type = ["type", "Error"] - error_content = ["content", self.catch .to_json] - error_message = ["message", self.catch .to_display_text] + caught = self.catch + error_content = ["content", caught.to_json] + error_message = ["message", caught.to_display_text] Json.from_pairs [error_type, error_content, error_message] ## Transforms an error. @@ -127,7 +143,7 @@ type Error map = Examples.map map.get 10 . map_error (_ -> "The element 10 was not found.") map_error : (Error -> Error) -> Any - map_error f = self.catch (x -> Error.throw (f x)) + map_error f = self.catch Any (x -> Error.throw (f x)) ## ADVANCED UNSTABLE @@ -297,7 +313,7 @@ type Panic example_rethrow = Panic.rethrow Examples.throw_error rethrow : (Any ! Any) -> Any - rethrow value = value.catch Panic.throw + rethrow value = value.catch Any Panic.throw ## Executes the provided action and if a panic matching the provided type was thrown, calls the provided callback. @@ -351,6 +367,36 @@ type Panic False -> Panic.throw caught_panic _ -> Panic.throw caught_panic + ## Executes the provided action and if a Java exception matching the provided type was + thrown, calls the provided callback. + + Normally, Java exceptions are wrapped in a `Polyglot_Error` instance, so + using a `Panic.catch` requires unwrapping the error by calling + `caught_panic.payload.cause`. This helper function allows the handler to + work with the Java exception directly. The downside is that if the Java + exception is rethrown, it will be rethrown as a Java exception object + wrapped in an Enso panic. So if the handler needs to rethrow the original + exception preserving its shape and stacktrace, `Panic.catch` should still + be preferred.` + + > Example + Convert a string to an integer, catching the Java `NumberFormatException` + and converting it to a more Enso-friendly dataflow error. + + polyglot java import java.lang.Long + polyglot java import java.lang.NumberFormatException + parse str = + Panic.catch_java NumberFormatException (Long.parseLong str) java_exception-> + Error.throw (Illegal_Argument_Error "The provided string is not a valid number: "+java_exception.getMessage) + catch_java : Any -> Any -> (Throwable -> Any) -> Any + catch_java panic_type ~action handler = + Panic.catch_primitive action caught_panic-> case caught_panic.payload of + Polyglot_Error java_exception -> + case (panic_type == Any) || (Java.is_instance java_exception panic_type) of + True -> handler java_exception + False -> Panic.throw caught_panic + _ -> Panic.throw caught_panic + ## Executes the provided action and converts a possible panic matching any of the provided types into a dataflow Error. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index e513bac0fe..da73038489 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -293,7 +293,7 @@ Base.Error.is_a typ = self.is_an typ Arguments: - typ: The type to check `self` against. Base.Error.is_an : Any -> Boolean -Base.Error.is_an typ = typ == Base.Error +Base.Error.is_an typ = typ==Any || typ==Base.Error ## UNSTABLE ADVANCED diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso index 67671bdcb9..14b1e94b0f 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http.enso @@ -10,7 +10,6 @@ import Standard.Base.Network.Http.Request import Standard.Base.Network.Http.Request.Body as Request_Body import Standard.Base.Network.Http.Response import Standard.Base.Network.Http.Version -import Standard.Base.Network.Internal import Standard.Base.Network.Proxy import Standard.Base.Network.Uri import Standard.Base.System.File @@ -597,11 +596,13 @@ type Http http.request req request : Request -> Response ! Request_Error request req = - response = Panic.recover Any <| + handle_request_error = + Panic.catch_java Any handler=(err-> Error.throw (Request_Error err.getClass.getSimpleName err.getMessage)) + Panic.recover Any <| handle_request_error <| body_publishers = HttpRequest.BodyPublishers builder = HttpRequest.newBuilder # set uri - builder.uri (Internal.panic_on_error req.uri.internal_uri) + builder.uri (Panic.rethrow req.uri.internal_uri) # prepare headers and body req_with_body = case req.body of Request_Body.Empty -> @@ -651,11 +652,6 @@ type Http http_request = builder.build body_handler = HttpResponse.BodyHandlers . ofByteArray Response.Response (self.internal_http_client.send http_request body_handler) - response.catch e-> case e of - Polyglot_Error err -> - Error.throw (Request_Error err.getClass.getSimpleName err.getMessage) - _ -> - Error.throw e ## PRIVATE diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Request.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Request.enso index 34ce003478..c0620311ce 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Request.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Http/Request.enso @@ -4,7 +4,6 @@ import Standard.Base.Network.Http.Form import Standard.Base.Network.Http.Header import Standard.Base.Network.Http.Method import Standard.Base.Network.Http.Request.Body as Request_Body -import Standard.Base.Network.Internal import Standard.Base.Network.Uri import Standard.Base.System.File @@ -26,7 +25,7 @@ import Standard.Base.System.File example_new = Request.new Method.Post (Uri.parse "http://example.com") new : Method -> (Text | Uri) -> Vector.Vector -> Request_Body -> Request new method addr (headers = []) (body = Request_Body.Empty) = - Panic.recover Any (Request method (Internal.panic_on_error (addr.to_uri)) headers body) . catch Internal.recover_panic + Panic.recover Any (Request method (Panic.rethrow (addr.to_uri)) headers body) ## Create an Options request. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Internal.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Internal.enso deleted file mode 100644 index d371766ebe..0000000000 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Internal.enso +++ /dev/null @@ -1,17 +0,0 @@ -from Standard.Base import all - -## PRIVATE - - Panics on encountering an error. - - Arguments: - - action: The action to perform that may recurn an error. -panic_on_error : Any -> Any -panic_on_error ~action = - action . catch Panic.throw - -## PRIVATE - - Recovers from a panic. -recover_panic : Any -> Error -recover_panic error = Error.throw error diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Uri.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Uri.enso index d25a6f7aff..5b84db5d53 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Uri.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Network/Uri.enso @@ -22,9 +22,8 @@ polyglot java import java.util.Optional example_parse = Uri.parse "http://example.com" parse : Text -> Uri ! Syntax_Error parse text = - Panic.recover Any (Uri (Java_URI.create text)) . catch e-> case e of - Polyglot_Error ex -> Error.throw (Syntax_Error ex.getMessage) - other -> Panic.throw other + Panic.catch_java Any (Uri (Java_URI.create text)) java_exception-> + Error.throw (Syntax_Error java_exception.getMessage) ## Convert Text to a Uri. diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Existing_File_Behavior.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Existing_File_Behavior.enso index 390c3f8331..7a56c9509e 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Existing_File_Behavior.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Existing_File_Behavior.enso @@ -57,7 +57,7 @@ type Existing_File_Behavior temporary file and creates a backup. handle_file_already_exists <| handle_internal_dataflow <| Panic.rethrow <| file.with_output_stream [Option.Write, Option.Create_New] output_stream-> - action output_stream . catch dataflow_error-> + action output_stream . catch Any dataflow_error-> Panic.throw (Internal_Write_Operation_Errored dataflow_error) ## PRIVATE @@ -87,7 +87,7 @@ write_file_backing_up_old_one file action = Panic.recover [Io_Error, File_Not_Fo new_file.with_output_stream [Option.Write, Option.Create_New] output_stream-> result = Panic.catch Any (action output_stream) caught_panic-> Panic.throw (Internal_Write_Operation_Panicked caught_panic) - result.catch dataflow_error-> + result.catch Any dataflow_error-> Panic.throw (Internal_Write_Operation_Errored dataflow_error) ## We ignore the file not found error, because it means that there is no file to back-up. This may also be caused by someone diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso index d78c9db1df..7f23f528a7 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Warning.enso @@ -156,7 +156,7 @@ map_attached_warnings mapper value = map_warnings_and_errors : (Any -> Maybe Any) -> Any -> Any map_warnings_and_errors mapper value = mapped_warnings_or_error = map_attached_warnings_helper mapper value 1 - mapped_warnings_or_error.catch error-> + mapped_warnings_or_error.catch Any error-> case mapper error of Maybe.Some new_error -> Error.throw new_error ## If the mapper did not want to affect the error, we return the diff --git a/distribution/lib/Standard/Image/0.0.0-dev/src/Codecs.enso b/distribution/lib/Standard/Image/0.0.0-dev/src/Codecs.enso index ea72e114d7..34d536a3d6 100644 --- a/distribution/lib/Standard/Image/0.0.0-dev/src/Codecs.enso +++ b/distribution/lib/Standard/Image/0.0.0-dev/src/Codecs.enso @@ -38,10 +38,8 @@ read location flags=[] = if flags.is_empty then Java_Codecs.READ_FLAG_EMPTY else flags.map .to_integer . reduce (_.bit_or _) _ -> flags.to_integer - Panic.recover Any (Image.Image (Java_Codecs.read path read_flags)) . catch e-> - case e of - Polyglot_Error _ -> Error.throw (File.Io_Error (File.new path) 'Failed to read the file') - err -> Error.throw err + Panic.catch_java Any (Image.Image (Java_Codecs.read path read_flags)) java_exception-> + Error.throw (File.Io_Error (File.new path) 'Failed to read the file') ## UNSTABLE @@ -71,10 +69,8 @@ Image.Image.write location flags=[] = Vector.Vector _ -> flags _ -> [flags] int_flags = Internal.mat_of_int (write_flags.flat_map x-> [x.to_integer, x.value]) - Panic.recover Any (Java_Codecs.write path self.opencv_mat int_flags) . catch e-> - case e of - Polyglot_Error _ -> Error.throw (File.Io_Error (File.new path) 'Failed to write to the file') - err -> Error.throw err + Panic.catch_java Any (Java_Codecs.write path self.opencv_mat int_flags) java_exception-> + Error.throw (File.Io_Error (File.new path) 'Failed to write to the file') ## UNSTABLE type Read_Flag diff --git a/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Image.enso b/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Image.enso index 7ce2f81cd6..8d06d2c99b 100644 --- a/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Image.enso +++ b/distribution/lib/Standard/Image/0.0.0-dev/src/Data/Image.enso @@ -153,7 +153,7 @@ type Image image = Examples.image image + (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels) + : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal - + value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.add _ _ _)) . catch Internal.core_op_handler + + value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.add _ _ _)) . catch Any Internal.core_op_handler ## UNSTABLE @@ -204,7 +204,7 @@ type Image image = Examples.image image - (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels) - : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal - - value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.subtract _ _ _)) . catch Internal.core_op_handler + - value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.subtract _ _ _)) . catch Any Internal.core_op_handler ## UNSTABLE @@ -255,7 +255,7 @@ type Image image = Examples.image image * (Matrix.ones rows=image.rows columns=image.columns channels=image.channels) * : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal - * value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.multiply _ _ _)) . catch Internal.core_op_handler + * value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.multiply _ _ _)) . catch Any Internal.core_op_handler ## UNSTABLE @@ -306,7 +306,7 @@ type Image image = Examples.image image / (Matrix.ones rows=image.rows columns=image.columns channels=image.channels) / : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal - / value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.divide _ _ _)) . catch Internal.core_op_handler + / value = Panic.recover Any (Internal.core_op self.opencv_mat value (Java_Image.divide _ _ _)) . catch Any Internal.core_op_handler ## UNSTABLE diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso index 76c05b517c..64473654e8 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Column.enso @@ -1061,7 +1061,7 @@ type Column example_first = Examples.integer_column.first first : Any ! Empty_Error - first = self.at 0 . catch (_ -> Error.throw Empty_Error) + first = self.at 0 . catch Index_Out_Of_Bounds_Error (_ -> Error.throw Empty_Error) ## UNSTABLE @@ -1093,7 +1093,7 @@ type Column example_last = Examples.integer_column.last last : Any ! Empty_Error - last = self.at (self.length - 1) . catch (_ -> Error.throw Empty_Error) + last = self.at (self.length - 1) . catch Index_Out_Of_Bounds_Error (_ -> Error.throw Empty_Error) ## UNSTABLE diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso index e46147fe26..e4cf77c503 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Data/Table.enso @@ -198,7 +198,7 @@ type Table example_to_json = Examples.inventory_table.to_json to_json : Json to_json = - index_prep = case self.index.catch (_->Nothing) of + index_prep = case self.index.catch No_Index_Set_Error (_->Nothing) of Nothing -> [] index -> [index] cols = index_prep + self.columns diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Aggregate_Column_Helper.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Aggregate_Column_Helper.enso index a92a7a430e..46253b6119 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Aggregate_Column_Helper.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Aggregate_Column_Helper.enso @@ -167,9 +167,7 @@ resolve_aggregate table problem_builder aggregate_column = ## Downgrade the `Internal_Missing_Column_Error` error into a `Nothing` value, keeping any other dataflow errors intact. - result.catch err-> case err of - Internal_Missing_Column_Error -> Nothing - _ -> result + result.catch Internal_Missing_Column_Error (_->Nothing) ## PRIVATE A marker for missing columns during resolution. diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Delimited_Reader.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Delimited_Reader.enso index b8d09677da..7a195d4371 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Delimited_Reader.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Delimited_Reader.enso @@ -68,8 +68,8 @@ read_text text format on_problems = or `Nothing`. It is used for more detailed error reporting. read_stream : Delimited -> Input_Stream -> Problem_Behavior -> Integer -> File | Nothing -> Any read_stream format stream on_problems max_columns=4096 related_file=Nothing = - handle_io_exception ~action = Panic.catch IOException action caught_panic-> - Error.throw (File.wrap_io_exception related_file caught_panic.payload.cause) + handle_io_exception ~action = Panic.catch_java IOException action java_exception-> + Error.throw (File.wrap_io_exception related_file java_exception) handle_io_exception <| stream.with_stream_decoder format.encoding on_problems reporting_stream_decoder-> diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Parse_Values_Helper.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Parse_Values_Helper.enso index 5da00685a8..43aef71c8d 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Parse_Values_Helper.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Internal/Parse_Values_Helper.enso @@ -11,6 +11,6 @@ translate_parsing_problem expected_datatype problem = translations = [invalid_format, leading_zeros] found = translations.find t-> Java.is_instance problem t.first - translation = found.catch _-> + translation = found.catch Any _-> Error.throw (Illegal_State_Error "Reported an unknown problem type: "+problem.to_text) translation.second problem diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso index 5df8aede0e..01cdbdd0c0 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Main.enso @@ -56,7 +56,7 @@ Json.Array.to_table fields = case self of rows = items.map item-> case item of Json.Object fs -> row = if item.get_type == Geo_Json.Feature.to_text then item.get_feature_row else fs - fields.map n-> row.get n . unwrap . catch (_ -> Nothing) + fields.map n-> row.get n . unwrap . catch Any (_ -> Nothing) _ -> Vector.fill fields.length Nothing cols = fields.map_with_index i-> n-> [n, rows.map (_.at i)] @@ -107,7 +107,7 @@ Json.Object.to_table fields=Nothing = column_names_row.keys _ -> fields rows = feature_rows.map row-> - column_names.map n-> row.get n . unwrap . catch (_ -> Nothing) + column_names.map n-> row.get n . unwrap . catch Any (_ -> Nothing) cols = column_names.map_with_index i-> n-> [n, rows.map (_.at i)] Table.new cols diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso index 910d6f48e0..e4016e404e 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso @@ -234,7 +234,7 @@ Any.should_fail_with matcher frames_to_skip=0 = Examples.throw_error . should_fail_with Examples.My_Error Error.should_fail_with : Any -> Integer -> Assertion Error.should_fail_with matcher frames_to_skip=0 = - caught = self.catch x->x + caught = self.catch if caught.is_a matcher then Nothing else loc = Meta.get_source_location 2+frames_to_skip fail ("Expected error "+matcher.to_text+", but error " + caught.to_text + " has been returned (at " + loc + ").") @@ -261,7 +261,7 @@ expect_panic_with ~action matcher = loc = Meta.get_source_location 2 return_suffix = if res.is_nothing then "" else "and returned ["+res.to_text+"]" fail ("Expected a " + matcher.to_text + " to be thrown, but the action succeeded " + return_suffix + " (at "+loc+").") - err = res.catch x->x + err = res.catch if err.is_a matcher then Nothing else fail ("Expected a " + matcher.to_text + ", but " + err.to_text + " was thrown instead.") @@ -572,7 +572,7 @@ type Suite_Config should_run_group name = regexp = self.only_group_regexp case regexp of - Text -> name.matches regexp . catch (_->True) + Text -> name.matches regexp . catch Any (_->True) _ -> True should_output_junit = @@ -678,11 +678,12 @@ run_spec : Any -> Assertion run_spec ~behavior = recovery = Panic.recover Any <| result = behavior - result.catch err-> Panic.throw (Finished_With_Error err result.get_stack_trace_text) + result.catch Any err-> + Panic.throw (Finished_With_Error err result.get_stack_trace_text) Nothing maybeExc = case recovery of _ -> Success - result = maybeExc.catch ex-> + result = maybeExc.catch Any ex-> case ex of Failure _ -> ex Finished_With_Error err stack_trace_text -> diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso index 5b5656fd9c..6afd09354d 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Helpers.enso @@ -73,7 +73,7 @@ Error.map_valid _ = self Arguments: - val: a value that will be evaluated and returned if `self` is an error. Any.catch_ : Any -> Any -Any.catch_ ~val = self.catch (_-> val) +Any.catch_ ~val = self.catch Any (_-> val) ## PRIVATE @@ -84,13 +84,13 @@ Any.catch_ ~val = self.catch (_-> val) Arguments: - val: a value that will be evaluated and returned if `self` is an error. Error.catch_ : Any -> Any -Error.catch_ ~val = self.catch (_-> val) +Error.catch_ ~val = self.catch Any (_-> val) ## PRIVATE recover_errors : Any -> Any recover_errors ~body = result = Panic.recover Any body - result.catch err-> + result.catch Any err-> Json.from_pairs [["error", err.to_display_text]] . to_text ## PRIVATE diff --git a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso index 24769bdc53..e1b5936288 100644 --- a/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso +++ b/distribution/lib/Standard/Visualization/0.0.0-dev/src/Table/Visualization.enso @@ -24,7 +24,7 @@ prepare_visualization x max_rows = Helpers.recover_errors <| case x of dataframe = x.take_start max_rows all_rows_count = x.row_count included_rows = dataframe.row_count - index = dataframe.index.catch _-> + index = dataframe.index.catch Any _-> Dataframe_Column.from_vector "" (Vector.new included_rows i->i) make_json dataframe [index] all_rows_count diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala index bca6b5e28b..1207b1ab25 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala @@ -2970,7 +2970,7 @@ class RuntimeServerTest |main = | x = Panic.catch_primitive @ .convert_to_dataflow_error | IO.println x - | IO.println (x.catch .to_text) + | IO.println (x.catch Any .to_text) |""".stripMargin.linesIterator.mkString("\n") val contents = metadata.appendToCode(code) val mainFile = context.writeMain(contents) @@ -3032,10 +3032,11 @@ class RuntimeServerTest val code = """import Standard.Base.IO |from Standard.Base.Error.Common import all + |from Standard.Base.Data.Any import all | |main = | x = Panic.catch_primitive () .convert_to_dataflow_error - | IO.println (x.catch .to_text) + | IO.println (x.catch Any .to_text) | |""".stripMargin.linesIterator.mkString("\n") val contents = metadata.appendToCode(code) @@ -3076,7 +3077,7 @@ class RuntimeServerTest Api.ExecutionResult.Diagnostic.error( "Parentheses can't be empty.", Some(mainFile), - Some(model.Range(model.Position(4, 30), model.Position(4, 32))) + Some(model.Range(model.Position(5, 30), model.Position(5, 32))) ) ) ) diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/DataflowErrorsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/DataflowErrorsTest.scala index 20f75396fd..7807f7cf0b 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/DataflowErrorsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/DataflowErrorsTest.scala @@ -196,7 +196,7 @@ class DataflowErrorsTest extends InterpreterTest { |main = | x = Panic.catch_primitive @ .convert_to_dataflow_error | IO.println x - | IO.println (x.catch .to_text) + | IO.println (x.catch Any .to_text) |""".stripMargin eval(code) consumeOut shouldEqual List( diff --git a/test/Tests/src/Data/Json_Spec.enso b/test/Tests/src/Data/Json_Spec.enso index 0588337371..debddfc0ee 100644 --- a/test/Tests/src/Data/Json_Spec.enso +++ b/test/Tests/src/Data/Json_Spec.enso @@ -8,7 +8,7 @@ type Book title author Text.should_fail_parsing_with expected = as_fail = case Json.parse self of _ -> Test.Failure "Expected a parse error, but no error reported." - result = as_fail.catch e-> case e of + result = as_fail.catch Any e-> case e of Json.Parse_Error msg -> if msg.contains expected then Test.Success else fail_msg = "The reported message " + msg.to_text + " did not contain " + expected.to_text + "." diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 2f47e1a22b..608965445b 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -17,7 +17,7 @@ spec = date . day . should_equal 1 Test.specify "should handle errors when creating local date" <| - case Date.new 2020 30 30 . catch (x -> x) of + case Date.new 2020 30 30 . catch of Time.Time_Error msg -> msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 30" result -> @@ -39,7 +39,7 @@ spec = date . day . should_equal 21 Test.specify "should throw error when parsing invalid date" <| - case Date.parse "birthday" . catch (x -> x) of + case Date.parse "birthday" . catch of Time.Time_Error msg -> msg . should_equal "Text 'birthday' could not be parsed at index 0" result -> @@ -59,7 +59,7 @@ spec = Test.specify "should throw error when parsing custom format" <| date = Date.parse "1999-01-01" "yyyy M d" - case date.catch (x -> x) of + case date.catch of Time.Time_Error msg -> msg . should_equal "Text '1999-01-01' could not be parsed at index 4" result -> @@ -100,14 +100,14 @@ spec = date . day . should_equal 1 Test.specify "should throw error when adding time-based interval" <| - case (Date.new 1970 + 1.hour) . catch (x -> x) of + case (Date.new 1970 + 1.hour) . catch of Time.Time_Error message -> message . should_equal "Date does not support time intervals" result -> Test.fail ("Unexpected result: " + result.to_text) Test.specify "should throw error when subtracting time-based interval" <| - case (Date.new 1970 - (1.day - 1.minute)) . catch (x -> x) of + case (Date.new 1970 - (1.day - 1.minute)) . catch of Time.Time_Error message -> message . should_equal "Date does not support time intervals" result -> diff --git a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso index d8fb986f77..f74eb1855b 100644 --- a/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Of_Day_Spec.enso @@ -18,7 +18,7 @@ spec = time . to_seconds . should_equal 3600 Test.specify "should handle errors when creating a time" <| - case Time_Of_Day.new 24 0 0 . catch (x -> x) of + case Time_Of_Day.new 24 0 0 . catch of Time.Time_Error msg -> msg . should_equal "Invalid value for HourOfDay (valid values 0 - 23): 24" result -> @@ -48,7 +48,7 @@ spec = time.to_seconds . should_equal 36000 Test.specify "should throw error when parsing invalid time" <| - case Time_Of_Day.parse "1200" . catch (x -> x) of + case Time_Of_Day.parse "1200" . catch of Time.Time_Error msg -> msg . should_equal "Text '1200' could not be parsed at index 2" result -> @@ -60,7 +60,7 @@ spec = Test.specify "should throw error when parsing custom format" <| time = Time_Of_Day.parse "12:30" "HH:mm:ss" - case time.catch (x -> x) of + case time.catch of Time.Time_Error msg -> msg . should_equal "Text '12:30' could not be parsed at index 5" result -> @@ -90,14 +90,14 @@ spec = time . to_seconds . should_equal 3599 Test.specify "should throw error when adding date-based interval" <| - case (Time_Of_Day.new + 1.day) . catch (x -> x) of + case (Time_Of_Day.new + 1.day) . catch of Time.Time_Error message -> message . should_equal "Time_Of_Day does not support date intervals" result -> Test.fail ("Unexpected result: " + result.to_text) Test.specify "should throw error when subtracting date-based interval" <| - case (Time_Of_Day.new - (1.day - 1.minute)) . catch (x -> x) of + case (Time_Of_Day.new - (1.day - 1.minute)) . catch of Time.Time_Error message -> message . should_equal "Time_Of_Day does not support date intervals" result -> diff --git a/test/Tests/src/Data/Time/Time_Spec.enso b/test/Tests/src/Data/Time/Time_Spec.enso index e8e91637e2..4bd829dcd6 100644 --- a/test/Tests/src/Data/Time/Time_Spec.enso +++ b/test/Tests/src/Data/Time/Time_Spec.enso @@ -20,7 +20,7 @@ spec = time . zone . zone_id . should_equal Zone.utc.zone_id Test.specify "should handle errors when creating time" <| - case Time.new 1970 0 0 . catch (x -> x) of + case Time.new 1970 0 0 . catch of Time.Time_Error msg -> msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 0" result -> @@ -108,7 +108,7 @@ spec = time . zone . zone_id . should_equal "Europe/Paris" Test.specify "should throw error when parsing invalid time" <| - case Time.parse "2008-1-1" . catch (x -> x) of + case Time.parse "2008-1-1" . catch of Time.Time_Error msg -> msg . should_equal "Text '2008-1-1' could not be parsed at index 5" result -> @@ -137,7 +137,7 @@ spec = Test.specify "should throw error when parsing custom format" <| time = Time.parse "2008-01-01" "yyyy-MM-dd'T'HH:mm:ss'['z']'" - case time.catch (x -> x) of + case time.catch of Time.Time_Error msg -> msg . should_equal "Text '2008-01-01' could not be parsed at index 10" result -> diff --git a/test/Tests/src/Data/Vector_Spec.enso b/test/Tests/src/Data/Vector_Spec.enso index 975407798b..bf7b66de96 100644 --- a/test/Tests/src/Data/Vector_Spec.enso +++ b/test/Tests/src/Data/Vector_Spec.enso @@ -297,7 +297,7 @@ spec = Test.group "Vectors" <| Test.specify "should be able to map over errors" <| fail a = Error.throw <| My_Error a - [fail 1].map (x -> x.catch (x -> x.a)) . should_equal [1] + [fail 1].map (x -> x.catch Any (x -> x.a)) . should_equal [1] [1].map fail . map .catch . should_equal [My_Error 1] Test.specify "should be able to be efficiently converted to a visualisation" <| diff --git a/test/Tests/src/Semantic/Conversion_Spec.enso b/test/Tests/src/Semantic/Conversion_Spec.enso index a8b1ac8531..eef46c8737 100644 --- a/test/Tests/src/Semantic/Conversion_Spec.enso +++ b/test/Tests/src/Semantic/Conversion_Spec.enso @@ -71,9 +71,9 @@ spec = Text.from Methods.get_bar . should_equal "'bar'" Test.specify "should fail graciously when there is no conversion" <| - Panic.recover Any (Foo.from (Quux 10)) . catch .to_display_text . should_equal "Could not find a conversion from `Quux` to `Foo`" + Panic.recover Any (Foo.from (Quux 10)) . catch Any .to_display_text . should_equal "Could not find a conversion from `Quux` to `Foo`" Test.specify "should fail graciously when the conversion target is invalid" <| - Panic.recover Any (123.from (Quux 10)) . catch .to_display_text . should_equal "123 is not a valid conversion target. Expected a type." + Panic.recover Any (123.from (Quux 10)) . catch Any .to_display_text . should_equal "123 is not a valid conversion target. Expected a type." Test.specify "should be callable with by-name arguments" <| .from that=4 self=Foo . should_equal (Foo [4, 0, 0, 0]) diff --git a/test/Tests/src/Semantic/Error_Spec.enso b/test/Tests/src/Semantic/Error_Spec.enso index c2bf70be0b..c3ad358e00 100644 --- a/test/Tests/src/Semantic/Error_Spec.enso +++ b/test/Tests/src/Semantic/Error_Spec.enso @@ -18,9 +18,9 @@ do_a_parse str = Long.parseLong str spec = Test.group "No Method Errors" <| Test.specify "should be recoverable" <| - err_1 = Panic.recover Any (123 . foobar "baz") . catch e->e - err_2 = Panic.recover Any ("foo" . baz 123) . catch e->e - err_3 = Panic.recover Any (My_Type False . nope) . catch e->e + err_1 = Panic.recover Any (123 . foobar "baz") . catch + err_2 = Panic.recover Any ("foo" . baz 123) . catch + err_3 = Panic.recover Any (My_Type False . nope) . catch err_1.target.should_equal 123 err_1.method_name.should_equal "foobar" @@ -32,6 +32,17 @@ spec = err_3.method_name.should_equal "nope" Test.group "Dataflow Errors" <| + Test.specify "should be recoverable" <| + err = Error.throw 42 + err.catch . should_equal 42 + err.should_fail_with Integer + + Test.specify "should allow recovery of only a specific error-type" <| + recover_illegal_argument ~action = + action . catch Illegal_Argument_Error err-> + "recovered error: "+err.message + (recover_illegal_argument (Error.throw (Illegal_Argument_Error "foo"))) . should_equal "recovered error: foo" + (recover_illegal_argument (Error.throw (Illegal_State_Error "bar"))) . should_fail_with Illegal_State_Error Test.specify "should be able to be shown in the default visualization" <| json = (Error.throw <| My_Type "aaa").to_default_visualization_data @@ -153,7 +164,7 @@ spec = Test.expect_panic_with (parse 0.0) Unsupported_Argument_Types Test.specify "should allow to throw raw Java exceptions" <| - exception = Panic.catch NumberFormatException (throw_raw_java "foo") (p -> p.payload.cause) + exception = Panic.catch_java NumberFormatException (throw_raw_java "foo") (p -> p) exception.getMessage . should_equal "foo" Panic.get_attached_stack_trace exception . second . name . should_equal "Error_Spec.throw_raw_java" diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso index 5581271f4f..c727b3f7a2 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Error/Common.enso @@ -1,3 +1,5 @@ +from project.Data.Any import Any + type Panic @Builtin_Type type Panic @@ -24,4 +26,6 @@ type Error type Error throw payload = @Builtin_Method "Error.throw" catch_primitive handler = @Builtin_Method "Error.catch_primitive" - catch (handler = x->x) = self.catch_primitive handler + catch (error_type = Any) (handler = x->x) = + _ = error_type + self.catch_primitive handler diff --git a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso index d377423e59..a248655dfe 100644 --- a/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso +++ b/test/micro-distribution/lib/Standard/Base/0.0.0-dev/src/Main.enso @@ -1,6 +1,9 @@ import project.IO export project.IO +import project.Data.Any +from project.Data.Any export Any + import project.Data.List from project.Data.List export Nil, Cons, List From 735053c2183967d0455fa45c7e2c2bae00b8dfe7 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 11 Jul 2022 12:30:44 +0200 Subject: [PATCH 2/2] Implementing basic functions (#3554) The language specification suggests to add [five basic functions into the standard library](https://github.com/enso-org/design/blob/wip/wd/enso-spec/epics/enso-spec-1.0/05.%20Functions.md#useful-functions-in-the-standard-library). `identity`, `flip`, `const`, `curry` & `uncurry`. # Important Notes The new functions are being added into existing `Function.enso` file. That may not be the best place, but it is not clear from the [design spec](https://github.com/enso-org/design/blob/wip/wd/enso-spec/epics/enso-spec-1.0/05.%20Functions.md#useful-functions-in-the-standard-library) how they are supposed to be imported. I can move them wherever needed. There is a documentation provided for each of the functions, but I am not sure how to verify it is correct. Do we generate the documentation for stdlib somehow? --- CHANGELOG.md | 2 + .../Standard/Base/0.0.0-dev/src/Function.enso | 55 +++++++++++++++++++ .../compiler/context/SuggestionBuilder.scala | 2 + test/Tests/src/Data/Function_Spec.enso | 43 +++++++++++++++ test/Tests/src/Main.enso | 2 + 5 files changed, 104 insertions(+) create mode 100644 test/Tests/src/Data/Function_Spec.enso diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bf432e4ee..b78eef3ce5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,7 @@ - [Added `File_Format.Delimited` support to `Table.write` for new files.][3528] - [Adjusted `Database.connect` API to new design.][3542] - [Added `File_Format.Excel` support to `Table.write` for new files.][3551] +- [identity,const,flip,curry,uncurry functions][3554] - [Added append support for `File_Format.Excel`.][3558] - [Added support for custom encodings in `File_Format.Delimited` writing.][3564] - [Allow filtering caught error type in `Error.catch`.][3574] @@ -236,6 +237,7 @@ [3542]: https://github.com/enso-org/enso/pull/3542 [3551]: https://github.com/enso-org/enso/pull/3551 [3552]: https://github.com/enso-org/enso/pull/3552 +[3554]: https://github.com/enso-org/enso/pull/3554 [3558]: https://github.com/enso-org/enso/pull/3558 [3564]: https://github.com/enso-org/enso/pull/3564 [3574]: https://github.com/enso-org/enso/pull/3574 diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso index 4a478a005f..d20c5469f1 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso @@ -1,3 +1,5 @@ +import Standard.Base.Data.Vector + # Function types. type Function @@ -7,3 +9,56 @@ type Function the this argument. @Builtin_Type type Function + + +## An identity function which returns the provided argument. + + Arguments: + - x: the value to return. + + > Example + five = Function.identity 5 # returns number 5 + +identity : a -> a +identity x = x + +## Flips the first two arguments of a function. Returns function that + takes two arguments, but in opposite order. + + Arguments: + - f function that takes two arguments + + > Example + IO.println <| Function.flip (+) "world" "hello" # Prints 'helloworld' + +flip : (a -> b -> c) -> (b -> a -> c) +flip f = (x -> y -> f y x) + + +## Creates a function which drops its input and returns the provided value instead. + The expression const a is the same as \_ -> a. + + Arguments: + - x constant value to return + + > Example + IO.println <| [1, 2, 3].map (Function.const 7) # Prints '[7, 7, 7]' + +const : a -> b -> a +const x _ = x + +## Converts a single-argument function accepting a pair of elements into a multi-argument one. + + Arguments: + - f function accepting pair of values + +curry : ([a, b] -> c) -> (a -> b -> c) +curry f = x -> y -> f [x, y] + +## Converts a multi-argument function into a single-argument one accepting a pair of elements. + + Arguments: + - f function accepting multiple arguments + +uncurry : (a -> b -> c) -> ([a, b] -> c) +uncurry f = (pair -> f pair.head pair.second) diff --git a/engine/runtime/src/main/scala/org/enso/compiler/context/SuggestionBuilder.scala b/engine/runtime/src/main/scala/org/enso/compiler/context/SuggestionBuilder.scala index a69e20ef83..b5d4c7c966 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/context/SuggestionBuilder.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/context/SuggestionBuilder.scala @@ -403,6 +403,8 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) { val tdef = resolveTypeName(bindings, typeName) .getOrElse(TypeArg.Value(QualifiedName.simpleName(typeName))) args :+ tdef + case seq: IR.Application.Literal.Sequence => + seq.items.foldLeft(args)((a, t) => go(t, a)) case _ => args } diff --git a/test/Tests/src/Data/Function_Spec.enso b/test/Tests/src/Data/Function_Spec.enso new file mode 100644 index 0000000000..bb05f43d47 --- /dev/null +++ b/test/Tests/src/Data/Function_Spec.enso @@ -0,0 +1,43 @@ +from Standard.Base import all +import Standard.Base.Data.Numbers + +import Standard.Test +from Standard.Base.Data.Ordering import Equal, Less, Greater + +spec = + Test.group "identity" <| + Test.specify "identity on number" <| + (identity 5) . should_equal 5 + + Test.specify "identity on text" <| + (identity '5') . should_equal '5' + + Test.specify "identity on boolean" <| + (identity False) . should_equal False + + Test.group "flip" <| + Test.specify "flip on number" <| + (flip (-) 2 5) . should_equal 3 + + Test.specify "flip on text" <| + (flip (+) "world" "hello") . should_equal "helloworld" + + Test.group "const" <| + Test.specify "const on number" <| + two = const 2 + two 5 . should_equal 2 + + Test.group "curry" <| + Test.specify "curry on number list" <| + sum = x -> x.fold 0 (+) + sum [1, 2, 3, 4] . should_equal 10 + plus = curry sum + plus 6 3 . should_equal 9 + + Test.group "uncurry" <| + Test.specify "uncurry on number list" <| + times = uncurry (*) + times [6, 7] . should_equal 42 + + +main = Test.Suite.run_main spec diff --git a/test/Tests/src/Main.enso b/test/Tests/src/Main.enso index 69d4d1606b..2804553e95 100644 --- a/test/Tests/src/Main.enso +++ b/test/Tests/src/Main.enso @@ -20,6 +20,7 @@ import project.Semantic.R_Interop_Spec import project.Data.Array_Spec import project.Data.Bool_Spec +import project.Data.Function_Spec import project.Data.Interval_Spec import project.Data.Json_Spec import project.Data.List_Spec @@ -66,6 +67,7 @@ main = Test.Suite.run_main <| Any_Spec.spec Array_Spec.spec Bool_Spec.spec + Function_Spec.spec Case_Spec.spec Conversion_Spec.spec Deep_Export_Spec.spec