-
Notifications
You must be signed in to change notification settings - Fork 33
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
Remote imports #16
Remote imports #16
Conversation
@Gabriel439 I think I am almost done with this - A little smoke testing, one more review of tests, code, documentation and so on; That's all what is left to do. Given that this topic involved a fair amount of design decisions I'd highly appreciate your comments on this PR 🙂 |
Also added HLint configuration file.
Usable as an entrypoint for custom interpreters.
Also improved documentation a lot.
4c0b825
to
2430687
Compare
@Gabriel439 The last commits implement the ideas from #16 (comment). Please let me know if that is closer to the API you envision. |
Yet another approach for the configuration topic is here: https://github.com/mmhat/grace/tree/remote-imports-backpack The idea is as follows:
TBH the main reason for this experiment is that I always wanted to play around with Backpack 😄 |
Regarding the backpack solution, I still think it's a non-goal of this project to support package-level or API-level configurability, because I don't plan on publishing this project to Hackage. The intended user experience is that people will fork this project and edit it to implement features The first reason why is to keep the implementation clearer for teaching purposes. The second reason why is because many desirable language features cannot (easily) be implemented by exposing a customizable interface. As an example of the latter reason: in #17 you mentioned the introduction of a The way I think about this project is that the source code is the API, not the actual Haskell API. |
@Gabriel439 Aaaah, okay! Thank you very much for clarification! 👍 Anyway, I will prepare this PR such that it does not have a configurable API and mark the respective parts as entrypoints for customization. |
@Gabriel439 Ok, this is now more in the spirit of this project: The interpreter does not take a callback function for URI resolving anymore and the |
Also updated documentation of env resolver
Co-authored-by: Gabriella Gonzalez <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great! Just a few last suggestions
grace-core/src/Grace/Interpret.hs
Outdated
tryAny (Import.resolverToCallback Resolver.defaultResolver uri) | ||
|
||
(importExpression, directory') <- case eitherResult of | ||
Left e -> throwError (ImportError uri (displayException e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you will want to store the exception and not the rendered form of the exception, like this:
Left e -> throwError (ImportError uri (displayException e)) | |
Left e -> throwError (ImportError uri e) |
The main reason I suggest this is to preserve as much structure as possible from the original exception. Instead, render the underlying exception at the last moment (i.e. have the displayException
method of ImportError
call displayException
for the wrapped exception).
The second reason that I suggest this is that you probably want the Show
instance for ImportError
to Show
(and not display…
) the underlying exception
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I totally agree with you; The reason I render the message already there is the Eq
instance of ImportError
. Sadly we have to choose between one of the two. What is your preference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is still doable if you use ExistentialQuantification
, like this:
data InterpretError
= forall e . (Exception e, Eq e) => ImportError URI e
| …
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that we use tryAny
to catch the exception, hence e :: SomeException
which does not have an Eq
instance. We could however change InterpretError
to
data InterpretError
= ImportError URI SomeException
and define the Eq
instance like
instance Eq InterpretError where
ImportError uri1 e1 == InterpretError uri2 e2 = uri1 == uri2 && show e1 == show e2
It is not a pretty "solution" but since the Eq
instance is only intended to be used in the test suite it could work.
As an alternative we can add another constructor to Grace.Import.ImportError
:
data ImportError
= forall e. (Eq e, Exception e) => SomeImportError e
| UnsupportedURI
Then we leave it to the resolvers to wrap their errors (and the one of the libraries they are using) and catch only this error in interpretExprWith
.
* Consolidate `Import` and `Input` types into a single type for uniformity * Fuse `parseInput` logic and resolver logic * Consolidate `interpretWith` and `interpretWithExpr`
Consolidate Input and Import types
I think this looks great! I think I can put up a PR of my own with the remaining proposed change for |
Thank you! 🚀 Regarding |
This PR adds the basic support for remote imports. That means:
newtype Resolver = Resolver { runResolver :: Text.URI.URI -> IO (Maybe Text) }
.A resolver receives the URL as an argument and shall return
Just code
if can process the URL (e.g. an HTTP resolver can resolvehttp://
URLs but notfile://
URLs.) andNothing
it not.interpretWith
ormainWith
- take a value of typeGrace.Interpret.InterpretSettings
. Part of those settings is a list of resolvers which are tried one after another.grace-core
includes two simple resolvers:env://
URLs.file://
URLs that is basically equivalent to the filepath-based imports.grace-resolver-<scheme> ${URL}
. If such an executable is not found in$PATH
the resolver fails, i.e. does not match the URL passed to it.