I’m currently hooking into parsedResultAction; is that not the right place to do this? Do I need to use renamedResultAction? I think that would work too, as long as it’s before the typechecking phase
What fixity do you have declared for foo? (Remember " Fixity is a property of a particular entity (constructor or variable), just like its type; fixity is not a property of that entity’s name ." – Report 4.4.2 – which has some counter-intuitive examples.)
No juggling the binding happens after parsing, precisely because fixity depends on the entity (dictionary look-up). So in your example, at first $ and foo are ‘on the same level’.
I haven’t declared any fixity for foo, so I should do that.
But even with a fixity defined for foo, your last paragraph sounds like it won’t fix the problem. It looks like -ddump-rn-ast shows the right fixity; so I should use renamedResultAction instead?
Emmm beyond my pay-scale to comment on that, sorry. ‘Juggling the binding’ must happen before type-checking, otherwise foo would get a left arg putStrLn $ 1, which would (presumably) be ill-typed. And type-checking must happen after dictionary look-up to get the entity’s type. renamedResult sounds more like it: looking up the dictionary resolves to the name’s in-scope declaration.
I vaguely remember something like “operator applications are initially parsed left to right and only adjusted to match their fixities in a later pass”.
Yeah, the output of the GHC parser does not take fixities into account, things get reassociated during renaming (this happens in mkOppAppRn).
That’s why e.g. in Ormolu (and hence also in Fourmolu), we have ad-hoc heuristics to find out what the fixity of a name is (both via an embedded list based on operators defined on Hackage, as well as user input), and then reassociate operator trees appropriately.
Lots of other tools also have the same underlying problem (hlint, apply-refact, retrie); there are a few approaches how to make handling of operator fixities easier for external tools:
Rely on GHC to provide the fixities somehow. This could happen via HLS or by parsing .hie files.
Most obvious problem here is that you can now only (correctly) format/lint your code if it can be renamed; which has its own can of worms.
But for a compiler plugin, using the renamed output should solve this problem