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

Introduce an HpackException type, for Hpack exceptions #530

Closed
mpilgrem opened this issue Nov 21, 2022 · 6 comments
Closed

Introduce an HpackException type, for Hpack exceptions #530

mpilgrem opened this issue Nov 21, 2022 · 6 comments

Comments

@mpilgrem
Copy link
Collaborator

I have been looking at Stack's exceptions, as part of the Haskell Foundation's Haskell Error Index initiative. That has taken me to the Pantry library, and then from Pantry to Hpack.

At the moment, Hpack uses String as its exception type and ends with System.Exit.die :: String -> IO a. This prints an error message to stderr and then throws an uninformative exception (ExitFailure 1). I propose to introduce an HpackException type for Hpack exceptions, which would produce the error message when the exception is thrown. That type would allow users of the Hpack library to catch the exceptions and give a different output to the one Hpack chooses to deliver, if they wished.

I have a pull request that implements this proposal and preserves Hpack's current exception messages, which I will raise.

mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 21, 2022
The `Show` instance for `HpackException` preserves the existing error messages of all existing Hpack exceptions.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.ProgramName` because `Hpack.Exception` needs to import the type and `Hpack.Config` now imports `Hpack.Exception`.

Also bumps Hpack's `stack.yaml` to use lts-20.0 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 21, 2022
The `Show` instance for `HpackException` preserves the existing error messages of all existing Hpack exceptions.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn` as `HpackException` cannot be an instance of `Eq`.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.ProgramName` because `Hpack.Exception` needs to import the type and `Hpack.Config` now imports `Hpack.Exception`.

Also bumps Hpack's `stack.yaml` to use lts-20.0 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
@sol
Copy link
Owner

sol commented Nov 24, 2022

I have been looking at Stack's exceptions, as part of the Haskell Foundation's Haskell Error Index initiative. That has taken me to the Pantry library, and then from Pantry to Hpack.

For GHC I see value in having identifiable and stable error messages. In the past I've been parsing GHC error messages myself and both the fact that GHC pretty prints them (leading to variations in line breaks and indentation) and that the format is not stable across GHC versions is a pain.

For Hpack I don't have the insight to judge if effort spent here will provide benefits that amortize that effort.

However, from the perspective of a user of the Hpack API I still think it's reasonable to want access to parse errors etc.

At the moment, Hpack uses String as its exception type and ends with System.Exit.die :: String -> IO a. This prints an error message to stderr and then throws an uninformative exception (ExitFailure 1). I propose to introduce an HpackException type for Hpack exceptions, which would produce the error message when the exception is thrown. That type would allow users of the Hpack library to catch the exceptions and give a different output to the one Hpack chooses to deliver, if they wished.

Exceptions are for unexpected situations. That a user has a syntax error in package.yaml is not something out of the ordinary. For that reason I don't think that exceptions are necessarily the best option here. How about adding a new primitive to the API that returns IO (Either String Result) or IO (Either SomeErrorType Result)

@mpilgrem
Copy link
Collaborator Author

@sol, thanks. Currently, Hpack has (extracts):

type Errors = ExceptT String

readPackageConfig :: DecodeOptions -> IO (Either String DecodeResult)

and, where the action is yielded:

hpackResultWithVersion :: Version -> Options -> IO Result
hpackResultWithVersion v (Options options force generateHashStrategy toStdout) = do
  DecodeResult pkg (lines -> cabalVersion) cabalFileName warnings <- readPackageConfig options >>= either die return

That is, Hpack uses String as its exeception type and throws an exception (in hpackResultWithVersion) if there is a syntax error in package.yaml or some other 'exception'.

My pull request (#531) replaces that with:

type Errors = ExceptT HpackException

readPackageConfig :: DecodeOptions -> IO (Either HpackException DecodeResult)

and

hpackResultWithVersion :: Version -> Options -> IO Result
hpackResultWithVersion v (Options options force generateHashStrategy toStdout) = do
  DecodeResult pkg (lines -> cabalVersion) cabalFileName warnings <- readPackageConfig options >>= either throwIO return

that is, rather than hpackResultWithVersion: (1) sending an error message to stderr and throwing uninformative ExitFailure 1 (die); (2) it thows an informative exception (throwIO) - if that is not caught and handled, the exception will send exactly the same error message to stderr as in hpack-0.35.

@mpilgrem
Copy link
Collaborator Author

As an example of what end users experience, pantry-0.8.0 handles exceptions thrown by Hpack. Currently, a user of Stack would receive output like this:

D:\Users\mike\Code\Haskell\foo\package.yaml:61:0: could not find expected ':' while scanning a simple key
Error: [S-305]
Failed to generate a Cabal file using the Hpack library on file:
D:\Users\mike\Code\Haskell\foo\package.yaml

The exception encountered was:

ExitFailure 1

The first line is what Hpack outputs into stderr. The rest is Pantry's handling of the Hpack exception ExitFailure 1.

After pull request #531, the user of Stack would receive this output:

Error: [S-305]
Failed to generate a Cabal file using the Hpack library on file:
D:\Users\mike\Code\Haskell\foo\package.yaml

The exception encountered was:

D:\Users\mike\Code\Haskell\foo\package.yaml:61:0: could not find expected ':' while scanning a simple key

Hpack's message is conveyed in the informative exception.

@sol
Copy link
Owner

sol commented Nov 24, 2022

@mpilgrem I would love to discuss this at length, but the amount of time I'm able to spend on this is limited. For that reason we have to handle this efficiently.

I looked at your PR and I estimate that we will need five rounds of review to get this in shape. To avoid any further confusion I will leave a comment on your PR.

@sol
Copy link
Owner

sol commented Nov 24, 2022

(2) it thows an informative exception (throwIO)

That's what I'm suggesting instead:

How about adding a new primitive to the API that returns IO (Either String Result) or IO (Either SomeErrorType Result)

mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 25, 2022
The `Exception` instance for `HpackException` preserves the existing error messages of all existing Hpack exceptions in the `displayException` functions.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn` as `HpackException` cannot be an instance of `Eq`.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.ProgramName` because `Hpack.Exception` needs to import the type and `Hpack.Config` now imports `Hpack.Exception`.

Also bumps Hpack's `stack.yaml` to use lts-20.0 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 25, 2022
The `Exception` instance for `HpackException` preserves the existing error messages of all existing Hpack exceptions in the `displayException` functions.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn` as `HpackException` cannot be an instance of `Eq`.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.ProgramName` because `Hpack.Exception` needs to import the type and `Hpack.Config` now imports `Hpack.Exception`.

Also bumps Hpack's `stack.yaml` to use lts-20.0 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 25, 2022
The `Exception` instance for `HpackException` preserves the existing error messages of all existing Hpack exceptions in the `displayException` functions.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn` as `HpackException` cannot be an instance of `Eq`.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.ProgramName` because `Hpack.Exception` needs to import the type and `Hpack.Config` now imports `Hpack.Exception`.

Also bumps Hpack's `stack.yaml` to use lts-20.0 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 26, 2022
The `HpackError` type is an instance of `Eq`. Consequently, its data constructors involve only values of types that are instances of `Eq`.

The `renderHpackError :: ProgramName -> HpackError -> String` function preserves the existing error messages of all existing Hpack errors.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.Error` because `renderHpackError` makes use of the type.

