Open Avaq opened 3 years ago
I think ap
could also be implemented on top of getConst
, like:
Constant.prototype['fantasy-land/ap'] = function(other){
return other.cata({
NoConst: () => this,
Const: otherContext => Constant.Const (
otherContext['fantasy-land/concat'] (getConst (otherContext.constructor) (this))
)
});
};
We can even make the Applicative implementation conditional, like done in other types to get better type introspection:
//...
function Const(x){
const cnst = Object.create (Const$prototype);
if(Monoid.test(x)){
cnst['fantasy-land/ap'] = Const$prototype$ap;
}
}
//...
Applicative.test (Const ("_")) // true
Applicative.test (Const (42)) // false
Applicative.test (NoConst) // true
Ah. I just remembered another idea David and I had: Since Lenses don't actually use the Applicative instance on Const, we could export two Const types from this library: One that just has Functor, and doesn't need a Monoid typeRep when evaluated. This one could be used for lens implementations. And the other being the Applicative Functor variant proposed above.
Hello @Avaq. I think this is a pretty good idea. It's worth noting that any Apply
is susceptible to this trick. So in Haskell, you can write:
data AddPure f a = Pure a | Impure (f a)
instance Apply f => Applicative (AddPure f)
where
pure = Pure
Pure ab <*> Pure a = Pure $ ab a
Pure ab <*> Impure fa = Impure $ ab <$> fa
Impure fab <*> Pure a = Impure $ (\ab -> ab a) <$> fab
Impure fab <*> Impure fa = Impure $ fab <.> fa
interpret :: Apply f => (forall x. x -> f x) -> AddPure f a -> f a
interpret pure (Pure a) = pure a
interpret _ (Impure fa) = fa
All this to say the approach you've outlined definitely works and gives a lawful Applicative. It's possible that a general purpose utility for "deferring" the pure implementation of Apply
s would be useful, although I can't immediately see a use for it beyond Const
.
Thanks for your thoughts @masaeedu!
It's possible that a general purpose utility for "deferring" the pure implementation of
Apply
s would be useful, although I can't immediately see a use for it beyondConst
.
My inclination would be to go with the const-specific approach initially, and extract out if we find ourselves duplicating the approach.
During a recent conversation between @davidchambers and I, we came up with a way to define an Applicative Const Functor that can be used to implement Lenses, without needing a Monoid up front. The conversation is starting to escape my memory, so I'll just put the idea down here lest we forget.
The Problem
As I understand, the need for a Monoid on Const currently blocks progress on this library. In particular, there currently is no way to implement Fantasy Land's Applicative for Const in the way Haskell does:
Here we see
Applicative
implemented forConst m
by restricting tom
s that haveMonoid
, and just puttingmempty
as the actual context-value of theConst
. Haskell then relies on the type system to make it so whengetConst
is resolved, it gets a value of the correct specific Monoid.In JavaScript (and specifically, Fantasy Land as it is right now), we don't have a way to carry type information along with our Const instances so that when
getConst
is used, we can put a value of the correct Monoid there:Solutions
An alternative that I came up with is to have a binary type implementation for Const, where reliance on the type system is removed, and the fallback value is provided by the user when evaluating the Const:
A few observations:
NoConst ++ Const a = Const a
, etc)What do you think? Does this approach have any benefits over the one taken in #1 ?
/cc @masaeedu