Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I was going to edit my other post, but this is big enough for its own.</p> <p>Here's one way of doing it with "type magic," but it feels to me like it's somewhat suboptimal, since it requires a lifting function that is specific to functions of a particular number of arguments (more below).</p> <p>Let's begin by defining a recursive datatype</p> <pre><code>data RecT a = RecR a | RecC (a -&gt; RecT a) </code></pre> <p>So variables of type RecT can either be just a wrapped result (RecR) or they can be a continued recursion (RecC).</p> <p>Now,how do we take something and cast it into type RecT a?</p> <p>Values are easy:</p> <pre><code>valRecT x = RecR x </code></pre> <p>RecR x is obviously of type RecT a.</p> <p>What about a function that takes one argument, like id?</p> <pre><code>idRecT x = RecC $ \x -&gt; RecR x </code></pre> <p>RecC wraps a function that takes a variable and returns type RecT a. The expression</p> <pre><code>\x -&gt; RecR x </code></pre> <p>is just such a function, since as we observed before RecR x is of type RecT a.</p> <p>More generally, any one-argument function can be lifted:</p> <pre><code>lift1RecT :: (a -&gt; a) -&gt; RecT a lift1RecT fn = RecC $ \a -&gt; RecR $ fn a </code></pre> <p>We can generalize this by repeatedly wrapping more deeply-nested function calls inside RecC:</p> <pre><code>lift2RecT :: (a -&gt; a -&gt; a) -&gt; RecT a lift2RecT fn = RecC $ \b -&gt; RecC $ \a -&gt; RecR $ fn b a lift3RecT :: (a -&gt; a -&gt; a -&gt; a) -&gt; RecT a lift3RecT fn = RecC $ \c -&gt; RecC $ \b -&gt; RecC $ \a -&gt; RecR $ fn c b a </code></pre> <p>OK, so we've done all this work to turn a function of an arbitrary number of arguments into a single type, RecT a. How do we use this?</p> <p>We can easily write down one level of function application:</p> <pre><code>reduceRecT :: RecT a -&gt; a -&gt; RecT a reduceRecT (RecC fn) = fn reduceRecT _ = undefined </code></pre> <p>In other words, reduceRecT takes an argument of type RecT a and another of type a and returns a new RecT a that's been one level reduced.</p> <p>We can also unroll a finished computation inside a RecT into the result:</p> <pre><code>unrollRecT :: RecT a -&gt; a unrollRecT (RecR fn) = fn unrollRecT _ = undefined </code></pre> <p>Now we're ready to apply a list of arguments to a function!</p> <pre><code>lApply :: [a] -&gt; RecT a -&gt; a lApply [] fn = unrollRecT fn lApply (l:ls) fn = lApply ls $ (reduceRecT fn) l </code></pre> <p>Let's consider the base case first: if we're finished with the computation, we just unwrap the result and return it. In the recursive case, we reduce the argument list by one, then transform the fn by applying the head of the list to the reduced fn, resulting in a new RecT a.</p> <p>Let's give this a try:</p> <pre><code>lApply [2,5] $ lift2RecT (**) &gt; 32.0 </code></pre> <p>So, advantages and disadvantages of this approach? Well, the Template Haskell version can do partial list application; this isn't true of the isorecursive type solution as presented here (though we could in principle fix this with some ugliness). The type solution also has the disadvantage of having a lot more boilerplate code associated with it: we need a listNRecT for all N that we want to use. Finally, it's a lot less easy to generalize this to the analogous tuple solution if we want to lApply to functions of mixed variable types.</p> <p>Of course, another interesting possibility is to enhance brevity by using Template Haskell to generate the listNRecT functions; this eliminates some boilerplate, but in a sense buys the disadvantages of both implementations.</p>
    singulars
    1. This table or related slice is empty.
    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.
    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