Defines, and applies, `hpackProgName :: ProgramName`, for convenience.

The `DecodeOptions` constructor no longer needs its `decodeOptionsProgramName :: ProgramName` field (and `setProgramName` falls away). Its `decodeOptionsDecode` field is of type `FilePath -> IO (Either HpackError ([String], [Value]))`.

The module "Hpack" exports new function `hpackResultWithError :: Options -> IO (Either HpackError Result)`.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn`.

Also bumps Hpack's `stack.yaml` to use lts-20.1 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 26, 2022
The `HpackError` type is an instance of `Eq`. Consequently, its data constructors involve only values of types that are instances of `Eq`.

The `renderHpackError :: ProgramName -> HpackError -> String` function preserves the existing error messages of all existing Hpack errors.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.Error` because `renderHpackError` makes use of the type.

Defines, and applies, `hpackProgName :: ProgramName`, for convenience.

The `DecodeOptions` constructor no longer needs its `decodeOptionsProgramName :: ProgramName` field (and `setProgramName` falls away). Its `decodeOptionsDecode` field is of type `FilePath -> IO (Either HpackError ([String], [Value]))`.

The module "Hpack" exports new function `hpackResultWithError :: Options -> IO (Either HpackError Result)`.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn`.

Also bumps Hpack's `stack.yaml` to use lts-20.1 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 26, 2022
The `HpackError` type is an instance of `Eq`. Consequently, its data constructors involve only values of types that are instances of `Eq`.

The `renderHpackError :: ProgramName -> HpackError -> String` function preserves the existing error messages of all existing Hpack errors.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.Error` because `renderHpackError` makes use of the type.

Defines, and applies, `hpackProgName :: ProgramName`, for convenience.

