Ok, I don’t know how to express this problem. But I have some (micro-)lenses created with template haskell. Depending on the position of the makeLens function call, the code compiles or not. The weird thing is that failure happens because a type synonym is out of scope, but if I move further the makeLens call, then It is in scope again.
I just recorded a gif because is too rare to explain.
I would understand that if suffixLenses create from sort of type and you depend on that type well… something could be wrong with respect code order… but St does not depend on suffixLenses
You have been bitten by splicing scope. Template Haskell declarations are grouped, then dependency analysis etc. is performed in the first group, then going to second group (where group one is now in scope) etc.
It is indeed very frustrating when you are used to plain old Haskell, i.e. moving declarations where they make sense instead of juggling with intra-file scope.
Disregarding its undeniable usefulness, Template Haskell will always feel like an afterthought when comparing it to other metalinguistic tools like Scheme/Lisp macros.
I’m having trouble imagining how a version of Template Haskell that retains all of the capabilities it currently has would be able to lose this restriction, honestly. Even Scheme macros can only use the environment that exists when they’re expanded.
With the way it has been designed you are probably right, that doesn’t mean that there are no other ways which might be more practical (but which might have their own drawbacks).
One can imagine a system where the file parses everything it can (skipping slices) and then reparse the slice accordingly or something.
Another approach would be to have a proper macro system which just expand code blindly (or as text) which is then parsed as Haskell.
I am sure there plenty of other possibilities.
CPP is only half supported. It is an external dependencies and it breaks Haskell multiline syntax.
What would like is typed as type-aware macro (which we don’t have).
A typed type aware version of CPP would not have the splice scope/restriction.
The problem is TH doesn’t have two stage (as a macro would have, first step expand the macro, second step parse it). Instead it mixes the parsing of the file and the expansion (to bring into scope the AST of what has been previously declared).
This is indeed nice (when you need it) but has he major drawback that it introduces “splicing scope”.
There are many instance when this no needed (i.e. when using TH as a macro) and it would be nice to not have the splicing scope issue when it is not necessary.
I think pretty much nobody is happy with TH “as it is”/nobody thinks it’s “fine”. It’s just that it took a huge amount of work to get it to the state it’s in/nobody can think of ways to improve it much without in effect scrapping it and starting again/an even huger amount of work. (There have been occasional mutterings about something completely different. The most annoying behaviour is that you have to compile the module containing your TH definition(s) before you can compile the module using them.)
It’s usually adequate for Lenses.
Are there other generic macro tools (more powerful than CPP) that would do better specifically with Haskell? (I think they wouldn’t be “between CPP and TH”. Rather they’d branch out in a different direction from CPP, not on a path to TH.)
You could still use it, I guess, but it fell out of use because typically you’re building a project with components in all sorts of source languages, cpphs doesn’t play so well with non-Haskell source.
CPP does require your #defines to appear before usages of the macro, so you still don’t get Haskell’s usual free ordering.