You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
<p>In the <ahref="#6d247327cf214fa698dc9ec29693bd6f">Lazy monoids as applicative operations</a> section, you mention "<code>Apply</code> overloads".
424
+
In pondering what those might look like, I found the two examples in the <ahref="https://blog.ploeh.dk/2018/10/01/applicative-functors/#cef395ee19644f30bfd1ad7a84b6f912">A C# perspective</a>
425
+
section of your post on applicative functors, and from those, drew the conclusion that <code>Apply</code> for <code>Lazy<T></code> would look like this:</p>
You want to be able to use <code>Apply</code> against a two-argument function, and since C# doesn't curry functions, we've got a couple of options. We could define an additional <code>Apply</code> overload to handle
436
+
two-argument functions. But since we're going to need to wrap up the function in a <code>Lazy</code> anyway, we could just curry while doing that:
<spanstyle="color:blue;">return</span> <spanstyle="color:blue;">new</span> <spanstyle="color:#2b91af;">Lazy</span><<spanstyle="color:#2b91af;">Func</span><<spanstyle="color:blue;">int</span>, <spanstyle="color:#2b91af;">Func</span><<spanstyle="color:blue;">int</span>, <spanstyle="color:blue;">int</span>>>>(x => y => f(x, y)).Apply(x).Apply(y);
440
+
</pre>
441
+
<p>
442
+
That seems in line not only with your formulation in the article on applicative functors, but also with how they look in Haskell: <code>Applicative</code> defines the first argument to <code><*></code> as being <code>f (a -> b)</code>.
443
+
In other words, the first argument to <code>Apply</code> is not a function, it's a function wrapped in a functor. In Haskell, that means doing something like this:
But that's apparently not what you've done here. You've called <code>Apply</code> directly on a function (and not a function wrapped in <code>Lazy</code>). But isn't the point of applicative functors that it's not just values that are wrapped up in
449
+
a functor: so, too, are the functions we might want to map over them, right? If you merely want to map a bare function over a functor, then you can just do that with <code>fmap</code> (or <code>Select</code>, as we normally call it in .NET).
450
+
(That said, your definition is consistent with <code>liftA2</code>. But my understanding is that the ability to define an applicative
451
+
in terms of <code>liftA2</code> is an optimization rather than a fundamental feature; originally only <code><*></code> was part of the typeclass, with <code>liftA2</code> being defined in terms of <code><*></code> and <code>fmap</code>.)
452
+
</p>
453
+
<p>
454
+
Not that <code>Select</code> alone would be sufficient here. If you are starting with an unwrapped function, you'd use <code>Select</code> to apply the first argument, but the result would be a <code>Lazy<Func<int, int>></code>,
455
+
so you would actually need to use <code>Apply</code> for the second step. So you'd end up with this oddity:
<spanstyle="color:#2b91af;">Func</span><<spanstyle="color:blue;">int</span>, <spanstyle="color:#2b91af;">Func</span><<spanstyle="color:blue;">int</span>, <spanstyle="color:blue;">int</span>>> curried = x => y => f(x, y);
460
+
return curried.Select(x).Apply(y);
461
+
</pre>
462
+
463
+
<p>
464
+
In Haskell, that would be equivalent to <code>((fmap (+)) (Just 10)) <*> (Just 32)</code>. Just like in the C# version, I'm using a plain function (<code>(+)</code>), not wrapped in a functor, and so I have to
465
+
use <code>fmap</code> (aka <code>Select</code>) to apply that unwrapped function to the value in the <code>Maybe</code>.
466
+
</p>
467
+
<p>
468
+
Obviously we could avoid the manual currying in C# by defining overloads that work with 2-argument functions, but I don't think it would change the fundamental fact that with applicative functors, you
469
+
apply a function wrapper in the functor, whereas if you just want to apply a function directly, that's ordinary functor <code>fmap</code> (aka <code>Select</code>).
470
+
</p>
471
+
<p>
472
+
Weird though this look, it makes plain something that I think is obscured in your <code>...Apply(x).Apply(y)</code> examples: the application of the first argument is a quite different operation, because you're applying
473
+
a lazy argument to a non-lazy function, but since the result is a lazy function, the application of the second argument needs to do something different: applying a lazy argument to a lazy function. Weird though
474
+
<code>f.Select(x).Apply(x)</code> looks, it does make it clear that the two stages are not the same thing.
0 commit comments