Defining a function and using it in a splice within the same module
Hi, The user guide from latest GHC release makes clear that doing what I wrote in the subject of this message is not possible:
From 7.6:
"You can only run a function at compile time if it is imported from another module. That is, you can't define a function in a module, and call it from within a splice in the same module." Unfortunately I have encountered this problem more times than I'd like while using TH to implement a DSL. My usual solution is to generate a function to process the extra arguments which cannot be passed to the splice (i.e. push the arguments out of the splice so that I can use functions defined in the same module). Nevertheless, I'd like to know if there are any chances of seeing this problem solved anytime soon. Thanks, Alfonso
| "You can only run a function at compile time if it is imported from | another module. That is, you can't define a function in a module, and | call it from within a splice in the same module." | | Unfortunately I have encountered this problem more times than I'd like | while using TH to implement a DSL. My usual solution is to generate a | function to process the extra arguments which cannot be passed to the | splice (i.e. push the arguments out of the splice so that I can use | functions defined in the same module). I don't quite understand your workaround. Can you describe it a bit more? | Nevertheless, I'd like to know if there are any chances of seeing this | problem solved anytime soon. Here's why it's hard. It involves compiling the functions *before* the splice to bytecode, so that they can be run. But we don't always want to do that! Usually we do not run any of these functions, and we want to compile them to object code. I don't think it's acceptable to *always* compile *everything* to bytecode, just in case. So the trickiness is simply the plumbing required to notice that f is called from inside a splice, so we'd better compile it to bytecode early. Oh, and the transitive closure of things called by f. A possible approximation is: see if *anything* defined in this module is called from within a splice, and if so compile *everything* to bytecode, just in case. I doubt I'll get to this soon. Is anyone else interested in working on it? I've created a ticket for it though: http://hackage.haskell.org/trac/ghc/ticket/1800 If there is more to add (e.g. details of your workaround), add commments to that ticket so we accumulate the lore in one place. Simon
On 10/24/07, Simon Peyton-Jones <simonpj@microsoft.com> wrote:
| "You can only run a function at compile time if it is imported from | another module. That is, you can't define a function in a module, and | call it from within a splice in the same module." | | Unfortunately I have encountered this problem more times than I'd like | while using TH to implement a DSL. My usual solution is to generate a | function to process the extra arguments which cannot be passed to the | splice (i.e. push the arguments out of the splice so that I can use | functions defined in the same module).
I don't quite understand your workaround. Can you describe it a bit more?
Well, it doesn't solve the issue of executing things at compile-time, it simply delays their execution until proper runtime. As a (simplified) example, in my System Modelling DSL I have to create system definition. That is done by providing the function Name together with the identifiers of its inputs and outputs. inputs = ["in1", "in2"] outputs = ["out1", "out2"] f :: Signal Int -> Signal Int -> (Signal Int, Signal Int) f = ... Since it is not possible to do system :: SysDef system = $(mkSysDef 'f inputs outputs) due to the limitation we mentioned I simply do system = $(mkSysDef 'f) inputs outputs where mkSysDef :: Name -> [String] -> [String] -> SysDef My solution does not solve the problem. It rather bypasses it. Furthermore, mkSysDef unfortunately cannot do static checking (for example, it cannot make sure that all the inputs and outputs have different identifiers). However, that's better than asking the user to create the system definitions and identifiers in different modules.
A possible approximation is: see if *anything* defined in this module is called from within a splice, and if so compile *everything* to bytecode, just in case.
Excuse my ignorance (I'm not familiar with the internals of GHC) but, wouldn't it be possible to compute a dependency graph for each splice before executing them? Based on that, the compiler could decide whether to generate bytecode for a function or not.
If there is more to add (e.g. details of your workaround), add commments to that ticket so we accumulate the lore in one place.
Since what I propossed is more "bypassing the problem" than a workaround I don't know if its significant for the ticket.
| As a (simplified) example, in my System Modelling DSL I have to create | system definition. That is done by providing the function Name | together with the identifiers of its inputs and outputs. | | inputs = ["in1", "in2"] | | outputs = ["out1", "out2"] | | f :: Signal Int -> Signal Int -> (Signal Int, Signal Int) | f = ... | | Since it is not possible to do | | system :: SysDef | system = $(mkSysDef 'f inputs outputs) | | due to the limitation we mentioned | | I simply do | | system = $(mkSysDef 'f) inputs outputs | | where mkSysDef :: Name -> [String] -> [String] -> SysDef Ah I see. Although you presumably meant mkSysDef :: Name -> Q ([String] -> [String] -> SysDef) else it'd be ill-typed. | Excuse my ignorance (I'm not familiar with the internals of GHC) but, | wouldn't it be possible to compute a dependency graph for each splice | before executing them? Based on that, the compiler could decide | whether to generate bytecode for a function or not. Indeed it could. It's just a bit more complicated. | Since what I propossed is more "bypassing the problem" than a | workaround I don't know if its significant for the ticket. It's a nice illustration of why the proposed feature is useful. I'd be happy if you added it. Simon
On 10/25/07, Simon Peyton-Jones <simonpj@microsoft.com> wrote:
| where mkSysDef :: Name -> [String] -> [String] -> SysDef
Ah I see. Although you presumably meant mkSysDef :: Name -> Q ([String] -> [String] -> SysDef) else it'd be ill-typed.
Yes, sure, sorry for the typo.
| Since what I propossed is more "bypassing the problem" than a | workaround I don't know if its significant for the ticket.
It's a nice illustration of why the proposed feature is useful. I'd be happy if you added it.
I'll add it then. Thanks, Fons
On 10/25/07, Alfonso Acosta <alfonso.acosta@gmail.com> wrote:
On 10/25/07, Simon Peyton-Jones <simonpj@microsoft.com> wrote:
| where mkSysDef :: Name -> [String] -> [String] -> SysDef
Ah I see. Although you presumably meant mkSysDef :: Name -> Q ([String] -> [String] -> SysDef) else it'd be ill-typed.
Yes, sure, sorry for the typo.
Actually mkSysDef :: Name -> Q ([String] -> [String] -> SysDef) would also be ill-typed, it should be mkSysDef :: Name -> ExpQ.
It's a nice illustration of why the proposed feature is useful. I'd be happy if you added it.
I'll add it then.
done
participants (2)
-
Alfonso Acosta -
Simon Peyton-Jones