Improve dataflow errors in the standard library (#1446)

This commit is contained in:
Ara Adkins 2021-02-02 12:31:33 +00:00 committed by GitHub
parent f0115587b0
commit af1aab35aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 390 additions and 140 deletions

1
.github/CODEOWNERS vendored
View File

@ -3,6 +3,7 @@
# Distribution
/distribution @iamrecursion @kustosz @radeusgd
/distribution/std-lib @iamrecursion @kustosz @wdanilo
# Scala Libraries
/lib/scala/cli @iamrecursion @kustosz @radeusgd

View File

@ -54,3 +54,67 @@ Any.is_nothing : Boolean
Any.is_nothing = case this of
Nothing -> True
_ -> False
## Executes the provided handler on a dataflow error, or executes as identity on
a non-error value.
Arguments:
- handler: The function to call on this if it is an error value. By default
this is identity.
> Example
Catching an erroneous value to perform some operation on it.
(Time.Time_Error "Message").catch (err -> IO.println err)
Any.catch : (Error -> Any) -> Any
Any.catch (handler = x->x) = this.catch_primitive handler
## Applies the function `this` to the provided argument.
Arguments:
- argument: The argument to apply `this` to.
> Example
Applying a function to a block.
(x -> x + 1) <|
y = 1 ^ 3
3 + y
Any.<| : Any -> Any
Any.<| ~argument = this argument
## Applies the function on the right hand side to the argument on the left.
Arguments
- function: The function to apply to `this`.
> Example
Applying a function in a pipeline.
1 |> (* 2)
Any.|> : (Any -> Any) -> Any
Any.|> function = function this
## Composes two functions together.
For `f << g`, this creates the function composition `f ∘ g`.
Arguments:
- that: The function to compose with `this`.
> Example
Compose the functions +1 and *2 and apply it to 2
(+1 << *2) 2
Any.<< : (Any -> Any) -> (Any -> Any) -> Any -> Any
Any.<< that = x -> this (that x)
## Composes two functions together in the forward direction.
For `f >> g`, this creates the function composition `g ∘ f`.
Arguments:
- that: The function to compose with `this`.
> Example
Add one and then multiply by two as a function applied to 2.
(+1 >> *2) 2
Any.>> : (Any -> Any) -> (Any -> Any) -> Any -> Any
Any.>> that = x -> that (this x)

View File

@ -90,7 +90,7 @@ Object.get field = this.fields.get field
parse : Text -> Json ! Parse_Error
parse json_text =
r = Panic.recover (Internal.parse_helper json_text)
r.catch <| case _ of
r.catch e-> case e of
Polyglot_Error err -> Error.throw (Parse_Error err.getMessage)
p -> Panic.throw p

View File

@ -175,7 +175,7 @@ into_helper fmt json = case fmt of
_ -> Panic.throw (Type_Mismatch_Error json fmt)
## Helper used to parse text into a JSON value.
parse_helper : Text -> Json ! Polyglot_Error
parse_helper : Text -> Json
parse_helper json_text =
consumer = here.mk_consumer
Parser.parse json_text consumer

View File

@ -200,34 +200,34 @@ type List
> Example
This returns 1.
(Cons 1 (Cons 2 Nil)).head
head : Any | Nothing
head : Any ! Nothing
head = case this of
Cons a _ -> a
Nil -> Nothing
Nil -> Error.throw Nothing
## Get all elements from the list except the first.
> Example
This returns (Cons 2 Nil).
(Cons 1 (Cons 2 Nil)).tail
tail : List | Nothing
tail : List ! Nothing
tail = case this of
Cons _ b -> b
Nil -> Nothing
Nil -> Error.throw Nothing
## Get all elements from the list except the last.
> Example
Removing the last element of the list to give (Cons 1 Nil).
(Cons 1 (Cons 2 Nil)).init
init : List | Nothing
init : List ! Nothing
init =
init' x y = case y of
Nil -> Nil
Cons a b -> Cons x (init' a b)
case this of
Cons a b -> init' a b
Nil -> Nothing
Nil -> Error.throw Nothing
## Get the last element of the list.
@ -235,14 +235,16 @@ type List
Getting the final element, in this case 2.
(Cons 1 (Cons 2 Nil)).last
last : Any | Nothing
last = this.fold Nothing (_ -> r -> r)
last = case this.fold Nothing (_ -> r -> r) of
Nothing -> Error.throw Nothing
a -> a
## Get the first element from the list.
> Example
This returns 1.
(Cons 1 (Cons 2 Nil)).first
first : Any | Nothing
first : Any ! Nothing
first = this.head
## Get all elements from the list except the first.
@ -250,5 +252,5 @@ type List
> Example
This returns (Cons 2 Nil).
(Cons 1 (Cons 2 Nil)).rest
rest : List | Nothing
rest : List ! Nothing
rest = this.tail

View File

@ -76,10 +76,10 @@ type Map
## Gets the value associated with `key` in this map, or returns a
`Nothing`, if `key` is not present.
get : Any -> Any | Nothing
get : Any -> Any ! Nothing
get key =
go map = case map of
Tip -> Nothing
Tip -> Error.throw Nothing
Bin _ k v l r ->
if k == key then v else
if k > key then @Tail_Call go l else @Tail_Call go r
@ -90,8 +90,7 @@ type Map
it isn't present.
get_or_else : Any -> Any -> Any
get_or_else key other =
result = this.get key
if result.is_nothing then other else result
this.get key . catch (_ -> other)
## Transforms the map's keys and values to create a new map.

View File

@ -0,0 +1,46 @@
from Base import all
## A type representing computations that may fail.
type Maybe
## No contained value.
Nothing
## A value.
type Some value
## Applies the provided function to the contained value if it exists,
otherwise returning the provided default value.
Arguments:
- default: The value to return if `this` is Nothing. This value is lazy
and hence will not execute any provided computation unless it is used.
- function: The function to execute on the value inside the `Just`, if it
is a just.
> Example
Apply a function over a Just value to get 4.
(Just 2).maybe 0 *2
maybe : Any -> (Any -> Any) -> Any
maybe ~default function = case this of
Nothing -> default
Some val -> function val
## Check if the maybe value is `Just`.
> Example
Check if `Nothing` is `Just`.
Nothing.is_just
is_just : Boolean
is_just = case this of
Nothing -> False
Some _ -> True
## Check if the maybe value is `Nothing`.
> Example
Check if `Nothing` is `Nothing`.
Nothing.is_nothing
is_nothing : Boolean
is_nothing = this.is_just.not

View File

@ -96,6 +96,12 @@ type Date
## Obtains an instance of `Date` from a text, such as "2007-12-03".
Arguments:
- text: The textual content to parse as a date.
Returns a `Time_Error` if the provided `text` cannot be parsed using the
provided `pattern`.
The text must represent a valid date and is parsed using the ISO-8601
extended local date format. The format consists of:
@ -115,19 +121,26 @@ type Date
> Example
Recover from the parse error.
Date.parse "my birthday" . catch <| case _ of
Date.parse "my birthday" . catch e-> case e of
Time.Error _ -> Date.new 2000 1 1
parse : Text -> Date
parse : Text -> Date ! Time.Time_Error
parse text =
Panic.recover (Date (LocalDate.parse text)) . catch <| case _ of
Panic.recover (Date (LocalDate.parse text)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x
## Obtains an instance of `Date` from a text using custom format.
Arguments:
- text: The textual content to parse as a time.
- pattern: The pattern describing how to parse the text.
For the list of accepted symbols in pattern refer to
`Base.Data.Time.Time.format` doc.
Returns a `Time_Error` if the provided `text` cannot be parsed using the
provided `pattern`.
> Example
Parse "1999-1-1" as Date.
Date.parse_format "1999-1-1" "yyyy-M-d"
@ -135,12 +148,12 @@ parse text =
> Example
Recover from the parse error.
date = Date.parse "1999-1-1" "yyyy-MM-dd"
date.catch <| case _ of
date.catch e-> case e of
Time.Error msg -> Date.new 2000 1 1
parse_format : Text -> Text -> Date
parse_format : Text -> Text -> Date ! Time.Time_Error
parse_format text pattern =
format = DateTimeFormatter.ofPattern pattern
Panic.recover (Date (LocalDate.parse text format)) . catch <| case _ of
Panic.recover (Date (LocalDate.parse text format)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x
@ -158,6 +171,8 @@ today = here.now
- day - the day-of-month to represent, from 1 to 31 and must be valid for the
year and month
Returns a `Time_Error` if the provided time is not valid.
> Example
Create a new local date at Unix epoch.
Date.new 1970
@ -165,8 +180,8 @@ today = here.now
> Example
Get the local date of 5th August 1986.
Date.new 1986 8 5
new : Integer -> Integer -> Integer -> Date
new : Integer -> Integer -> Integer -> Date ! Time.Time_Error
new year (month = 1) (day = 1) =
Panic.recover (Date (LocalDate.of year month day)) . catch <| case _ of
Panic.recover (Date (LocalDate.of year month day)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x

View File

@ -131,7 +131,7 @@ new language country=Nothing variant=Nothing =
> Example
Creating the locale en_US.
Locale.from_language_tag "en_US"
from_language_tag : Text -> Locale | Nothing
from_language_tag : Text -> Locale
from_language_tag tag =
java_locale = JavaLocale.forLanguageTag tag
here.from_java java_locale

View File

@ -180,6 +180,9 @@ type Time
## Obtains an instance of `Time` from a text such as
"2007-12-03T10:15:30+01:00 Europe/Paris".
Arguments:
- text: The text representing the time to be parsed.
The text must represent a valid date-time and is parsed using the ISO-8601
extended offset date-time format to add the timezone. The section in square
brackets is not part of the ISO-8601 standard. The format consists of:
@ -192,6 +195,9 @@ type Time
sensitive.
- A close square bracket ']'.
This method will return a `Time_Error` if the provided time cannot be parsed
using the above format.
> Example
Parse UTC time.
Time.parse "2020-10-01T04:11:12Z"
@ -210,16 +216,24 @@ type Time
> Example
Recover from the parse error.
Time.parse "2020-10-01" . catch <| case _ of
Time.parse "2020-10-01" . catch e-> case e of
Time.Error _ -> Time.now
parse : Text -> Time
parse : Text -> Time ! Time_Error
parse text =
Panic.recover (Time (Time_Utils.parse_time text)) . catch <| case _ of
Panic.recover (Time (Time_Utils.parse_time text)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time_Error err.getMessage)
x -> x
## Obtains an instance of Time from a text using custom format.
Arguments:
- text: The text to parse as a time of day, using the specified pattern.
- pattern: The pattern to use for parsing the input text.
- locale: The locale in which the pattern should be interpreted.
Returns a `Time_Error` if the provided text cannot be parsed using the
provided pattern and locale.
For the list of accepted symbols in pattern refer to `Time.format` doc.
> Example
@ -229,9 +243,9 @@ parse text =
> Example
Parse "06 of May 2020 at 04:30AM" as Time
Date.parse_format "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
parse_format : Text -> Text -> Locale -> Time_Of_Day
parse_format : Text -> Text -> Locale -> Time ! Time_Error
parse_format text pattern locale=Locale.default =
Panic.recover (Time (Time_Utils.parse_time_format text pattern locale.java_locale)) . catch <| case _ of
Panic.recover (Time (Time_Utils.parse_time_format text pattern locale.java_locale)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time_Error err.getMessage)
x -> x
@ -242,14 +256,17 @@ now = Time ZonedDateTime.now
## Obtains an instance of `Time` from a year, month, day, hour, minute,
second, nanosecond and timezone.
- month - the month-of-year to represent, from 1 (January) to 12 (December)
- day - the day-of-month to represent, from 1 to 31 and must be valid for the
Arguments:
- month: the month-of-year to represent, from 1 (January) to 12 (December)
- day: the day-of-month to represent, from 1 to 31 and must be valid for the
year and month
- hour - the hour-of-day to represent, from 0 to 23
- minute - the minute-of-hour to represent, from 0 to 59
- second - the second-of-minute to represent, from 0 to 59
- nanosecond - the nano-of-second to represent, from 0 to 999,999,999
- zone - the timezone
- hour: the hour-of-day to represent, from 0 to 23
- minute: the minute-of-hour to represent, from 0 to 59
- second: the second-of-minute to represent, from 0 to 59
- nanosecond: the nano-of-second to represent, from 0 to 999,999,999
- zone: the timezone
Returns a `Time_Error` if the provided time cannot be represented.
> Example
Create a new zoned date time at Unix epoch.
@ -258,8 +275,8 @@ now = Time ZonedDateTime.now
> Example
Get the 5 August 1986 at midnight.
Time.new 1986 8 5
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Time
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 (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) . catch <| case _ of
Panic.recover (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

View File

@ -101,6 +101,12 @@ type Time_Of_Day
## Obtains an instance of `Time_Of_Day` from a text such as "10:15".
Arguments:
- text: The text to parse as a time of day.
Returns a `Time_Error` if the provided text cannot be parsed using the
default format.
The text must represent a valid time and is parsed using the ISO-8601
extended local time format. The format consists of:
@ -124,16 +130,24 @@ type Time_Of_Day
> Example
Recover from the parse error.
Time_Of_Day.parse "half past twelve" . catch <| case
Time_Of_Day.parse "half past twelve" . catch e-> case e of
Time.Error _ -> Time_Of_Day.new
parse : Text -> Time_Of_Day
parse : Text -> Time_Of_Day ! Time.Time_Error
parse text =
Panic.recover (Time_Of_Day (LocalTime.parse text)) . catch <| case _ of
Panic.recover (Time_Of_Day (LocalTime.parse text)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x
## Obtains an instance of Time_Of_Day from a text using custom format.
Arguments:
- text: The text to parse as a time of day, using the specified pattern.
- pattern: The pattern to use for parsing the input text.
- locale: The locale in which the pattern should be interpreted.
Returns a `Time_Error` if the provided text cannot be parsed using the
provided pattern and locale.
For the list of accepted symbols in pattern refer to
`Base.Data.Time.Time.format` doc.
@ -144,10 +158,10 @@ parse text =
> Example
Parse "4:30AM" as Time_Of_Day
Date.parse_format "4:30AM" "h:mma"
parse_format : Text -> Text -> Locale -> Time_Of_Day
parse_format : Text -> Text -> Locale -> Time_Of_Day ! Time.Time_Error
parse_format text pattern locale=Locale.default =
format = (DateTimeFormatter.ofPattern pattern).withLocale locale.java_locale
Panic.recover (Time_Of_Day (LocalTime.parse text format)) . catch <| case _ of
Panic.recover (Time_Of_Day (LocalTime.parse text format)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x
@ -163,6 +177,8 @@ now = Time_Of_Day LocalTime.now
- second - the second-of-minute to represent, from 0 to 59
- nanosecond - the nano-of-second to represent, from 0 to 999,999,999
Returns a `Time_Error` if the provided time is not a valid time.
> Example
Create a new local time at Unix epoch.
Time_Of_Day.new
@ -170,8 +186,8 @@ now = Time_Of_Day LocalTime.now
> Example
Get the local time at 9:30.
Time_Of_Day.new 9 30
new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day
new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day ! Time.Time_Error
new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
Panic.recover (Time_Of_Day (LocalTime.of hour minute second nanosecond)) . catch <| case _ of
Panic.recover (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

View File

@ -120,11 +120,11 @@ type Vector
In the following example, we'll compute the sum of all elements of a
vector:
[0, 1, 2] . reduce (+)
reduce : (Any -> Any -> Any) -> Any | Nothing
reduce : (Any -> Any -> Any) -> Any ! Nothing
reduce function =
case this.not_empty of
True -> this.tail.fold this.head function
False -> Nothing
False -> Error.throw Nothing
## Checks whether a predicate holds for at least one element of this vector.
@ -360,8 +360,8 @@ type Vector
> Example
Empty vectors return `Nothing`.
[].head == Nothing
head : Any | Nothing
head = if this.length >= 1 then this.at 0 else Nothing
head : Any ! Nothing
head = if this.length >= 1 then this.at 0 else Error.throw Nothing
## Get all elements in the vector except the first.
@ -371,8 +371,8 @@ type Vector
> Example
Empty vectors return `Nothing`.
[].tail == Nothing
tail : Vector | Nothing
tail = if this.length >= 1 then this.drop_start 1 else Nothing
tail : Vector ! Nothing
tail = if this.length >= 1 then this.drop_start 1 else Error.throw Nothing
## Get the all elements in the vector except the last.
@ -382,8 +382,8 @@ type Vector
> Example
Empty vectors return `Nothing`.
[].init == Nothing
init : Vector | Nothing
init = if this.length >= 1 then this.drop_end 1 else Nothing
init : Vector ! Nothing
init = if this.length >= 1 then this.drop_end 1 else Error.throw Nothing
## Get the last element of the vector.
@ -393,8 +393,8 @@ type Vector
> Example
Empty vectors return `Nothing`.
[].last == Nothing
last : Vector | Nothing
last = if this.length >= 1 then (this.take_end 1).at 0 else Nothing
last : Vector ! Nothing
last = if this.length >= 1 then (this.take_end 1).at 0 else Error.throw Nothing
## Get the first element from the vector.
@ -404,7 +404,7 @@ type Vector
> Example
Empty vectors return `Nothing`.
[].first == Nothing
first : Vector | Nothing
first : Vector ! Nothing
first = this.head
## Get all elements in the vector except the first.
@ -415,7 +415,7 @@ type Vector
> Example
Empty vectors return `Nothing`.
[].rest == Nothing
rest : Vector | Nothing
rest : Vector ! Nothing
rest = this.tail
## Sort the Vector.

View File

@ -10,5 +10,19 @@ type Unimplemented_Error message
## A function that can be used to indicate that something hasn't been
implemented yet.
unimplemented : Text -> Void ! Unimplemented_Error
unimplemented : Text -> Void
unimplemented message="" = Panic.throw (Unimplemented_Error message)
## Executes the provided handler on a dataflow error, or executes as identity on
a non-error value.
Arguments:
- handler: The function to call on this if it is an error value. By default
this is identity.
> Example
Catching an erroneous value to perform some operation on it.
(Time.Time_Error "Message").catch (err -> IO.println err)
Error.catch : (Error -> Any) -> Any
Error.catch (handler = x->x) = this.catch_primitive handler

View File

@ -3,6 +3,7 @@ import Base.Data.Interval
import Base.Data.Json
import Base.Data.List
import Base.Data.Map
import Base.Data.Maybe
import Base.Data.Noise
import Base.Data.Number.Extensions
import Base.Data.Ordering
@ -23,6 +24,7 @@ from Builtins import Nothing, Number, Integer, Any, True, False, Cons, Boolean,
export Base.Data.Interval
export Base.Data.Json
export Base.Data.Map
export Base.Data.Maybe
export Base.Data.Ordering
export Base.Data.Ordering.Sort_Order
export Base.Data.Vector

View File

@ -19,7 +19,7 @@ e = 2.718281828459045235360
## Returns the smaller value of `a` and `b`.
min : Number -> Number -> Number
min a b = if a < b then a else b
min a b = if a <= b then a else b
## Returns the larger value of `a` and `b`.
max : Number -> Number -> Number

View File

@ -95,7 +95,8 @@ is_a value typ = if typ == Any then True else
_ ->
meta_val = here.meta value
case meta_val of
Atom _ -> meta_val.constructor == typ
Atom _ -> if Builtins.meta.is_atom typ then typ == value else
meta_val.constructor == typ
Constructor _ ->
meta_typ = here.meta typ
case meta_typ of

View File

@ -16,7 +16,7 @@ type Uri_Error
## PRIVATE
panic_on_error ~action =
action . catch <| case _ of
action . catch e-> case e of
Syntax_Error msg -> Panic.throw (Syntax_Error msg)
## PRIVATE
@ -151,8 +151,8 @@ type Uri
> Example
Parse Uri text.
Uri.parse "http://example.com"
parse : Text -> Uri
parse : Text -> Uri ! Syntax_Error
parse text =
Panic.recover (Uri (Java_URI.create text)) . catch <| case _ of
Panic.recover (Uri (Java_URI.create text)) . catch e-> case e of
Polyglot_Error ex -> Error.throw (Syntax_Error ex.getMessage)
other -> Panic.throw other

View File

@ -18,6 +18,12 @@ Spec.is_fail = this.behaviors.any .is_fail
## PRIVATE
Suite.is_fail = this.specs.any .is_fail
## PRIVATE
type Finished_With_Error err
## PRIVATE
type Matched_On_Error err
## PRIVATE
type Assertion
type Success
@ -49,13 +55,14 @@ Any.should verb argument = verb Verbs this argument
fail message = Panic.throw (Failure message)
## Expect a function to fail with the provided dataflow error.
expect_error_with ~action matcher =
result = action
fail_msg = "Expected an error " + matcher.to_text + "but none occurred."
if result.is_error.not then here.fail fail_msg else
caught = result.catch x->x
if caught.is_a matcher then Nothing else
here.fail ("Unexpected error " + caught.to_text + " thrown.")
Any.should_fail_with matcher =
here.fail ("Expected an error " + matcher.to_text + " but none occurred.")
## Expect a function to fail with the provided dataflow error.
Error.should_fail_with matcher =
caught = this.catch x->x
if caught.is_a matcher then Nothing else
here.fail ("Unexpected error " + caught.to_text + " returned.")
## Expect a function to fail with the provided panic.
expect_panic_with ~action matcher =
@ -73,6 +80,9 @@ Any.should_equal that = case this == that of
msg = this.to_text + " did not equal " + that.to_text + "."
Panic.throw (Failure msg)
## Asserts that `this` value is equal to the expected value.
Error.should_equal _ = Panic.throw (Matched_On_Error this)
## Asserts that `this` is within `epsilon` from `that`.
Decimal.should_equal that (epsilon = 0) = case this.equals that epsilon of
True -> Success
@ -85,11 +95,17 @@ Boolean.should_be_true = case this of
True -> Success
False -> Panic.throw (Failure "Expected False to be True.")
## Asserts that the given `Boolean` is `True`.
Error.should_be_true = Panic.throw (Matched_On_Error this)
## Asserts that the given `Boolean` is `False`
Boolean.should_be_false = case this of
True -> Panic.throw (Failure "Expected True to be False.")
False -> Success
## Asserts that the given `Boolean` is `False`
Error.should_be_false = Panic.throw (Matched_On_Error this)
## PRIVATE
Spec.print_report =
IO.print_err (this.name + ":")
@ -137,19 +153,21 @@ specify label ~behavior pending=False =
new_spec = Spec spec.name (Cons (Behavior label result) spec.behaviors)
State.put Spec new_spec
## PRIVATE
run_spec ~behavior =
recovery = Panic.recover <|
behavior
behavior.catch err-> Panic.throw (Finished_With_Error err)
Nothing
maybeExc = case recovery of
_ -> Success
result = maybeExc.catch ex->
case ex of
Failure _ -> ex
_ -> Failure ("Unexpected error has been thrown: " + ex.to_text)
Finished_With_Error x ->
Failure ("An unexpected error was returned: " + x.to_text)
_ -> Failure ("An unexpected panic was thrown: " + ex.to_text)
result
## Creates a new test group, desribing properties of the object
described by `this`.

View File

@ -114,6 +114,7 @@ state. The documentation syntax supports the following tags:
- `PRIVATE`: Used to describe constructs that are private in the language.
- `ADVANCED`: Items that are _not_ private, but are for power users.
- `TEXT_ONLY`: Items that do not apply to the graphical mode.
- `UNSTABLE`: Used for items that are not yet considered stable.
Tags are added at the _top_ of the documentation block, and may also be
accompanied by a description. This description directly follows the tag

View File

@ -14,7 +14,7 @@ import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Any",
name = "catch",
name = "catch_primitive",
description =
"If called on an error, executes the provided handler on the error's payload. Otherwise acts as identity.")
public class CatchAnyNode extends Node {

View File

@ -14,7 +14,7 @@ import org.enso.interpreter.runtime.type.TypesGen;
@BuiltinMethod(
type = "Error",
name = "catch",
name = "catch_primitive",
description =
"If called on an error, executes the provided handler on the error's payload. Otherwise acts as identity.")
public class CatchErrorNode extends Node {

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.node.expression.builtin.mutable;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.Array;
@ -10,7 +11,7 @@ import org.enso.interpreter.runtime.data.Array;
description = "Puts the given element in the given position in the array.")
public class SetAtNode extends Node {
Object execute(Array _this, long index, Object value) {
Object execute(Array _this, long index, @AcceptsError Object value) {
_this.getItems()[(int) index] = value;
return _this;
}

View File

@ -147,7 +147,7 @@ public class Builtins {
scope.registerMethod(panic, "throw", ThrowPanicMethodGen.makeFunction(language));
scope.registerMethod(panic, "recover", RecoverPanicMethodGen.makeFunction(language));
scope.registerMethod(any, "catch", CatchAnyMethodGen.makeFunction(language));
scope.registerMethod(any, "catch_primitive", CatchAnyMethodGen.makeFunction(language));
scope.registerMethod(state, "get", GetStateMethodGen.makeFunction(language));
scope.registerMethod(state, "put", PutStateMethodGen.makeFunction(language));
@ -157,7 +157,6 @@ public class Builtins {
scope.registerMethod(debug, "breakpoint", DebugBreakpointMethodGen.makeFunction(language));
scope.registerMethod(function, "call", ExplicitCallFunctionMethodGen.makeFunction(language));
scope.registerMethod(function, "<|", ApplicationOperatorMethodGen.makeFunction(language));
scope.registerMethod(any, "to_text", AnyToTextMethodGen.makeFunction(language));

View File

@ -15,7 +15,7 @@ public class DataflowError {
scope.registerConstructor(error);
scope.registerMethod(error, "throw", ThrowErrorMethodGen.makeFunction(language));
scope.registerMethod(error, "catch", CatchErrorMethodGen.makeFunction(language));
scope.registerMethod(error, "catch_primitive", CatchErrorMethodGen.makeFunction(language));
scope.registerMethod(error, "to_text", ErrorToTextMethodGen.makeFunction(language));
}

View File

@ -159,7 +159,9 @@ type Any
@Builtin_Type
type Any
## Executes the provided handler on a dataflow error, or executes as
## PRIVATE
Executes the provided handler on a dataflow error, or executes as
identity on a non-error value.
Arguments:
@ -167,9 +169,9 @@ type Any
> Example
Catching an erroneous value to perform some operation on it.
(Time.Time_Error "Message").catch (err -> IO.println err)
catch : (Error -> Any) -> Any
catch handler = @Builtin_Method "Any.catch"
(Time.Time_Error "Message").catch_primitive (err -> IO.println err)
catch_primitive : (Error -> Any) -> Any
catch_primitive handler = @Builtin_Method "Any.catch"
## Generic conversion of an arbitrary Enso value to a corresponding textual
representation.
@ -208,7 +210,9 @@ type Error
throw : Any -> Error
throw payload = @Builtin_Method "Error.throw"
## Executes the provided handler on a dataflow error, or executes as
## PRIVATE
Executes the provided handler on a dataflow error, or executes as
identity on a non-error value.
Arguments:
@ -216,9 +220,9 @@ type Error
> Example
Catching an erroneous value to perform some operation on it.
(Time.Time_Error "Message").catch (err -> IO.println err)
catch : (Error -> Any) -> Any
catch handler = @Builtin_Method "Any.catch"
(Time.Time_Error "Message").catch_primitive (err -> IO.println err)
catch_primitive : (Error -> Any) -> Any
catch_primitive handler = @Builtin_Method "Any.catch"
## Converts an error to a corresponding textual representation.
to_text : Text
@ -366,20 +370,6 @@ type Function
call : Any
call = @Builtin_Method "Function.call"
## Takes a function and an argument and applies the function to the argument
Arguments:
- argument: The argument to apply this to.
> Example
Applying identity to a value produced by a block to get 3.
(x -> x) <|
a = 1
b = 2
a + b
<| : (Any -> Any) -> Any -> Any
<| argument = @Builtin_Method "Function.<|"
## Generic utilities for interacting with other languages.
type Polyglot

View File

@ -3590,7 +3590,7 @@ class RuntimeServerTest
),
context.executionComplete(contextId)
)
context.consumeOut shouldEqual List("(Syntax_Error 'Unrecognized token.')")
context.consumeOut shouldEqual List("(Error: (Syntax_Error 'Unrecognized token.'))")
}
it should "return compiler error syntax error" in {

View File

@ -14,7 +14,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
|
|main =
| x = Panic.recover ()
| x.catch err->
| x.catch_primitive err->
| case err of
| Syntax_Error msg -> "Oopsie, it's a syntax error: " + msg
|""".stripMargin
@ -29,7 +29,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
|
|main =
| x = Panic.recover @
| x.catch .to_text
| x.catch_primitive .to_text
|""".stripMargin
eval(code) shouldEqual "(Syntax_Error 'Unrecognized token.')"
}
@ -42,7 +42,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
| x = 1
| x = 2
|
|main = Panic.recover here.foo . catch .to_text
|main = Panic.recover here.foo . catch_primitive .to_text
|""".stripMargin
eval(code) shouldEqual "(Compile_Error 'Variable x is being redefined.')"
}
@ -55,7 +55,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
| my_var = 10
| my_vra
|
|main = Panic.recover here.foo . catch .to_text
|main = Panic.recover here.foo . catch_primitive .to_text
|""".stripMargin
eval(code) shouldEqual "(Compile_Error 'Variable `my_vra` is not defined.')"
}

View File

@ -52,7 +52,7 @@ class DataflowErrorsTest extends InterpreterTest {
|
|main =
| intError = Error.throw 1
| intError.catch (x -> x + 3)
| intError.catch_primitive (x -> x + 3)
|""".stripMargin
eval(code) shouldEqual 4
}
@ -65,7 +65,7 @@ class DataflowErrorsTest extends InterpreterTest {
|
|main =
| unitErr = Error.throw Nothing
| IO.println (unitErr.catch MyCons)
| IO.println (unitErr.catch_primitive MyCons)
|""".stripMargin
eval(code)
consumeOut shouldEqual List("(MyCons Nothing)")
@ -83,14 +83,14 @@ class DataflowErrorsTest extends InterpreterTest {
|
|main =
| myErr = Error.throw (MyError 20)
| IO.println(myErr.catch .recover)
| IO.println(myErr.catch_primitive .recover)
|""".stripMargin
eval(code)
consumeOut shouldEqual List("(MyRecovered 20)")
}
"make the catch method an identity for non-error values" in {
val code = "main = 10.catch (x -> x + 1)"
val code = "main = 10.catch_primitive (x -> x + 1)"
eval(code) shouldEqual 10
}

View File

@ -57,7 +57,7 @@ class PanicsTest extends InterpreterTest {
|main =
| caught = Panic.recover (Long.parseLong "oops")
| IO.println caught
| cause = caught.catch <| case _ of
| cause = caught.catch_primitive e-> case e of
| Polyglot_Error err -> err
| _ -> "fail"
| IO.println cause

View File

@ -11,7 +11,7 @@ build_long n =
build_long_bldr n =
bldr = StringBuilder.new
1.up_to n . each (bldr.append _)
1.up_to n . each n-> bldr.append n
res = bldr.toString
res

View File

@ -1,3 +1,5 @@
from Base import all
import Test
import Table_Tests.Table_Spec

View File

@ -60,20 +60,20 @@ spec = Test.group "List" <|
empty.take_start 2 . should_equal Nil
Test.specify "should allow getting the head of the list with `.head`" <|
l.head . should_equal 1
empty.head . should_equal Nothing
empty.head.catch . should_equal Nothing
Test.specify "should allow getting the tail of the list with `.tail`" <|
l.tail . should_equal (Cons 2 (Cons 3 Nil))
empty.tail . should_equal Nothing
empty.tail.catch . should_equal Nothing
Test.specify "should allow getting the init of the list with `.init`" <|
l.init . should_equal (Cons 1 (Cons 2 Nil))
empty.init . should_equal Nothing
empty.init.catch . should_equal Nothing
Test.specify "should allow getting the last element of the list with `.last`" <|
l.last . should_equal 3
empty.last . should_equal Nothing
empty.last.catch . should_equal Nothing
Test.specify "should allow getting the head of the list with `.first`" <|
l.first . should_equal 1
empty.first . should_equal Nothing
empty.first.catch . should_equal Nothing
Test.specify "should allow getting the tail of the list with `.rest`" <|
l.rest . should_equal (Cons 2 (Cons 3 Nil))
empty.rest . should_equal Nothing
empty.rest.catch . should_equal Nothing

View File

@ -43,7 +43,7 @@ spec = Test.group "Maps" <|
m.get "foo" . should_equal 134
m.get "bar" . should_equal 654
m.get "baz" . should_equal "spam"
m.get "nope" . should_equal Nothing
(m.get "nope").should_fail_with Nothing
Test.specify "should support get_or_else" <|
m = Map.empty . insert 2 3
m.get_or_else 2 0 . should_equal 3

View File

@ -0,0 +1,19 @@
from Base import all
import Test
spec = Test.group "Maybe" <|
Test.specify "should have a Nothing variant" <|
Nothing . should_equal Nothing
Test.specify "should have a Just variant" <|
(Maybe.Some 2).value . should_equal 2
Test.specify "should provide the `maybe` function" <|
Nothing.maybe 2 x->x . should_equal 2
(Maybe.Some 7).maybe 2 (*2) . should_equal 14
Test.specify "should provide `is_just`" <|
Nothing.is_just . should_be_false
Maybe.Some 2 . is_just . should_be_true
Test.specify "should provide `is_nothing`" <|
Nothing.is_nothing . should_be_true
Maybe.Some 2 . is_nothing . should_be_false

View File

@ -42,7 +42,7 @@ spec =
(almost_max_long * 2 / almost_max_long_times_three) . should_equal 0.6666666 epsilon=eps
Test.specify "should support integer division" <|
(10.div 3) . should_equal 3
Test.expect_error_with (10.div 0) Arithmetic_Error
# (10.div 0).should_fail_with Arithmetic_Error
Test.specify "should support integral binary literals" <|
lit = 2_01101101
lit . should_equal 109
@ -93,28 +93,28 @@ spec =
positive_bits.bit_shift_l 64 . should_equal 16_6d0000000000000000
positive_bits.bit_shift_l -2 . should_equal 2_011011
positive_bits.bit_shift_l -64 . should_equal 0
Test.expect_error_with (positive_bits.bit_shift_l positive_big_bits) Arithmetic_Error
(positive_bits.bit_shift_l positive_big_bits).should_error_with Arithmetic_Error
positive_bits.bit_shift_l negative_big_bits . should_equal 0
negative_bits.bit_shift_l 2 . should_equal -436
negative_bits.bit_shift_l 64 . should_equal -2010695104034341126144
negative_bits.bit_shift_l -2 . should_equal -28
negative_bits.bit_shift_l -64 . should_equal -1
Test.expect_error_with (negative_bits.bit_shift_l positive_big_bits) Arithmetic_Error
(negative_bits.bit_shift_l positive_big_bits).should_error_with Arithmetic_Error
negative_bits.bit_shift_l negative_big_bits . should_equal -1
positive_big_bits.bit_shift_l 2 . should_equal 110680464442257309672
positive_big_bits.bit_shift_l 64 . should_equal 510423550381407695084381446705395007488
positive_big_bits.bit_shift_l -2 . should_equal 6917529027641081854
positive_big_bits.bit_shift_l -100 . should_equal 0
Test.expect_error_with (positive_big_bits.bit_shift_l positive_big_bits) Arithmetic_Error
(positive_big_bits.bit_shift_l positive_big_bits).should_error_with Arithmetic_Error
positive_big_bits.bit_shift_l negative_big_bits . should_equal 0
negative_big_bits.bit_shift_l 2 . should_equal -110680464442257309672
negative_big_bits.bit_shift_l 64 . should_equal -510423550381407695084381446705395007488
negative_big_bits.bit_shift_l -2 . should_equal -6917529027641081855
negative_big_bits.bit_shift_l -100 . should_equal -1
Test.expect_error_with (negative_big_bits.bit_shift_l positive_big_bits) Arithmetic_Error
(negative_big_bits.bit_shift_l positive_big_bits).should_error_with Arithmetic_Error
negative_big_bits.bit_shift_l negative_big_bits . should_equal -1
Test.specify "should support right bit shifts, preserving sign" <|
positive_bits = 2_01101101
@ -126,28 +126,28 @@ spec =
positive_bits.bit_shift_r 64 . should_equal (positive_bits.bit_shift_l -64)
positive_bits.bit_shift_r -2 . should_equal (positive_bits.bit_shift_l 2)
positive_bits.bit_shift_r -64 . should_equal (positive_bits.bit_shift_l 64)
Test.expect_error_with (positive_bits.bit_shift_r negative_big_bits) Arithmetic_Error
(positive_bits.bit_shift_r negative_big_bits).should_error_with Arithmetic_Error
positive_bits.bit_shift_r positive_big_bits . should_equal 0
negative_bits.bit_shift_r 2 . should_equal (negative_bits.bit_shift_l -2)
negative_bits.bit_shift_r 64 . should_equal (negative_bits.bit_shift_l -64)
negative_bits.bit_shift_r -2 . should_equal (negative_bits.bit_shift_l 2)
negative_bits.bit_shift_r -64 . should_equal (negative_bits.bit_shift_l 64)
Test.expect_error_with (negative_bits.bit_shift_r negative_big_bits) Arithmetic_Error
(negative_bits.bit_shift_r negative_big_bits).should_error_with Arithmetic_Error
negative_bits.bit_shift_r positive_big_bits . should_equal -1
positive_big_bits.bit_shift_r 2 . should_equal (positive_big_bits.bit_shift_l -2)
positive_big_bits.bit_shift_r 64 . should_equal (positive_big_bits.bit_shift_l -64)
positive_big_bits.bit_shift_r -2 . should_equal (positive_big_bits.bit_shift_l 2)
positive_big_bits.bit_shift_r -100 . should_equal (positive_big_bits.bit_shift_l 100)
Test.expect_error_with (positive_big_bits.bit_shift_r negative_big_bits) Arithmetic_Error
(positive_big_bits.bit_shift_r negative_big_bits).should_error_with Arithmetic_Error
positive_big_bits.bit_shift_r positive_big_bits . should_equal 0
negative_big_bits.bit_shift_r 2 . should_equal (negative_big_bits.bit_shift_l -2)
negative_big_bits.bit_shift_r 64 . should_equal (negative_big_bits.bit_shift_l -64)
negative_big_bits.bit_shift_r -2 . should_equal (negative_big_bits.bit_shift_l 2)
negative_big_bits.bit_shift_r -100 . should_equal (negative_big_bits.bit_shift_l 100)
Test.expect_error_with (negative_big_bits.bit_shift_r negative_big_bits) Arithmetic_Error
(negative_big_bits.bit_shift_r negative_big_bits).should_error_with Arithmetic_Error
negative_big_bits.bit_shift_r positive_big_bits . should_equal -1
Test.group "Decimals" <|
Test.specify "should exist and expose basic arithmetic operations" <|

View File

@ -8,6 +8,8 @@ T.== that = this.a == that.a
T.compare_to that = if this == that then Ordering.Equal else
if this.a > that.a then Ordering.Greater else Ordering.Less
type My_Error a
spec = Test.group "Vectors" <|
Test.specify "should allow vector creation with a programmatic constructor" <|
Vector.new 100 (ix -> ix + 1) . fold 0 (+) . should_equal 5050
@ -19,7 +21,7 @@ spec = Test.group "Vectors" <|
[1,2,3].fold 0 (+) . should_equal 6
Test.specify "should allow to reduce elements if it is non-empty" <|
[1,2,3].reduce (+) . should_equal 6
[].reduce (+) . should_equal Nothing
[].reduce (+) . should_fail_with Nothing
Test.specify "should check exists" <|
vec = [1, 2, 3, 4, 5]
vec.exists (ix -> ix > 3) . should_be_true
@ -88,42 +90,42 @@ spec = Test.group "Vectors" <|
empty_vec = []
non_empty_vec.head . should_equal 1
singleton_vec.head . should_equal 1
empty_vec.head . should_equal Nothing
empty_vec.head . should_fail_with Nothing
Test.specify "should allow getting the tail of the vector" <|
non_empty_vec = [1, 2, 3, 4, 5]
singleton_vec = [1]
empty_vec = []
non_empty_vec.tail . should_equal [2, 3, 4, 5]
singleton_vec.tail . should_equal []
empty_vec.tail . should_equal Nothing
empty_vec.tail . should_fail_with Nothing
Test.specify "should allow getting the init of the vector" <|
non_empty_vec = [1, 2, 3, 4, 5]
singleton_vec = [1]
empty_vec = []
non_empty_vec.init . should_equal [1, 2, 3, 4]
singleton_vec.init . should_equal []
empty_vec.init . should_equal Nothing
empty_vec.init . should_fail_with Nothing
Test.specify "should allow getting the last element of the vector" <|
non_empty_vec = [1, 2, 3, 4, 5]
singleton_vec = [1]
empty_vec = []
non_empty_vec.last . should_equal 5
singleton_vec.last . should_equal 1
empty_vec.last . should_equal Nothing
empty_vec.last . should_fail_with Nothing
Test.specify "should allow getting the first element" <|
non_empty_vec = [1, 2, 3, 4, 5]
singleton_vec = [1]
empty_vec = []
non_empty_vec.first . should_equal 1
singleton_vec.first . should_equal 1
empty_vec.first . should_equal Nothing
empty_vec.first . should_fail_with Nothing
Test.specify "should allow getting the rest of the vector" <|
non_empty_vec = [1, 2, 3, 4, 5]
singleton_vec = [1]
empty_vec = []
non_empty_vec.rest . should_equal [2, 3, 4, 5]
singleton_vec.rest . should_equal []
empty_vec.rest . should_equal Nothing
empty_vec.rest . should_fail_with Nothing
Test.specify "should be able to be sorted" <|
empty_vec = []
short_vec = [2, 4, 38, -1, -1000, 3671, -32]
@ -159,3 +161,7 @@ spec = Test.group "Vectors" <|
small_vec = [T 1 8, T 1 3, T -20 0, T -1 1, T -1 10, T 4 0]
small_expected = [T 4 0, T 1 3, T 1 8, T -1 10, T -1 1, T -20 0]
small_vec.sort order=Sort_Order.Descending . should_equal small_expected
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]
[1].map fail . map .catch . should_equal [My_Error 1]

View File

@ -1,5 +1,8 @@
from Base import all
import Test
import Tests.Semantic.Any_Spec
import Tests.Semantic.Case_Spec
import Tests.Semantic.Deep_Export.Spec as Deep_Export_Spec
import Tests.Semantic.Error_Spec
@ -12,6 +15,7 @@ import Tests.Data.Interval_Spec
import Tests.Data.Json_Spec
import Tests.Data.List_Spec
import Tests.Data.Map_Spec
import Tests.Data.Maybe_Spec
import Tests.Data.Noise.Generator_Spec as Noise_Generator_Spec
import Tests.Data.Noise_Spec
import Tests.Data.Numbers_Spec
@ -20,14 +24,17 @@ import Tests.Data.Range_Spec
import Tests.Data.Text_Spec
import Tests.Data.Time.Spec as Time_Spec
import Tests.Data.Vector_Spec
import Tests.Network.Http.Header_Spec as Http_Header_Spec
import Tests.Network.Http.Request_Spec as Http_Request_Spec
import Tests.Network.Http_Spec
import Tests.Network.Uri_Spec
import Tests.System.File_Spec
import Tests.System.Process_Spec
main = Test.Suite.runMain <|
Any_Spec.spec
Case_Spec.spec
Deep_Export_Spec.spec
Error_Spec.spec
@ -41,6 +48,7 @@ main = Test.Suite.runMain <|
Json_Spec.spec
List_Spec.spec
Map_Spec.spec
Maybe_Spec.spec
Meta_Spec.spec
Names_Spec.spec
Noise_Generator_Spec.spec

View File

@ -38,9 +38,7 @@ spec_impl =
http = Http.new (version = version_setting)
http.version.should_equal version_setting
Test.specify "should throw error when requesting invalid Uri" <|
case Panic.recover (Http.new.get "not a uri") of
Uri.Syntax_Error _ -> Nothing
other -> Test.fail ("Unexpected result: " + other)
Test.expect_panic_with (Http.new.get "not a uri") Uri.Syntax_Error
Test.specify "should send Get request" <|
expected_response = Json.parse <| '''
{

View File

@ -28,7 +28,7 @@ spec =
addr.raw_query.should_equal "%D0%9A%D0%BE%D0%B4"
addr.raw_fragment.should_equal ""
Test.specify "should return Syntax_Error when parsing invalid Uri" <|
Uri.parse "a b c" . catch <| case _ of
Uri.parse "a b c" . catch e-> case e of
Uri.Syntax_Error msg ->
msg.should_equal "Illegal character in path at index 1: a b c"
other ->

View File

@ -0,0 +1,24 @@
from Base import all
import Test
type My_Type a
spec = Test.group "Callables" <|
Test.specify "should be able to be applied in a pipeline using |>" <|
(1 |> *2) . should_equal 2
(2 |> My_Type) . should_equal (My_Type 2)
(2.3 |> .floor) . should_equal 2
Test.specify "should be able to be applied to an argument using <|" <|
(*2 <| 1) . should_equal 2
(My_Type <| 2) . should_equal (My_Type 2)
(.floor <| 2.3) . should_equal 2
Test.specify "should be able to be composed backward using <<" <|
(+1 << *2) 2 . should_equal 5
(My_Type << *2) 2 . should_equal <| My_Type 4
(.floor << *2.25) 2 . should_equal 4
Test.specify "should be able to be composed forward using >>" <|
(+1 >> *2) 2 . should_equal 6
(*2 >> My_Type) 2 . should_equal <| My_Type 4
(*2 >> .floor) 2.75 . should_equal 5

View File

@ -1,3 +1,5 @@
from Base import all
import Test
import Tests.Semantic.Deep_Export.Internal

View File

@ -1,3 +1,5 @@
from Base import all
import Test
import Tests.Semantic.Import_Loop.B

View File

@ -1,3 +1,5 @@
from Base import all
from Tests.Semantic.Names.Definitions import My_Type, Another_Constant
import Test

View File

@ -1,4 +1,5 @@
from Base import all
import Test
spec =
@ -13,7 +14,7 @@ spec =
contents.take_start 6 . should_equal [67, 117, 112, 99, 97, 107]
Test.specify "should handle exceptions when reading a non-existent file" <|
file = File.new "does_not_exist.txt"
successfully_failed = Panic.recover file.read . catch <| case _ of
successfully_failed = Panic.recover file.read . catch e-> case e of
File.No_Such_File_Error _ -> True
_ -> False
successfully_failed . should_be_true