-
Notifications
You must be signed in to change notification settings - Fork 375
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
Add %foreign_impl pragma for augmenting ffi functions #3303
Conversation
Very nice! Thanks for taking the time to implement this. |
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.
Really nice to have this. I haven’t played with it yet, but a couple of additional questions I think would be good to have answered and probably mentioned in the docs:
- Is redefinition of an existing backend allowed (foreign directive and foreign impl directive specifying the same backend)?
- What happens when two foreign impls collide (not a foreign def and new impl per (1) but rather two foreign impl directives)?
Currently, it just aggregates the directives. The most recently loaded module is first. I was on the fence on whether this should be an error as I can see a case for overriding the declaration in the library and possibly a case for a backend wanting more than one declaration for a given name. So I took the simpler path and the handling of duplicates is left to the backend. From auditing the code (I haven't experimented to verify):
There is a little bit of ambiguity here, if two TTC are loaded that have an override for the same backend, the behavior depends on the load order. I do check that the name we override/augment is a Aside from what is already implemented, if we want to detect these conflicts, we could either give a warning or an error when there is a duplicate. We could check:
|
I think this capability may be too useful not to merge even with some shortcomings (though non-determinism is particularly unfortunate). Long term, I wonder if foreign functions would be more ergonomic if an error for ambiguity could be given and a hide directive could be used to address that error. |
For now, I have added documentation for how duplicate declarations are handled. Note that it is also possible to introduce ambiguity with the existing compiler by doing: %foreign "javascript:foo"
"javascript:bar"
something : Nat -> Nat |
Description
Currently in Idris, we can't add a new backend to an existing
%foreign
definition without editing the library containing the definition. This issue came up for me when I wanted to useControl.App
in node and needed to stub outprim__fork
for thenode
codegen. But it also arises when working with a third party backend.In a discord discussion @stefan-hoeck suggested the following syntax to add additional bindings to an existing
%foreign
definition:This implementation of that syntax follows the pattern of
%foreign
and allows an indented block of expressions that evaluate to strings. The test case uses the pragma to makeControl.App
work with thenode
codegen.The general idea is to update the definition of the
%foreign
function to add the new bindings. We store the additional information inOptions
, accumulate it as TTC files are read in, and then update the defs inCompiler.Common
. We can't directly modify the definition when the pragma is evaluated because the context is dropped after compiling the module.Should this change go in the CHANGELOG?
implementation, I have updated
CHANGELOG_NEXT.md
(and potentially alsoCONTRIBUTORS.md
).