Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Lifting is more of a design pattern than a mathematical concept (although I expect someone around here will now refute me by showing how lifts are a category or something).</p> <p>Typically you have some data type with a parameter. Something like</p> <pre><code>data Foo a = Foo { ...stuff here ...} </code></pre> <p>Suppose you find that a lot of uses of <code>Foo</code> take numeric types (<code>Int</code>, <code>Double</code> etc) and you keep having to write code that unwraps these numbers, adds or multiplies them, and then wraps them back up. You can short-circuit this by writing the unwrap-and-wrap code once. This function is traditionally called a "lift" because it looks like this:</p> <pre><code>liftFoo2 :: (a -&gt; b -&gt; c) -&gt; Foo a -&gt; Foo b -&gt; Foo c </code></pre> <p>In other words you have a function which takes a two-argument function (such as the <code>(+)</code> operator) and turns it into the equivalent function for Foos.</p> <p>So now you can write</p> <pre><code>addFoo = liftFoo2 (+) </code></pre> <p><strong><em>Edit: more information</em></strong></p> <p>You can of course have <code>liftFoo3</code>, <code>liftFoo4</code> and so on. However this is often not necessary.</p> <p>Start with the observation</p> <pre><code>liftFoo1 :: (a -&gt; b) -&gt; Foo a -&gt; Foo b </code></pre> <p>But that is exactly the same as <code>fmap</code>. So rather than <code>liftFoo1</code> you would write</p> <pre><code>instance Functor Foo where fmap foo = ... </code></pre> <p>If you really want complete regularity you can then say</p> <pre><code>liftFoo1 = fmap </code></pre> <p>If you can make <code>Foo</code> into a functor, perhaps you can make it an applicative functor. In fact, if you can write <code>liftFoo2</code> then the applicative instance looks like this:</p> <pre><code>import Control.Applicative instance Applicative Foo where pure x = Foo $ ... -- Wrap 'x' inside a Foo. (&lt;*&gt;) = liftFoo2 ($) </code></pre> <p>The <code>(&lt;*&gt;)</code> operator for Foo has the type</p> <pre><code>(&lt;*&gt;) :: Foo (a -&gt; b) -&gt; Foo a -&gt; Foo b </code></pre> <p>It applies the wrapped function to the wrapped value. So if you can implement <code>liftFoo2</code> then you can write this in terms of it. Or you can implement it directly and not bother with <code>liftFoo2</code>, because the <code>Control.Applicative</code> module includes</p> <pre><code>liftA2 :: Applicative f =&gt; (a -&gt; b -&gt; c) -&gt; f a -&gt; f b -&gt; f c </code></pre> <p>and likewise there are <code>liftA</code> and <code>liftA3</code>. But you don't actually use them very often because there is another operator</p> <pre><code>(&lt;$&gt;) = fmap </code></pre> <p>This lets you write:</p> <pre><code>result = myFunction &lt;$&gt; arg1 &lt;*&gt; arg2 &lt;*&gt; arg3 &lt;*&gt; arg4 </code></pre> <p>The term <code>myFunction &lt;$&gt; arg1</code> returns a new function wrapped in Foo. This in turn can be applied to the next argument using <code>(&lt;*&gt;)</code>, and so on. So now instead of having a lift function for every arity, you just have a daisy chain of applicatives.</p>
 

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