Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I came up against this same problem: I have a function like</p> <pre><code>someFunc :: Int -&gt; Int -&gt; Int -&gt; Int </code></pre> <p>What I'd love to do is make a magic function such that, for example</p> <pre><code>listApply :: [Int] -&gt; (Int -&gt; Int -&gt; Int -&gt; Int) -&gt; Int </code></pre> <p>such that I can say</p> <pre><code>listApply [1,2,3] someFunc </code></pre> <p>Instinctively it seems, and John's answer agrees, that it should be possible to do some type system magic in order to do this. There are solutions to similar problems involving making explicitly iso-recursive datatypes with a bunch of explicit rolling and unrolling (see, e.g., chapter 20 of Types and Programming Languages, or the 4th post in <a href="https://groups.google.com/group/fa.haskell/browse_frm/thread/f0a62b6de1416d8b" rel="nofollow">this thread</a>).</p> <p>I hacked on the type solution for a while; it feels possible, but I didn't quite get it working before deciding to try out Template Haskell, and there things are a lot friendlier.</p> <pre><code>{-# LANGUAGE TemplateHaskell #-} import Language.Haskell.TH import Language.Haskell.TH.Syntax lApply :: [Int] -&gt; String -&gt; ExpQ lApply [] fn = return $ VarE (mkName fn) lApply (l:ls) fn = [| $(lApply ls fn) l |] </code></pre> <p>(Remember to use the LANGUAGE pragma or the -XTemplateHaskell commandline switch.)</p> <p>To use this, you call lApply inside a splice like so:</p> <pre><code>&gt; $(lApply [1,2] "+") 3 </code></pre> <p>Note that I have to use a string containing the name of the function I want to call: I can't lift a function directly into an ExpQ, but I can refer to its global binding. I can see how this might get annoying. Also, because of the way we traverse the list, arguments must be presented in reverse order in the list.</p> <p>There are a few other wrinkles: in order to generalize this to other datatypes, those types have to have corresponding instances in the Lift class. For example, Double has no instance, but you can easily make one:</p> <pre><code>instance Lift Double where lift x = return $ LitE (RationalL (toRational x)) </code></pre> <p>The Lit datatype does not have a DoubleL constructor, but RationalL can be used in its place since it'll splice to a general member of the Fractional class.</p> <p>If you want to use this with functions that take a mix of types as arguments, you won't be able to pass a list, since lists cannot be of mixed types. You could use tuples to do this, which is honestly not much harder using Template Haskell. In that case, you'd make a function that generates the AST of a function that takes a tuple with the appropriate types inside and maps it to the function call you want. Alternatively, you might wrap your argument types inside an appropriately crafted ADT, which incidentally you could also create with Template Haskell. This is left as an exercise to the reader :)</p> <p>Finally, all of the standard Template Haskell limitations apply. For example, you can't call this function from the module where it's defined because of the GHC stage restriction.</p> <p>Template Haskell is fun and interesting stuff, but to be completely honest the iso-recursive datatype solution is probably a bit higher performance, and obviously doesn't require the additional use of TH. I'll come back and post a follow-up if/when I get that working :)</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload