Merge branch 'develop' into wip/jtulach/PolyglotDateSupport_181755990

This commit is contained in:
Jaroslav Tulach 2022-07-11 12:47:17 +02:00 committed by GitHub
commit 61e44800d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 298 additions and 136 deletions

View File

@ -147,8 +147,10 @@
- [Added `File_Format.Delimited` support to `Table.write` for new files.][3528] - [Added `File_Format.Delimited` support to `Table.write` for new files.][3528]
- [Adjusted `Database.connect` API to new design.][3542] - [Adjusted `Database.connect` API to new design.][3542]
- [Added `File_Format.Excel` support to `Table.write` for new files.][3551] - [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 append support for `File_Format.Excel`.][3558]
- [Added support for custom encodings in `File_Format.Delimited` writing.][3564] - [Added support for custom encodings in `File_Format.Delimited` writing.][3564]
- [Allow filtering caught error type in `Error.catch`.][3574]
[debug-shortcuts]: [debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
@ -235,8 +237,10 @@
[3542]: https://github.com/enso-org/enso/pull/3542 [3542]: https://github.com/enso-org/enso/pull/3542
[3551]: https://github.com/enso-org/enso/pull/3551 [3551]: https://github.com/enso-org/enso/pull/3551
[3552]: https://github.com/enso-org/enso/pull/3552 [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 [3558]: https://github.com/enso-org/enso/pull/3558
[3564]: https://github.com/enso-org/enso/pull/3564 [3564]: https://github.com/enso-org/enso/pull/3564
[3574]: https://github.com/enso-org/enso/pull/3574
#### Enso Compiler #### Enso Compiler

View File

@ -1,5 +1,7 @@
from Standard.Base import all from Standard.Base import all
from Standard.Base.Error.Common import dataflow_error_handler
# The type that subsumes all types. # The type that subsumes all types.
type Any type Any
@ -18,7 +20,7 @@ type Any
Arguments: Arguments:
- handler: The function to call on this if it is an error value. - handler: The function to call on this if it is an error value.
catch_primitive : (Error -> Any) -> Any 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 ## Generic conversion of an arbitrary Enso value to a corresponding textual
representation. representation.
@ -246,23 +248,37 @@ type Any
if_nothing : Any -> Any if_nothing : Any -> Any
if_nothing ~_ = self if_nothing ~_ = self
## Executes the provided handler on an error, or returns a non-error value ## Executes the provided handler on an error, or returns the value unchanged.
unchanged.
Arguments: Arguments:
- handler: The function to call on this if it is an error value. By default - error_type: The type of error to handle. Defaults to `Any` to handle
this is identity. 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 > 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 from Standard.Base import all
example_catch = example_catch =
error = Error.throw "My message" error = Error.throw (Illegal_Argument_Error "My message")
error.catch (err -> err.length) error.catch Illegal_Argument_Error (err -> err.message)
catch : (Error -> Any) -> Any
catch (handler = x->x) = self.catch_primitive handler > 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. ## Transforms an error.

View File

@ -38,8 +38,8 @@ from_pairs contents =
example_parse = Json.parse '{ "a": 1 }' example_parse = Json.parse '{ "a": 1 }'
parse : Text -> Json ! Parse_Error parse : Text -> Json ! Parse_Error
parse json_text = parse json_text =
Panic.catch Polyglot_Error (Internal.parse_helper json_text) caught_panic-> Panic.catch_java Any (Internal.parse_helper json_text) java_exception->
Error.throw (Parse_Error caught_panic.payload.cause.getMessage) Error.throw (Parse_Error java_exception.getMessage)
## Represents a JSON structure. ## Represents a JSON structure.
type Json type Json

View File

@ -308,7 +308,7 @@ into_helper fmt json = case fmt of
fnames = cons.fields fnames = cons.fields
ffmts = m.fields ffmts = m.fields
field_values = fnames.zip ffmts n-> inner_fmt-> 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) Panic.throw (Missing_Field_Error json fmt n)
into_helper inner_fmt fjson into_helper inner_fmt fjson
cons.new field_values cons.new field_values

View File

@ -217,7 +217,7 @@ type Map
example_get_or_else = Examples.map.get_or_else 2 "zero" example_get_or_else = Examples.map.get_or_else 2 "zero"
get_or_else : Any -> Any -> Any get_or_else : Any -> Any -> Any
get_or_else key ~other = 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. ## Transforms the map's keys and values to create a new map.

View File

