There’s an expressiveness problem I’ve been fighting with for quite a while now. I had thought (and hoped, really) I might just be missing something obvious. But from discussions at JuliaCon, it seems at least @dpsanders, @jpfairbanks, and @ChrisRackauckas have had some similar problems.
I’ll try to boil the problem down to a simple case. There’s a risk in doing this I might reduce the problem to one that has an easy but non-generalizable solution, or that I might not quite align with others’ use cases. Anyway, here goes…
Say your library has a function
makecode that takes an
x::T and produces an
Expr. You’d like the user of your library to be able to pass their own
x (or more likely, one they build using other tools you provide) and generate fast code.
There have been a few suggestions that might address this in at least some situations:
- @ChrisRackauckas had earlier suggested there may be a way to use nested macro calls to do this. But at this point I may not have been understanding the problem well enough to describe it well, so I don’t know if this is still a candidate.
@mohamed82008 suggested expressing
Tin the type system, in order to use a generated function. This is an elegant solution, but type-level programming presents its own challenges. Even if this is the preferred approach, it would be great to be able to prototype DSLs more quickly than this approach allows.
- @tim.holy suggested (maybe jokingly?) that hacking the method table could help to get this working.
- The obvious goto (and my current approach) uses
invokelatest. This is generally discouraged.
It seems there must be a way, however hacky, to allow this to be done easily and efficiently. My understanding of
invokelatest had been that it creates a sort of boundary that’s expensive to cross but harmless otherwise.
But this seems not to be the case. Here’s a weird little example that attempts to abstract away some of my current workflow:
f(x) = quote function foo(a) a + $x end end function g(x) fx = f(x) quote $fx function h(start) s = start for j=1:10000 s += foo(j) end return s end end |> eval Base.invokelatest(h, x) end
Performance is… not great:
julia> @btime g(2) 7.831 ms (23946 allocations: 1.35 MiB) 50025002