Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Single place to handle errors in a function #337

Closed
sameerajayasoma opened this issue Sep 18, 2019 · 8 comments
Closed

Single place to handle errors in a function #337

sameerajayasoma opened this issue Sep 18, 2019 · 8 comments
Assignees
Labels
design/usability Design does not work well for some tasks implementation/inprogress Implementation inprogress lang Relates to the Ballerina language specification

Comments

@sameerajayasoma
Copy link
Contributor

Consider the following program. My aim is to provide a public API to retrieve sunrise/sunset times of a given city. The function sunriseSunsetInternal has to the logic, but it does contain many check operator usages, simply because I don't want to handle errors at every line. That would make this unreadable. I also want to give a proper error for all my API users. This is the only approach that I can think of.

Can we think of an "error handler" concept? This has been proposed in Go 2 draft as well.

import ballerina/config;
import ballerina/http;
import ballerina/io;

var appId = <@untainted>config:getAsString("OPEN_WEATHER_API_KEY");
http:Client openWeatherEp = new ("http://api.openweathermap.org/data/2.5");
http:Client sunriseSunsetEp = new ("http://api.sunrise-sunset.org");

public function sunriseSunset(string city) returns json | error {
    var result = sunriseSunsetInternal(city);
    if result is error {
        return error("RetrievingSunriseTimeFailed",
            message = string `Failed to retrieve sunrise/sunset times: ${city}`,
            cause = result
        );
    } else {
        return result;
    }
}

function sunriseSunsetInternal(string city) returns json | error {
    // Do a GET on OpenWeather API resource
    string weatherResPath = <@untainted>string `/weather?q=${city}&appid=${appId}`;
    http:Response weatherResp = check openWeatherEp->get(weatherResPath);
    json weatherPayload = check weatherResp.getJsonPayload();

    // The following line fails with the error: invalid literal for type 'tuple binding pattern'
    [float, float][longitude, latitude] = [<float>weatherPayload.coord.lon, <float>weatherPayload.coord.lat];

    // Do a GET on sunrise sunset API resource
    string sunriseResPath = <@untainted>string `/json?lat=${latitude}&lng=${longitude}`;
    http:Response sunriseResp = check sunriseSunsetEp->get(sunriseResPath);
    json sunrisePayload = check sunriseResp.getJsonPayload();

    // Create the response payload
    return {
        city: city,
        sunrise:check sunrisePayload.results.sunrise,
        sunset:check sunrisePayload.results.sunset
    };
}
@sameerajayasoma sameerajayasoma added lang Relates to the Ballerina language specification design/usability Design does not work well for some tasks labels Sep 18, 2019
@jclark jclark self-assigned this Sep 19, 2019
@jclark jclark added this to the 2020R1 milestone Sep 19, 2019
@jclark
Copy link
Collaborator

jclark commented Sep 19, 2019

I agree that we should find a way to handle this case better.

This is the Go 2 draft, which has lots of useful discussion.

The are a number of things to keep in mind here:

  • How do we want to represent errors visually (as part of the sequence diagram)? One idea is a separate error "track".
  • How do we enable easy cleanup on function return (Go does this with its defer statement)? This is issue Easier cleanup on function return #334.
  • Do we need more flexible ways to trap panics? What relationship do we want between error handling and panic handling?
  • What relationship do we want between check and return? If what check does with an error just return (as it is now)?
  • Should we do some sort of error lifting? This is issue Generalized support for error lifting #195.
  • How do we deal with transaction abort/rollback? This is part of issue Stabilize transactions support #267.

@jclark
Copy link
Collaborator

jclark commented Sep 19, 2019

Unfortunately, we have used up the word handle for a type, so we cannot simple adopt Go's design, even if we wanted to.

One possibility is something like Swift's do/catch. Swift's try is similar to our check. Note that although this superficially looks like exception handling, it isn't (as the Go2 error draft explains): the flow of control is explicit.

@bruno-garcia
Copy link

bruno-garcia commented Nov 29, 2019

I've made a comment in the lang repo but I guess this is the right place:

Ideally this would be made available to dependencies so services like sentry.io can hook and capture per/request and/or global errors.

In comparison, this is how one can extend an ASP.NET Core application with an extension method made available by Sentry's package:

https://github.com/getsentry/sentry-dotnet/blob/f4f9c908183f462b6ed6e703ae77e3e5093b3d61/samples/Sentry.Samples.AspNetCore.Mvc/Program.cs#L22

The framework offers hooks that can be used from that simple code change done by the application developer.

@jclark
Copy link
Collaborator

jclark commented Jul 9, 2020

The transaction proposal change to check semantics provides a way to solve this

@mohanvive
Copy link

mohanvive commented Jul 20, 2020

@jclark can you please provide more details on what block statements are going to support the on-fail clause. Below are the block statements that I could think of,

  • do clause
  • transaction statement
  • lock

Do we need to consider below block statements as well?

  • retry
  • foreach
  • while

What are the principles that we need to consider when deciding the block statements?

@jclark
Copy link
Collaborator

jclark commented Jul 20, 2020

The principle is that it should work for any block statement that has the syntactic form

keyword keyword-dependent-syntax {
  statements
}

unless it wouldn’t make sense or would be syntactically confusing. So, for example, it should not work for if because it would get confusing with if/else. I agree it should work with the three you mention (retry, foreach, while). I would also add match (although it isn’t exactly that form).

@hasithaa hasithaa added the implementation/inprogress Implementation inprogress label Aug 9, 2020
@jclark jclark added the status/pending Design is agreed and waiting to be added label Aug 14, 2020
@jclark
Copy link
Collaborator

jclark commented Aug 14, 2020

Note that fail is a statement.

jclark added a commit that referenced this issue Aug 18, 2020
Grammar changes done.
Rearrange statement subsections.
Part of #337.
@jclark jclark added status/inprogress Fixes are in the process of being added and removed status/pending Design is agreed and waiting to be added labels Aug 18, 2020
@jclark
Copy link
Collaborator

jclark commented Aug 18, 2020

Grammar changes now in.

@jclark jclark closed this as completed in d3433ed Aug 22, 2020
@jclark jclark removed the status/inprogress Fixes are in the process of being added label Aug 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design/usability Design does not work well for some tasks implementation/inprogress Implementation inprogress lang Relates to the Ballerina language specification
Projects
None yet
Development

No branches or pull requests

5 participants