@ -56,9 +56,8 @@ now = Time ZonedDateTime.now
example_new = Time.new 1986 8 5 example_new = Time.new 1986 8 5
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Time ! Time_Error 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) = 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 Panic.catch_java Any (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) java_exception->
Polyglot_Error err -> Error.throw (Time_Error err.getMessage) Error.throw (Time_Error java_exception.getMessage)
x -> x
## ALIAS Time from Text ## 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 import Standard.Base.Data.Time
example_parse = Time.parse "2020-10-01" . catch e-> case e of example_parse = Time.parse "2020-10-01" . catch Time_Error (_->Time.now)
Time.Error _ -> Time.now
> Example > Example
Parse "2020-05-06 04:30:20" as Time Parse "2020-05-06 04:30:20" as Time

View File

@ -60,7 +60,14 @@ today = now
new : Integer -> Integer -> Integer -> Date ! Time.Time_Error new : Integer -> Integer -> Integer -> Date ! Time.Time_Error
new year (month = 1) (day = 1) = 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) Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x x -> x
@ -107,8 +114,8 @@ new year (month = 1) (day = 1) =
import Standard.Base.Data.Time.Date import Standard.Base.Data.Time.Date
import Standard.Base.Data.Time import Standard.Base.Data.Time
example_parse_err = Date.parse "my birthday" . catch e-> case e of example_parse_err = Date.parse "my birthday" . catch Time.Time_Error _->
Time.Time_Error _ -> Date.new 2000 1 1 Date.new 2000 1 1
> Example > Example
Parse "1999-1-1" as Date using a custom format. Parse "1999-1-1" as Date using a custom format.
@ -125,8 +132,7 @@ new year (month = 1) (day = 1) =
example_parse_err = example_parse_err =
date = Date.parse "1999-1-1" "yyyy-MM-dd" date = Date.parse "1999-1-1" "yyyy-MM-dd"
date.catch e-> case e of date.catch Time.Time_Error (_->Date.new 2000 1 1)
Time.Time_Error _ -> Date.new 2000 1 1
parse : Text -> (Text | Nothing) -> Date ! Time.Time_Error parse : Text -> (Text | Nothing) -> Date ! Time.Time_Error
parse text pattern=Nothing = parse text pattern=Nothing =
result = Panic.recover Any <| case pattern of result = Panic.recover Any <| case pattern of

View File

@ -48,9 +48,8 @@ now = Time_Of_Day LocalTime.now
example_epoch = Time_Of_Day.new hour=9 minute=30 example_epoch = Time_Of_Day.new hour=9 minute=30
new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day ! Time.Time_Error new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day ! Time.Time_Error
new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) = 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 Panic.catch_java Any (Time_Of_Day (LocalTime.of hour minute second nanosecond)) java_exception->
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage) Error.throw (Time.Time_Error java_exception.getMessage)
x -> x
## Obtains an instance of `Time_Of_Day` from a text such as "10:15". ## 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
import Standard.Base.Data.Time.Time_Of_Day import Standard.Base.Data.Time.Time_Of_Day
example_parse = Time_Of_Day.parse "half twelve" . catch e-> case e of example_parse = Time_Of_Day.parse "half twelve" . catch Time.Time_Error _->
Time.Time_Error _ -> Time_Of_Day.new Time_Of_Day.new
> Example > Example
Parse "04:30:20" as Time_Of_Day. Parse "04:30:20" as Time_Of_Day.

View File

@ -61,24 +61,39 @@ type Error
Returns a human-readable text representing this error. Returns a human-readable text representing this error.
to_display_text : Text 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 ## Executes the provided handler on an error, or returns the value unchanged.
value unchanged.
Arguments: Arguments:
- handler: The function to call on this if it is an error value. By default - error_type: The type of error to handle. Defaults to `Any` to handle
this is identity. 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 > 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 = example_catch =
Examples.throw_error.catch (err -> err.message.length) error = Error.throw (Illegal_Argument_Error "My message")
catch : (Error -> Any) -> Any error.catch Illegal_Argument_Error (err -> err.message)
catch (handler = x->x) = self.catch_primitive handler
> 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 ## UNSTABLE
@ -91,7 +106,7 @@ type Error
example_display = Examples.throw_error.to_default_visualization_data example_display = Examples.throw_error.to_default_visualization_data
to_default_visualization_data : Text 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 ## UNSTABLE
@ -106,8 +121,9 @@ type Error
to_json : Json.Object to_json : Json.Object
to_json = to_json =
error_type = ["type", "Error"] error_type = ["type", "Error"]
error_content = ["content", self.catch .to_json] caught = self.catch
error_message = ["message", self.catch .to_display_text] error_content = ["content", caught.to_json]
error_message = ["message", caught.to_display_text]
Json.from_pairs [error_type, error_content, error_message] Json.from_pairs [error_type, error_content, error_message]
## Transforms an error. ## Transforms an error.
@ -127,7 +143,7 @@ type Error
map = Examples.map map = Examples.map
map.get 10 . map_error (_ -> "The element 10 was not found.") map.get 10 . map_error (_ -> "The element 10 was not found.")
map_error : (Error -> Error) -> Any 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 ## ADVANCED
UNSTABLE UNSTABLE
@ -297,7 +313,7 @@ type Panic
example_rethrow = Panic.rethrow Examples.throw_error example_rethrow = Panic.rethrow Examples.throw_error
rethrow : (Any ! Any) -> Any 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 ## Executes the provided action and if a panic matching the provided type was
thrown, calls the provided callback. thrown, calls the provided callback.
@ -351,6 +367,36 @@ type Panic
False -> Panic.throw caught_panic False -> Panic.throw caught_panic
_ -> 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 ## Executes the provided action and converts a possible panic matching any of
the provided types into a dataflow Error. the provided types into a dataflow Error.