The `DecodeOptions` constructor no longer needs its `decodeOptionsProgramName :: ProgramName` field (and `setProgramName` falls away). Its `decodeOptionsDecode` field is of type `FilePath -> IO (Either HpackError ([String], [Value]))`.

The module "Hpack" exports new function `hpackResultWithError :: Options -> IO (Either HpackError Result)`.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn`.

Also bumps Hpack's `stack.yaml` to use lts-20.1 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 26, 2022
The `HpackError` type is an instance of `Eq`. Consequently, its data constructors involve only values of types that are instances of `Eq`.

The `renderHpackError :: ProgramName -> HpackError -> String` function preserves the existing error messages of all existing Hpack errors.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.Error` because `renderHpackError` makes use of the type.

Defines, and applies, `hpackProgName :: ProgramName`, for convenience.

The `DecodeOptions` constructor no longer needs its `decodeOptionsProgramName :: ProgramName` field (and `setProgramName` falls away). Its `decodeOptionsDecode` field is of type `FilePath -> IO (Either HpackError ([String], [Value]))`.

The module "Hpack" exports new function `hpackResultWithError :: Options -> IO (Either HpackError Result)`.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn`.

Also bumps Hpack's `stack.yaml` to use lts-20.1 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to mpilgrem/hpack that referenced this issue Nov 26, 2022
The `HpackError` type is an instance of `Eq`. Consequently, its data constructors involve only values of types that are instances of `Eq`.

The `renderHpackError :: ProgramName -> HpackError -> String` function preserves the existing error messages of all existing Hpack errors.

Moves `ProgramName` from `Hpack.Config` to new module `Hpack.Error` because `renderHpackError` makes use of the type.

Defines, and applies, `hpackProgName :: ProgramName`, for convenience.

The `DecodeOptions` constructor no longer needs its `decodeOptionsProgramName :: ProgramName` field (and `setProgramName` falls away). Its `decodeOptionsDecode` field is of type `FilePath -> IO (Either HpackError ([String], [Value]))`.

The module "Hpack" exports new function `hpackResultWithError :: Options -> IO (Either HpackError Result)`.

Updates tests accordingly. Some tests use `shouldSatisfy` rather than `shouldReturn`.

Also bumps Hpack's `stack.yaml` to use lts-20.1 (GHC 9.2.5) rather than lts-15.11 (GHC 8.8.3).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 7, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/stack that referenced this issue Dec 7, 2022
For the context to this pull request, see see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds). See also [Pantry PR #74](commercialhaskell/pantry#74) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 7, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 7, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 7, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/stack that referenced this issue Dec 7, 2022
For the context to this pull request, see see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds). See also [Pantry PR #74](commercialhaskell/pantry#74) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 11, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 11, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 11, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 11, 2022
For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/stack that referenced this issue Dec 11, 2022
For the context to this pull request, see see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds). See also [Pantry PR #74](commercialhaskell/pantry#74) (on which this PR builds).
@sol
Copy link
Owner

sol commented Dec 11, 2022

Addressed by #535.

@sol sol closed this as completed Dec 11, 2022
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 12, 2022
Prettier Hpack error messages are enabled by hpack-0.35.1.

For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).

Also bumps from lts-20.0 to lts-20.4 (most recent GHC 9.2.5).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 12, 2022
Prettier Hpack error messages are enabled by hpack-0.35.1.

For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).

Also conforms form of messages of Errors [S-305] and [S-720].

Also bumps from lts-20.0 to lts-20.4 (most recent GHC 9.2.5).
mpilgrem added a commit to commercialhaskell/pantry that referenced this issue Dec 12, 2022
Prettier Hpack error messages are enabled by hpack-0.35.1.

For the context to this pull request see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds).

Also conforms form of messages of Errors [S-305] and [S-720] and the naming of arguments (`err`) in the `PantryException` instance of `Display`.

Also bumps from lts-20.0 to lts-20.4 (most recent GHC 9.2.5).
mpilgrem added a commit to commercialhaskell/stack that referenced this issue Dec 15, 2022
For the context to this pull request, see see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds). See also [Pantry PR #74](commercialhaskell/pantry#74) (on which this PR builds).
mpilgrem added a commit to commercialhaskell/stack that referenced this issue Dec 16, 2022
For the context to this pull request, see see [Hpack issue #530](sol/hpack#530), the [Hpack PR #531](sol/hpack#531) and the alternative [Hpack PR #535](sol/hpack#535) (on which this PR builds). See also [Pantry PR #74](commercialhaskell/pantry#74) (on which this PR builds).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants