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
In #459, there have been a number of approaches proposed to solve the "make a Command return multiple values" problem. @ocharles used a GADT to build a defunctionalised expression tree with the specific functions he needed, and I proposed using Coyoneda (free Functor) or even a free Applicative to "pretend" to compute over Symbolic values. When running an Ensure hook, an actual value can be extracted from the state Concrete using lowerCoyoneda or retractAp or whatever.
@ChickenProp then discovered that these existing free wrappers are not suitable for hedgehog for two main reasons:
They do not have the functor parameter as the final types argument, which means you have to manually write out instances for any barbies typeclasses, instead of using its generic-based deriving; and
These free wrappers lack a usable Show instance, which hedgehog requires.
(pro) Has FunctorB and TraversableB instances, and probably any others from barbies which we need;
(con?) Provides instance (Eq a, Eq1 v) => Eq (FVar a v), but instance Eq1 Symbolic ignores its function argument, so this means that given x :: Symbolic a, FVar (const 0) x == FVar (const 1) x will return True. This seems more like a "Symbolic's fault", but I think we should not provide the Eq instance if we can get away without it;
(pro) Provides instance Show (FVar a v), so it's actually usable with hedgehog;
(con) Not a Functor, because the type arguments are the wrong way around; and
(con) Not possible to apply a function over multiple Symbolic values.
I think the ergonomic benefits justify building our own free wrapper. I also think we should consider going beyond a free Functor to an analogue of a free Applicative, either the one in free:Control.Applicative.Free.Fast (which I don't really understand) or the one in free:Control.Applicative.Free.Final. It seems like it'd be pretty useful to be able to lift pure functions across multiple Symbolic values, and if #493 is accepted then we'll have instance Applicative Concrete to make the retraction from a free Applicative.
On the API side I have some suggestions. Here's what I think we should provide:
-- I think not working with `Var a v` will be more ergonomic, since if we get a good-- way of returning multiple values from a `Command` in #459, we'll have various-- `v a` values kicking around.fvar::Showa=>va->FVaravfapply::Showa=> (a->b) ->va->FVarbv($$)= fapply -- infix aliasmapFVar:: (a->b) ->FVarav->FVarbv(<$$>)= mapFVar -- infix alias, same fixity as (<$>)fconcrete::FVaraConcrete->afretract::Functor/Applicativev=>FVarav->va-- Exact constraint depends on whether we build a free `Functor` or `Applicative`.-- If you agree that we should go to a free `Appliactive`apFVar::FVar (a->b) v->FVarav->FVarbv(<@@>)= apFVar -- infix alias, same fixity as (<*>). Note that (<**>) exists in `base` and so is unavailable. There's precedent for `<@>` being a weird variant of `<*>` in some FRP libraries.liftFVar2:: (a->b->c) ->FVarav->FVarbv->FVarcvliftFVar3:: (a->b->c->d) ->FVarav->FVarbv->FVarcv->FVardv
The text was updated successfully, but these errors were encountered:
(I don't have strong feelings about a free applicative here. I suppose the main benefit would be applying a function to two symbolic values and storing the result in state? I don't think I've had a use for this so far, but that's just me.)
On FVar: I agree that the Eq instance is weird. I think it would be reasonable to get rid of it, and keep Var around for when people want Eq and Ord. (Or maybe it's more accurate to say I think the instance Eq1 Symbolic is weird? I'm not sure. The law is liftEq (==) = (==) and it does satisfy that. For a while I thought we might expect liftEq (\_ _ -> False) x x == False, which the instance violates. But that doesn't hold for Maybe or [] so it's clearly not a sensible thing to expect. So this instance does seem fine. And the Eq FVar instance seems fine to me too. But somehow the combination seems weird.)
I suppose the main benefit would be applying a function to two symbolic values and storing the result in state?
Exactly. I can't imagine an exact use right now (I was sketching with a system of Users, Roles, and User<->Role attachments, but that seems doable with the current command type). Extending to a free Applicative seems like it solves situations where you want to apply a pure function to 2..N Symbolic values, and store that Symbolic result somewhere that shouldn't see all the Symbolic arguments (like the input of a Command). These seem plausible and not hard to build support for (as opposed to, say, going all the way to a free Monad), which is why I think it's worthwhile.
instance (...) => Eq (FVar a v) is weird.
I think there's an additional coherence that we expect due to the relation between Concrete and Symbolic that makes this instance weird. (It's even worse for Ord where the concrete values could compare very differently to the generated names.)
I vote that we scrap those Eq/Ord instances but keep instance Eq1/Ord1 Symbolic and instance Eq/Ord (Symbolic a), which seem really important for maintaining e.g. sets of things generated from previous commands.
In #459, there have been a number of approaches proposed to solve the "make a
Command
return multiple values" problem. @ocharles used a GADT to build a defunctionalised expression tree with the specific functions he needed, and I proposed usingCoyoneda
(freeFunctor
) or even a freeApplicative
to "pretend" to compute overSymbolic
values. When running anEnsure
hook, an actual value can be extracted from thestate Concrete
usinglowerCoyoneda
orretractAp
or whatever.@ChickenProp then discovered that these existing free wrappers are not suitable for
hedgehog
for two main reasons:barbies
typeclasses, instead of using its generic-based deriving; andShow
instance, which hedgehog requires.He proposed a custom
FVar
type, which I think has the following advantages and drawbacks:FunctorB
andTraversableB
instances, and probably any others frombarbies
which we need;instance (Eq a, Eq1 v) => Eq (FVar a v)
, butinstance Eq1 Symbolic
ignores its function argument, so this means that givenx :: Symbolic a
,FVar (const 0) x == FVar (const 1) x
will returnTrue
. This seems more like a "Symbolic
's fault", but I think we should not provide theEq
instance if we can get away without it;instance Show (FVar a v)
, so it's actually usable withhedgehog
;Functor
, because the type arguments are the wrong way around; andSymbolic
values.I think the ergonomic benefits justify building our own free wrapper. I also think we should consider going beyond a free
Functor
to an analogue of a freeApplicative
, either the one infree:Control.Applicative.Free.Fast
(which I don't really understand) or the one infree:Control.Applicative.Free.Final
. It seems like it'd be pretty useful to be able to lift pure functions across multipleSymbolic
values, and if #493 is accepted then we'll haveinstance Applicative Concrete
to make the retraction from a freeApplicative
.On the API side I have some suggestions. Here's what I think we should provide:
The text was updated successfully, but these errors were encountered: