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

Better error messages #722

Merged
merged 22 commits into from
Aug 30, 2019
Merged

Better error messages #722

merged 22 commits into from
Aug 30, 2019

Conversation

jaspervdj
Copy link
Owner

This resumes the excellent work done in #462

bergus and others added 18 commits August 4, 2016 16:08
Force templates to be consumed completely,
propagate error appropriately
...makes better error messages :-)

Notice the breaking change in 'applyElem', where $if(...)$ conditions
in templates now will throw errors if their field 'fail'ed (instead of just
being 'empty')!
...when a more important error prevails.

Also not throwing from 'if' conditions any more,
only logging those errors to the debug screen
* boolFields used outside of 'if'-conditions now get a "stack trace"
  using a new 'NoField' they don't have to rely on 'error' any more
* templates applied to their own file get proper description
  (did use incompatible paths/identifiers before)
* renamed 'compilerFail' to more descriptive name
Closes #507 (actually was fixed by 9ec43a6 already, this just adds the test)
See #462 (comment)
and below for detailed explanation

Also abstracted out `testCompilerError` in the test suite,
and added a `compilerTry` that is much easier to use (and specifically, to
branch on) than `compilerCatch`
@jaspervdj
Copy link
Owner Author

@bergus I'm pretty keen to merge this in soon. Do you think you can give it another review? I made a few changes to your code -- most of it just a number of renames, but I also changed the new error type to use NonEmpty in case there are any actual failures.

@bergus
Copy link
Contributor

bergus commented Aug 25, 2019

Sure, I'll have a look. Haven't worked with Hakyll for some time now, though a project relying on #620 is still in my backlog...

@@ -132,7 +135,7 @@ data CompilerResult a
= CompilerDone a CompilerWrite
| CompilerSnapshot Snapshot (Compiler a)
| CompilerRequire (Identifier, Snapshot) (Compiler a)
| CompilerError (Reason [String])
| CompilerError (CompilerErrors String)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I did use the name Reason, (Compiler)Errors looks just weird here.

| NoCompilationResult a
data CompilerErrors a
-- | One or more exceptions occured during compilation
= CompilationFailure (NonEmpty a)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your specific reasoning to avoid empty lists here? And why did compilerThrow change to produce a CompilationNoResult if no reason value is given?
(Not assuming it's a bad thing, I just don't understand the idea behind it).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also seems to make the Alternative.<|> implementation more complicated

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's to prevent us from shooting ourselves in the foot -- if there's no error, we shouldn't get into a CompilationFailure scenario. This is not true for the other case where we have empty.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After familiarising myself with the code again, I would heavily prefer

-- | Put a 'CompilerError' with  multiple error messages as 'CompilationFailure'
compilerThrow :: NonEmpty String -> Compiler a
compilerThrow = compilerResult . CompilerError . CompilationFailure

The compilerThrow function is an internal function (only available on Compiler.Internal) so we can easily "break" it by changing the type. (IIRC it was earlier used in Context or so for transforming errors, but has been replaced there with more sensible alternatives). It never was meant to be called with an empty list, and neither in fail nor in <|> that could happen.
By having compilerThrow :: NonEmpty String -> Compiler a, we don't need to convert back and forth between lists and non-empty lists in <|>.

Where it does matter, and where I agree with you that we should prevent anyone from shooting themselves in the foot, is throwError in the MonadError [String] instance. Let's do

throwError errs = maybe (compilerNoResult errs) compilerThrow $ NonEmpty.nonEmpty errs

renderFeed :: String -- ^ Default feed template
-> String -- ^ Default item template
renderFeed :: Item String -- ^ Default feed template
-> Item String -- ^ Default item template
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my remaining nitpick about the PR -- changing this breaks backwards-compatibility, which is fine, since this is a relatively rarely used function so it's worth it. However, if we do change it, we should change it to the "right" type which is Template or Item Template rather than Item String.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Template is fine, but it shouldn't be a template constructed by readTemplate which doesn't know about the origin of the template file (it just assumes a string literal), which is important for error messages. Doing the compileTemplateItem did allow me to use the Items path for the template, which I had explicitly added, without having to use any of the Template.Internal stuff to construct this "embedded Template".

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bergus That makes a lot of sense -- what do you think of just providing embedTemplate from the template modules then? It has the filepath that way, and simplifies the Feed code: f96b99e

let source = T.unpack $ T.decodeUtf8 $(embedFile filePath) in
case parseTemplateElemsFile filePath source of
Left err -> error err
Right tpl -> template filePath tpl |]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is how to do it properly with Template Haskell - thanks!

@jaspervdj jaspervdj merged commit 036c583 into master Aug 30, 2019
@jaspervdj jaspervdj deleted the error-messages branch August 30, 2019 09:46
@jaspervdj jaspervdj mentioned this pull request Aug 30, 2019
@LaurentRDC LaurentRDC mentioned this pull request Nov 4, 2024
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

Successfully merging this pull request may close these issues.

2 participants