Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>It's good practice to be thinking about code reuse this way, and I'll get to that, but first:</p> <p><strong>Using function composition to write more neatly</strong></p> <p>Can I first point out as an aside that I would define your <code>oddOf</code> function more simply using existing functions:</p> <pre><code>oddOf :: Eq a -&gt; a -&gt; [a] -&gt; Bool oddOf value list = odd . length . filter (== value) $ list </code></pre> <p><code>f $ x = f x</code> but has low precedence, so that's a neater way of writing <code>(not . even . length . filter (== value) ) list</code>.</p> <p>This works by composing functions; we take the list and filter it so we get just the ones that equal the <code>value</code>, using partial application of <code>(==)</code> in the form of an 'operator section' <code>(== value) :: Eq a =&gt; a -&gt; Bool</code>. Next we find the <code>length</code> of that, check if it's <code>even</code>, then finally negate the answer with <code>not</code>. This hints at a concise way of writing <code>evenOf</code>:</p> <pre><code>evenOf :: Eq a -&gt; a -&gt; [a] -&gt; Bool evenOf value list = even . length . filter (== value) $ list </code></pre> <p>In fact, since the prelude defines <code>odd</code> as <code>not . even</code> it would be very slightly simpler to start with <code>evenOf</code> and define <code>oddOf</code> from that.</p> <p><strong>Why <code>not . oddOf</code> isn't what you wanted</strong></p> <p>Looking at types, you have</p> <pre><code>not :: Bool -&gt; Bool oddOf :: Eq a =&gt; a -&gt; [a] -&gt; Bool (.) :: (b -&gt; bb) -&gt; (a -&gt; b) -&gt; a -&gt; bb -- function composition </code></pre> <p>Now <code>-&gt;</code> associates to the right, which means that really,</p> <pre><code>oddOf :: Eq a =&gt; a -&gt; ([a] -&gt; Bool) </code></pre> <p>Secretly, that means that Haskell functions only ever take one argument! (What we think of as functions that take more than one argument are actually functions that take one argument then return a function that'll take another, etc...) Unfortunately that means we can't match the types up with <code>(.)</code> directly, because we'd need <code>b</code> to be <code>Bool</code>, not <code>[a] -&gt; Bool</code>.</p> <h2>Simple solutions to your problem</h2> <p>OK, sorry I took a while to get to the answer you wanted, but I think that was all worth saying.</p> <ul> <li><p><strong>Simple solution 1</strong> is writing <code>evenOf</code> with function composition as I showed you above. </p> <pre><code>oddOf value list = odd . length . filter (== value) $ list evenOf value list = even . length . filter (== value) $ list </code></pre></li> <li><p><strong>Simple solution 2</strong> is writing <code>evenOf</code> directly from <code>oddOf</code> by supplying all the arguments:</p> <pre><code>evenOf value list = not $ oddOf value list oddOf value list = odd . length . filter (== value) $ list </code></pre></li> <li><p><strong>Simple solution 3</strong> is writing <code>evenOf value</code> by composing <code>not</code> with <code>oddOf value</code></p> <p>The only problem with <code>not.oddOf</code> was that <code>oddOf</code> doesn't return a <code>Bool</code> directly, it returns a <code>[a]-&gt;Bool</code>. We can fix this by supplying one of the arguments. Notice that <code>oddOf value</code> has type <code>Eq a =&gt; [a] -&gt; Bool</code> so we <em>can</em> compose that with <code>not</code>, because it <em>does</em> return a <code>Bool</code>:</p> <pre><code>evenOf value = not . oddOf value oddOf value list = odd . length . filter (== value) $ list </code></pre></li> <li><p><strong>Simple solution 4</strong> is putting together simple solutions 1 and 2 to write <code>oddOf</code> using <code>evenOf</code> instead:</p> <pre><code>oddOf value list = not $ evenOf value list evenOf value list = even . length . filter (== value) $ list </code></pre> <p>(You could, of course, do the same thing to simple solutions 1 and 3.)</p></li> </ul> <h1>Awesome brain-changing ways to solve the problem</h1> <p>Every Haskell programmer should read Conal Elliott's excellent <a href="http://conal.net/blog/posts/semantic-editor-combinators">semantic editor combinators webpage/article</a>.</p> <p>In particular, you should read it since it answers your question title "Defining Functions In Relation To Other Functions Without Considering Implementation Details" in a very general way; "Semantic Editor Combinators" is Conal's phrase for exactly that concept.</p> <p>The main idea is that you can edit a function after it's written by writing a sort of path to the value you want to change in brackets, the function you want to apply at that point, then the original function. In this case, you need to apply <code>not</code> to the result (<code>::Bool</code>) of the result (<code>::Eq a =&gt; [a]-&gt;Bool</code>) of the original function (<code>:: Eq a =&gt; a -&gt; [a] -&gt; Bool</code>). </p> <p>So if you want to edit the result of the result with <code>not</code>, you do:</p> <pre><code>oddOf = (result.result) not evenOf evenOf value list = even . length . filter (== value) $ list </code></pre> <p>Conal Elliott defines <code>result = (.)</code> because it's conceptually easier, but you could define</p> <pre><code>oddOf = ((.).(.)) not evenOf evenOf value list = even . length . filter (== value) $ list </code></pre> <p>directly if you prefer.</p> <p>If you needed to change <code>more :: a -&gt; b -&gt; [a] -&gt; [b] -&gt; Bool</code>, you could use</p> <pre><code>less = (result.result.result.result) not more </code></pre> <p>or</p> <pre><code>less = ((.).(.).(.).(.)) not more </code></pre> <p>Using the same idea, you can change inside a list, part of a pair, one of the arguments,...<br> Read the paper for a deeper and fuller explanation.</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. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      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