View File

@ -1,3 +1,5 @@
import Standard.Base.Data.Vector
# Function types. # Function types.
type Function type Function
@ -7,3 +9,56 @@ type Function
the this argument. the this argument.
@Builtin_Type @Builtin_Type
type Function 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)

View File

@ -293,7 +293,7 @@ Base.Error.is_a typ = self.is_an typ
Arguments: Arguments:
- typ: The type to check `self` against. - typ: The type to check `self` against.
Base.Error.is_an : Any -> Boolean 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 ## UNSTABLE
ADVANCED ADVANCED

View File

@ -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.Request.Body as Request_Body
import Standard.Base.Network.Http.Response import Standard.Base.Network.Http.Response
import Standard.Base.Network.Http.Version import Standard.Base.Network.Http.Version
import Standard.Base.Network.Internal
import Standard.Base.Network.Proxy import Standard.Base.Network.Proxy
import Standard.Base.Network.Uri import Standard.Base.Network.Uri
import Standard.Base.System.File import Standard.Base.System.File
@ -597,11 +596,13 @@ type Http
http.request req http.request req
request : Request -> Response ! Request_Error request : Request -> Response ! Request_Error
request req = 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 body_publishers = HttpRequest.BodyPublishers
builder = HttpRequest.newBuilder builder = HttpRequest.newBuilder
# set uri # set uri
builder.uri (Internal.panic_on_error req.uri.internal_uri) builder.uri (Panic.rethrow req.uri.internal_uri)
# prepare headers and body # prepare headers and body
req_with_body = case req.body of req_with_body = case req.body of
Request_Body.Empty -> Request_Body.Empty ->
@ -651,11 +652,6 @@ type Http
http_request = builder.build http_request = builder.build
body_handler = HttpResponse.BodyHandlers . ofByteArray body_handler = HttpResponse.BodyHandlers . ofByteArray
Response.Response (self.internal_http_client.send http_request body_handler) 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 ## PRIVATE

View File

@ -4,7 +4,6 @@ import Standard.Base.Network.Http.Form
import Standard.Base.Network.Http.Header import Standard.Base.Network.Http.Header
import Standard.Base.Network.Http.Method import Standard.Base.Network.Http.Method
import Standard.Base.Network.Http.Request.Body as Request_Body import Standard.Base.Network.Http.Request.Body as Request_Body
import Standard.Base.Network.Internal
import Standard.Base.Network.Uri import Standard.Base.Network.Uri
import Standard.Base.System.File 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") example_new = Request.new Method.Post (Uri.parse "http://example.com")
new : Method -> (Text | Uri) -> Vector.Vector -> Request_Body -> Request new : Method -> (Text | Uri) -> Vector.Vector -> Request_Body -> Request
new method addr (headers = []) (body = Request_Body.Empty) = 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. ## Create an Options request.

View File

@ -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

View File

@ -22,9 +22,8 @@ polyglot java import java.util.Optional
example_parse = Uri.parse "http://example.com" example_parse = Uri.parse "http://example.com"
parse : Text -> Uri ! Syntax_Error parse : Text -> Uri ! Syntax_Error
parse text = parse text =
Panic.recover Any (Uri (Java_URI.create text)) . catch e-> case e of Panic.catch_java Any (Uri (Java_URI.create text)) java_exception->
Polyglot_Error ex -> Error.throw (Syntax_Error ex.getMessage) Error.throw (Syntax_Error java_exception.getMessage)
other -> Panic.throw other
## Convert Text to a Uri. ## Convert Text to a Uri.

View File

@ -57,7 +57,7 @@ type Existing_File_Behavior
temporary file and creates a backup. temporary file and creates a backup.
handle_file_already_exists <| handle_internal_dataflow <| handle_file_already_exists <| handle_internal_dataflow <|
Panic.rethrow <| file.with_output_stream [Option.Write, Option.Create_New] output_stream-> 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) Panic.throw (Internal_Write_Operation_Errored dataflow_error)
## PRIVATE ## 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-> new_file.with_output_stream [Option.Write, Option.Create_New] output_stream->
result = Panic.catch Any (action output_stream) caught_panic-> result = Panic.catch Any (action output_stream) caught_panic->
Panic.throw (Internal_Write_Operation_Panicked 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) Panic.throw (Internal_Write_Operation_Errored dataflow_error)
## We ignore the file not found error, because it means that there ## 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 is no file to back-up. This may also be caused by someone

View File

@ -156,7 +156,7 @@ map_attached_warnings mapper value =
map_warnings_and_errors : (Any -> Maybe Any) -> Any -> Any map_warnings_and_errors : (Any -> Maybe Any) -> Any -> Any
map_warnings_and_errors mapper value = map_warnings_and_errors mapper value =
mapped_warnings_or_error = map_attached_warnings_helper mapper value 1 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 case mapper error of
Maybe.Some new_error -> Error.throw new_error Maybe.Some new_error -> Error.throw new_error
## If the mapper did not want to affect the error, we return the ## If the mapper did not want to affect the error, we return the

View File

@ -38,10 +38,8 @@ read location flags=[] =
if flags.is_empty then Java_Codecs.READ_FLAG_EMPTY else if flags.is_empty then Java_Codecs.READ_FLAG_EMPTY else
flags.map .to_integer . reduce (_.bit_or _) flags.map .to_integer . reduce (_.bit_or _)
_ -> flags.to_integer _ -> flags.to_integer
Panic.recover Any (Image.Image (Java_Codecs.read path read_flags)) . catch e-> Panic.catch_java Any (Image.Image (Java_Codecs.read path read_flags)) java_exception->
case e of Error.throw (File.Io_Error (File.new path) 'Failed to read the file')
Polyglot_Error _ -> Error.throw (File.Io_Error (File.new path) 'Failed to read the file')
err -> Error.throw err
## UNSTABLE ## UNSTABLE
@ -71,10 +69,8 @@ Image.Image.write location flags=[] =
Vector.Vector _ -> flags Vector.Vector _ -> flags
_ -> [flags] _ -> [flags]
int_flags = Internal.mat_of_int (write_flags.flat_map x-> [x.to_integer, x.value]) 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-> Panic.catch_java Any (Java_Codecs.write path self.opencv_mat int_flags) java_exception->
case e of Error.throw (File.Io_Error (File.new path) 'Failed to write to the file')
Polyglot_Error _ -> Error.throw (File.Io_Error (File.new path) 'Failed to write to the file')
err -> Error.throw err
## UNSTABLE ## UNSTABLE
type Read_Flag type Read_Flag

View File

@ -153,7 +153,7 @@ type Image
image = Examples.image image = Examples.image
image + (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels) image + (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels)
+ : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal + : (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 ## UNSTABLE
@ -204,7 +204,7 @@ type Image
image = Examples.image image = Examples.image
image - (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels) image - (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels)
- : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal - : (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 ## UNSTABLE
@ -255,7 +255,7 @@ type Image
image = Examples.image image = Examples.image
image * (Matrix.ones rows=image.rows columns=image.columns channels=image.channels) image * (Matrix.ones rows=image.rows columns=image.columns channels=image.channels)
* : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal * : (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 ## UNSTABLE
@ -306,7 +306,7 @@ type Image
image = Examples.image image = Examples.image
image / (Matrix.ones rows=image.rows columns=image.columns channels=image.channels) image / (Matrix.ones rows=image.rows columns=image.columns channels=image.channels)
/ : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal / : (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 ## UNSTABLE

View File

@ -1061,7 +1061,7 @@ type Column
example_first = Examples.integer_column.first example_first = Examples.integer_column.first
first : Any ! Empty_Error 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 ## UNSTABLE
@ -1093,7 +1093,7 @@ type Column
example_last = Examples.integer_column.last example_last = Examples.integer_column.last
last : Any ! Empty_Error 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 ## UNSTABLE

View File

@ -198,7 +198,7 @@ type Table
example_to_json = Examples.inventory_table.to_json example_to_json = Examples.inventory_table.to_json
to_json : Json to_json : Json
to_json = to_json =
index_prep = case self.index.catch (_->Nothing) of index_prep = case self.index.catch No_Index_Set_Error (_->Nothing) of
Nothing -> [] Nothing -> []
index -> [index] index -> [index]
cols = index_prep + self.columns cols = index_prep + self.columns

View File

@ -167,9 +167,7 @@ resolve_aggregate table problem_builder aggregate_column =
## Downgrade the `Internal_Missing_Column_Error` error into a `Nothing` ## Downgrade the `Internal_Missing_Column_Error` error into a `Nothing`
value, keeping any other dataflow errors intact. value, keeping any other dataflow errors intact.
result.catch err-> case err of result.catch Internal_Missing_Column_Error (_->Nothing)
Internal_Missing_Column_Error -> Nothing
_ -> result
## PRIVATE ## PRIVATE
A marker for missing columns during resolution. A marker for missing columns during resolution.

View File

@ -68,8 +68,8 @@ read_text text format on_problems =
or `Nothing`. It is used for more detailed error reporting. or `Nothing`. It is used for more detailed error reporting.
read_stream : Delimited -> Input_Stream -> Problem_Behavior -> Integer -> File | Nothing -> Any read_stream : Delimited -> Input_Stream -> Problem_Behavior -> Integer -> File | Nothing -> Any
read_stream format stream on_problems max_columns=4096 related_file=Nothing = read_stream format stream on_problems max_columns=4096 related_file=Nothing =
handle_io_exception ~action = Panic.catch IOException action caught_panic-> handle_io_exception ~action = Panic.catch_java IOException action java_exception->
Error.throw (File.wrap_io_exception related_file caught_panic.payload.cause) Error.throw (File.wrap_io_exception related_file java_exception)
handle_io_exception <| handle_io_exception <|
stream.with_stream_decoder format.encoding on_problems reporting_stream_decoder-> stream.with_stream_decoder format.encoding on_problems reporting_stream_decoder->

View File

@ -11,6 +11,6 @@ translate_parsing_problem expected_datatype problem =
translations = [invalid_format, leading_zeros] translations = [invalid_format, leading_zeros]
found = translations.find t-> Java.is_instance problem t.first 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) Error.throw (Illegal_State_Error "Reported an unknown problem type: "+problem.to_text)
translation.second problem translation.second problem

View File

@ -56,7 +56,7 @@ Json.Array.to_table fields = case self of
rows = items.map item-> case item of rows = items.map item-> case item of
Json.Object fs -> Json.Object fs ->
row = if item.get_type == Geo_Json.Feature.to_text then item.get_feature_row else 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 _ -> Vector.fill fields.length Nothing
cols = fields.map_with_index i-> n-> cols = fields.map_with_index i-> n->
[n, rows.map (_.at i)] [n, rows.map (_.at i)]
@ -107,7 +107,7 @@ Json.Object.to_table fields=Nothing =
column_names_row.keys column_names_row.keys
_ -> fields _ -> fields
rows = feature_rows.map row-> 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-> cols = column_names.map_with_index i-> n->
[n, rows.map (_.at i)] [n, rows.map (_.at i)]
Table.new cols Table.new cols

View File

@ -234,7 +234,7 @@ Any.should_fail_with matcher frames_to_skip=0 =
Examples.throw_error . should_fail_with Examples.My_Error Examples.throw_error . should_fail_with Examples.My_Error
Error.should_fail_with : Any -> Integer -> Assertion Error.should_fail_with : Any -> Integer -> Assertion
Error.should_fail_with matcher frames_to_skip=0 = 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 if caught.is_a matcher then Nothing else
loc = Meta.get_source_location 2+frames_to_skip 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 + ").") 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 loc = Meta.get_source_location 2
return_suffix = if res.is_nothing then "" else "and returned ["+res.to_text+"]" 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+").") 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 if err.is_a matcher then Nothing else
fail ("Expected a " + matcher.to_text + ", but " + err.to_text + " was thrown instead.") fail ("Expected a " + matcher.to_text + ", but " + err.to_text + " was thrown instead.")
@ -572,7 +572,7 @@ type Suite_Config
should_run_group name = should_run_group name =
regexp = self.only_group_regexp regexp = self.only_group_regexp
case regexp of case regexp of
Text -> name.matches regexp . catch (_->True) Text -> name.matches regexp . catch Any (_->True)
_ -> True _ -> True
should_output_junit = should_output_junit =
@ -678,11 +678,12 @@ run_spec : Any -> Assertion
run_spec ~behavior = run_spec ~behavior =
recovery = Panic.recover Any <| recovery = Panic.recover Any <|
result = behavior 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 Nothing
maybeExc = case recovery of maybeExc = case recovery of
_ -> Success _ -> Success
result = maybeExc.catch ex-> result = maybeExc.catch Any ex->
case ex of case ex of
Failure _ -> ex Failure _ -> ex
Finished_With_Error err stack_trace_text -> Finished_With_Error err stack_trace_text ->

View File

@ -73,7 +73,7 @@ Error.map_valid _ = self
Arguments: Arguments:
- val: a value that will be evaluated and returned if `self` is an error. - val: a value that will be evaluated and returned if `self` is an error.
Any.catch_ : Any -> Any Any.catch_ : Any -> Any
Any.catch_ ~val = self.catch (_-> val) Any.catch_ ~val = self.catch Any (_-> val)
## PRIVATE ## PRIVATE
@ -84,13 +84,13 @@ Any.catch_ ~val = self.catch (_-> val)
Arguments: Arguments:
- val: a value that will be evaluated and returned if `self` is an error. - val: a value that will be evaluated and returned if `self` is an error.
Error.catch_ : Any -> Any Error.catch_ : Any -> Any
Error.catch_ ~val = self.catch (_-> val) Error.catch_ ~val = self.catch Any (_-> val)
## PRIVATE ## PRIVATE
recover_errors : Any -> Any recover_errors : Any -> Any
recover_errors ~body = recover_errors ~body =
result = Panic.recover Any body result = Panic.recover Any body
result.catch err-> result.catch Any err->
Json.from_pairs [["error", err.to_display_text]] . to_text Json.from_pairs [["error", err.to_display_text]] . to_text
## PRIVATE ## PRIVATE

View File

@ -24,7 +24,7 @@ prepare_visualization x max_rows = Helpers.recover_errors <| case x of
dataframe = x.take_start max_rows dataframe = x.take_start max_rows
all_rows_count = x.row_count all_rows_count = x.row_count
included_rows = dataframe.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) Dataframe_Column.from_vector "" (Vector.new included_rows i->i)
make_json dataframe [index] all_rows_count make_json dataframe [index] all_rows_count

View File

@ -2970,7 +2970,7 @@ class RuntimeServerTest
|main = |main =
| x = Panic.catch_primitive @ .convert_to_dataflow_error | x = Panic.catch_primitive @ .convert_to_dataflow_error
| IO.println x | IO.println x
| IO.println (x.catch .to_text) | IO.println (x.catch Any .to_text)
|""".stripMargin.linesIterator.mkString("\n") |""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code) val contents = metadata.appendToCode(code)
val mainFile = context.writeMain(contents) val mainFile = context.writeMain(contents)
@ -3032,10 +3032,11 @@ class RuntimeServerTest
val code = val code =
"""import Standard.Base.IO """import Standard.Base.IO
|from Standard.Base.Error.Common import all |from Standard.Base.Error.Common import all
|from Standard.Base.Data.Any import all
| |
|main = |main =
| x = Panic.catch_primitive () .convert_to_dataflow_error | 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") |""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code) val contents = metadata.appendToCode(code)
@ -3076,7 +3077,7 @@ class RuntimeServerTest
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
"Parentheses can't be empty.", "Parentheses can't be empty.",
Some(mainFile), Some(mainFile),
Some(model.Range(model.Position(4, 30), model.Position(4, 32))) Some(model.Range(model.Position(5, 30), model.Position(5, 32)))
) )
) )
) )

View File

@ -403,6 +403,8 @@ final class SuggestionBuilder[A: IndexedSource](val source: A) {
val tdef = resolveTypeName(bindings, typeName) val tdef = resolveTypeName(bindings, typeName)
.getOrElse(TypeArg.Value(QualifiedName.simpleName(typeName))) .getOrElse(TypeArg.Value(QualifiedName.simpleName(typeName)))
args :+ tdef args :+ tdef
case seq: IR.Application.Literal.Sequence =>
seq.items.foldLeft(args)((a, t) => go(t, a))
case _ => case _ =>
args args
} }

View File

@ -196,7 +196,7 @@ class DataflowErrorsTest extends InterpreterTest {
|main = |main =
| x = Panic.catch_primitive @ .convert_to_dataflow_error | x = Panic.catch_primitive @ .convert_to_dataflow_error
| IO.println x | IO.println x
| IO.println (x.catch .to_text) | IO.println (x.catch Any .to_text)
|""".stripMargin |""".stripMargin
eval(code) eval(code)
consumeOut shouldEqual List( consumeOut shouldEqual List(

View File

@ -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

View File

@ -8,7 +8,7 @@ type Book title author
Text.should_fail_parsing_with expected = Text.should_fail_parsing_with expected =
as_fail = case Json.parse self of as_fail = case Json.parse self of
_ -> Test.Failure "Expected a parse error, but no error reported." _ -> 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 -> Json.Parse_Error msg ->
if msg.contains expected then Test.Success else if msg.contains expected then Test.Success else
fail_msg = "The reported message " + msg.to_text + " did not contain " + expected.to_text + "." fail_msg = "The reported message " + msg.to_text + " did not contain " + expected.to_text + "."

View File

@ -25,7 +25,7 @@ specWith name createNewDate parseDate =
date . day . should_equal 1 date . day . should_equal 1
Test.specify "should handle errors when creating local date" <| Test.specify "should handle errors when creating local date" <|
case createNewDate 2020 30 30 . catch (x -> x) of case createNewDate 2020 30 30 . catch of
Time.Time_Error msg -> Time.Time_Error msg ->
msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 30" msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 30"
result -> result ->
@ -47,7 +47,7 @@ specWith name createNewDate parseDate =
date . day . should_equal 21 date . day . should_equal 21
Test.specify "should throw error when parsing invalid date" <| Test.specify "should throw error when parsing invalid date" <|
case parseDate "birthday" . catch (x -> x) of case parseDate "birthday" . catch of
Time.Time_Error msg -> Time.Time_Error msg ->
msg . should_equal "Text 'birthday' could not be parsed at index 0" msg . should_equal "Text 'birthday' could not be parsed at index 0"
result -> result ->
@ -67,7 +67,7 @@ specWith name createNewDate parseDate =
Test.specify "should throw error when parsing custom format" <| Test.specify "should throw error when parsing custom format" <|
date = parseDate "1999-01-01" "yyyy M d" date = parseDate "1999-01-01" "yyyy M d"
case date.catch (x -> x) of case date.catch of
Time.Time_Error msg -> Time.Time_Error msg ->
msg . should_equal "Text '1999-01-01' could not be parsed at index 4" msg . should_equal "Text '1999-01-01' could not be parsed at index 4"
result -> result ->
@ -108,14 +108,14 @@ specWith name createNewDate parseDate =
date . day . should_equal 1 date . day . should_equal 1
Test.specify "should throw error when adding time-based interval" <| Test.specify "should throw error when adding time-based interval" <|
case (createNewDate 1970 + 1.hour) . catch (x -> x) of case (createNewDate 1970 + 1.hour) . catch of
Time.Time_Error message -> Time.Time_Error message ->
message . should_equal "Date does not support time intervals" message . should_equal "Date does not support time intervals"
result -> result ->
Test.fail ("Unexpected result: " + result.to_text) Test.fail ("Unexpected result: " + result.to_text)
Test.specify "should throw error when subtracting time-based interval" <| Test.specify "should throw error when subtracting time-based interval" <|
case (createNewDate 1970 - (1.day - 1.minute)) . catch (x -> x) of case (createNewDate 1970 - (1.day - 1.minute)) . catch of
Time.Time_Error message -> Time.Time_Error message ->
message . should_equal "Date does not support time intervals" message . should_equal "Date does not support time intervals"
result -> result ->

View File

@ -18,7 +18,7 @@ spec =
time . to_seconds . should_equal 3600 time . to_seconds . should_equal 3600
Test.specify "should handle errors when creating a time" <| 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 -> Time.Time_Error msg ->
msg . should_equal "Invalid value for HourOfDay (valid values 0 - 23): 24" msg . should_equal "Invalid value for HourOfDay (valid values 0 - 23): 24"
result -> result ->
@ -48,7 +48,7 @@ spec =
time.to_seconds . should_equal 36000 time.to_seconds . should_equal 36000
Test.specify "should throw error when parsing invalid time" <| 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 -> Time.Time_Error msg ->
msg . should_equal "Text '1200' could not be parsed at index 2" msg . should_equal "Text '1200' could not be parsed at index 2"
result -> result ->
@ -60,7 +60,7 @@ spec =
Test.specify "should throw error when parsing custom format" <| Test.specify "should throw error when parsing custom format" <|
time = Time_Of_Day.parse "12:30" "HH:mm:ss" time = Time_Of_Day.parse "12:30" "HH:mm:ss"
case time.catch (x -> x) of case time.catch of
Time.Time_Error msg -> Time.Time_Error msg ->
msg . should_equal "Text '12:30' could not be parsed at index 5" msg . should_equal "Text '12:30' could not be parsed at index 5"
result -> result ->
@ -90,14 +90,14 @@ spec =
time . to_seconds . should_equal 3599 time . to_seconds . should_equal 3599
Test.specify "should throw error when adding date-based interval" <| 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 -> Time.Time_Error message ->
message . should_equal "Time_Of_Day does not support date intervals" message . should_equal "Time_Of_Day does not support date intervals"
result -> result ->
Test.fail ("Unexpected result: " + result.to_text) Test.fail ("Unexpected result: " + result.to_text)
Test.specify "should throw error when subtracting date-based interval" <| 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 -> Time.Time_Error message ->
message . should_equal "Time_Of_Day does not support date intervals" message . should_equal "Time_Of_Day does not support date intervals"
result -> result ->

View File

@ -20,7 +20,7 @@ spec =
time . zone . zone_id . should_equal Zone.utc.zone_id time . zone . zone_id . should_equal Zone.utc.zone_id
Test.specify "should handle errors when creating time" <| 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 -> Time.Time_Error msg ->
msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 0" msg . should_equal "Invalid value for MonthOfYear (valid values 1 - 12): 0"
result -> result ->
@ -108,7 +108,7 @@ spec =
time . zone . zone_id . should_equal "Europe/Paris" time . zone . zone_id . should_equal "Europe/Paris"
Test.specify "should throw error when parsing invalid time" <| 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 -> Time.Time_Error msg ->
msg . should_equal "Text '2008-1-1' could not be parsed at index 5" msg . should_equal "Text '2008-1-1' could not be parsed at index 5"
result -> result ->
@ -137,7 +137,7 @@ spec =
Test.specify "should throw error when parsing custom format" <| Test.specify "should throw error when parsing custom format" <|
time = Time.parse "2008-01-01" "yyyy-MM-dd'T'HH:mm:ss'['z']'" 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 -> Time.Time_Error msg ->
msg . should_equal "Text '2008-01-01' could not be parsed at index 10" msg . should_equal "Text '2008-01-01' could not be parsed at index 10"
result -> result ->

View File

@ -297,7 +297,7 @@ spec = Test.group "Vectors" <|
Test.specify "should be able to map over errors" <| Test.specify "should be able to map over errors" <|
fail a = Error.throw <| My_Error a 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] [1].map fail . map .catch . should_equal [My_Error 1]
Test.specify "should be able to be efficiently converted to a visualisation" <| Test.specify "should be able to be efficiently converted to a visualisation" <|

View File

@ -20,6 +20,7 @@ import project.Semantic.R_Interop_Spec
import project.Data.Array_Spec import project.Data.Array_Spec
import project.Data.Bool_Spec import project.Data.Bool_Spec
import project.Data.Function_Spec
import project.Data.Interval_Spec import project.Data.Interval_Spec
import project.Data.Json_Spec import project.Data.Json_Spec
import project.Data.List_Spec import project.Data.List_Spec
@ -66,6 +67,7 @@ main = Test.Suite.run_main <|
Any_Spec.spec Any_Spec.spec
Array_Spec.spec Array_Spec.spec
Bool_Spec.spec Bool_Spec.spec
Function_Spec.spec
Case_Spec.spec Case_Spec.spec
Conversion_Spec.spec Conversion_Spec.spec
Deep_Export_Spec.spec Deep_Export_Spec.spec

View File

@ -71,9 +71,9 @@ spec =
Text.from Methods.get_bar . should_equal "'bar'" Text.from Methods.get_bar . should_equal "'bar'"
Test.specify "should fail graciously when there is no conversion" <| 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" <| 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" <| Test.specify "should be callable with by-name arguments" <|
.from that=4 self=Foo . should_equal (Foo [4, 0, 0, 0]) .from that=4 self=Foo . should_equal (Foo [4, 0, 0, 0])

View File

@ -18,9 +18,9 @@ do_a_parse str = Long.parseLong str
spec = spec =
Test.group "No Method Errors" <| Test.group "No Method Errors" <|
Test.specify "should be recoverable" <| Test.specify "should be recoverable" <|
err_1 = Panic.recover Any (123 . foobar "baz") . catch e->e err_1 = Panic.recover Any (123 . foobar "baz") . catch
err_2 = Panic.recover Any ("foo" . baz 123) . catch e->e err_2 = Panic.recover Any ("foo" . baz 123) . catch
err_3 = Panic.recover Any (My_Type False . nope) . catch e->e err_3 = Panic.recover Any (My_Type False . nope) . catch
err_1.target.should_equal 123 err_1.target.should_equal 123
err_1.method_name.should_equal "foobar" err_1.method_name.should_equal "foobar"
@ -32,6 +32,17 @@ spec =
err_3.method_name.should_equal "nope" err_3.method_name.should_equal "nope"
Test.group "Dataflow Errors" <| 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" <| Test.specify "should be able to be shown in the default visualization" <|
json = (Error.throw <| My_Type "aaa").to_default_visualization_data 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.expect_panic_with (parse 0.0) Unsupported_Argument_Types
Test.specify "should allow to throw raw Java exceptions" <| 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" exception.getMessage . should_equal "foo"
Panic.get_attached_stack_trace exception . second . name . should_equal "Error_Spec.throw_raw_java" Panic.get_attached_stack_trace exception . second . name . should_equal "Error_Spec.throw_raw_java"

View File

@ -1,3 +1,5 @@
from project.Data.Any import Any
type Panic type Panic
@Builtin_Type @Builtin_Type
type Panic type Panic
@ -24,4 +26,6 @@ type Error
type Error type Error
throw payload = @Builtin_Method "Error.throw" throw payload = @Builtin_Method "Error.throw"
catch_primitive handler = @Builtin_Method "Error.catch_primitive" 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

View File

@ -1,6 +1,9 @@
import project.IO import project.IO
export project.IO export project.IO
import project.Data.Any
from project.Data.Any export Any
import project.Data.List import project.Data.List
from project.Data.List export Nil, Cons, List from project.Data.List export Nil, Cons, List