this "should be" a straightforward of application and interpretation of Resource (right???), but I'm having issues with the fact that the inside action has a different type from the r
and it is very notable here that our ma is already concretely typed, but runT can still be used since it has ∀ m that is just instantiated to our visible Sem rInitial
if this doesn't work, Resource probably won't too :thinking: it's almost directly a Resource usage
in fact now that I think about it, you can probably do away with Transaction entirely
afaiu you're peeling off all the possible Transaction invocations (and in general, when you use Tactics and make that recursive call you're doing that)
when you recurse, you basically run the interpreter on the ma, since it might contain repeated uses of it, afaiu. I recommend (re-)reading Sandy's blogposts on the matter
if I wanted to use the Transaction-less version, I would have made my "business logic" functions take polymorphic functions (the acquire, release and act) to use
I think you can't actually call (in a "meaningful way") runTransaction with that type signature
since you have to provide a runDb :: (forall r'. t -> InterpreterFor Db r') that means that your runDb has to work for any r', or in other words, you can't rely on any effects (other than Db) being in the stack :thinking:
I took another look and I don't see how it could be possible for this to work. we can only hope @Love Waern (King of the Homeless) has some dark magic up his sleeve
Yet again interpretH fails me and I need to work with Weavings directly.
This might or might not be safe depending on the semantics of the runner. Is it ok if it's used multiple times for a single token -- once per Db action -- as long as the block within a specific token is used is delimited by acquire and release? If so, then this is safe. If not, then this is not safe.
Note that Transaction now needs a type variable. This is like Cont, where application code is expected to keep it fully polymorphic.
There's probably a more elegant solution. Maybe. I'll look into it more later.
so I guess there's no "easy" way to "limit" what effects you can use inside InTransaction (or any HO effect for that matter) :thinking: and hide them from the "outside world" so that it can't interpret them however it pleases
The way my definition of Transaction works is that InTransaction provides the means of executing effects of Db locally within a region through some token. Executing actions of Db is done through Trade. Combining these, you can create the inTransaction action, where you gain access to a token locally so that you may execute actions of Db, and then you interpret the Db effect by using Trade.
This is an implementation detail: users should never use the constructors InTransaction and Trade directly.
Where Opaque is simply a newtype-wrapper to prevent the polymorphic q from messing with Member.
No weird state threading required! Believe it or not, this is almost possible! You can switch from the weave abstraction to a different mechanism with all the same restrictions, but is able to support interpretHGood. Still, there's so much tied to the weave abstraction that doing that switch would cause massive breakage. Something for v2.0 maybe.
since we now had two discussions of that kind within a few days, I think we need an interpretH that specializes in e (Sem r1) a -> Tactical e (Sem r2) r x, or whatever would make sense.
or at least r1 ~ eff : r2
how can I implement
runTransaction
?this "should be" a straightforward of application and interpretation of
Resource
(right???), but I'm having issues with the fact that the inside action has a different type from ther
do we want to
runDb
insiderunTransaction
?yup
otherwise you can't dispatch the Db inside
tentative solution, without
Embed IO
onrunDb
:I've left all the signatures in for analysis
so it definitely helps for type inference to make all the existentials explicit
and it is very notable here that our
ma
is already concretely typed, butrunT
can still be used since it has∀ m
that is just instantiated to our visibleSem rInitial
do you think you'll need an explicit way to show that
Member (Embed IO) (Db ': r)
impliesMember (Embed IO) rInitial
?it works if you put the constraint in the
InTransaction
ctor as wellbut optimally I guess you'd just pass
runDb
as an argument torunTransaction
how does that help?
Embed IO
won't be visible torunTransaction
thenif you pass it in as an argument and use it you'll still get the constraint tho?
but at the callsite
ah so you mean
run :: Token -> Sem r a
and not the specialised version
hmmm
one sec
like this. but this might have to be tweaked, search the
polysemy-resume
topic for significantly relaxed :sweat_smile:but with this, you should be able to experiment more easily
cool, thanks!
still not sure how to navigate the Tactics/Strategy related type sigs/errors
well, the most important thing is to internalize that
Tactics
is just a regular effect that has to be at the head everywhere ininterpretH
and that it carries an
m
, which corresponds to theSem r
in you higher-order thunk in the constructorInTransaction
and then you only need to find the right combinator from the
Tactics
module to lift your thunk into the right signaturerunT
does that here, it'sSem rInitial a -> Sem (Transaction : r) (f a)
effectivelywhere
rInitial
is ther
fromInTransaction
as seen from the perspective ofinterpretH
, wherer
is the final stackreally curious to see if this works well at runtime :sweat_smile:
btw if you want to allow nested calls to
inTransaction
, you will have to switch out the interpreter in the last line to avoid double bracketingif this doesn't work, Resource probably won't too :thinking: it's almost directly a Resource usage
in fact now that I think about it, you can probably do away with
Transaction
entirelyTorsten Schmits said:
won't the semantics be right? in nested
inTransaction
s you're using the "innermost" transactionwhich is what I'd expected (e.g. from name shadowing)
guess so!
and using Resource directly just means that you can't use a pure interpreter
you can still
runIgnoreResource
which just drops alloc and dealloc (not in library), if you want to use aSet
instead of an actual dbI see!
for completeness:
btw, do you know how the Tactics internals actually "give you" the next
Transaction
that you might needfor the recursive call
afaiu you're peeling off all the possible
Transaction
invocations (and in general, when you use Tactics and make that recursive call you're doing that)when you recurse, you basically run the interpreter on the
ma
, since it might contain repeated uses of it, afaiu. I recommend (re-)reading Sandy's blogposts on the matterbecause at that point, your
ma
is still untouched and you're already in the interpreterfunny, you can't do that transformation
this is fine
nvm, just sleepy
aand the
Transaction
-less version is uh..and that's better?
¯\_(ツ)_/¯
you won't be able to switch
acquire
andrelease
for no-ops outside of the programif I make
Token
polymorphic it'll be finethen I can use
token ~ ()
how are you using that function then? won't you call it in business logic?
yeah yeah, it's worse
you're basically doing the work of polysemy manually
because you'll end up threading all your functions as arguments
that you need to mock
just so I'm on the same page, you would have passed
acquire
etc into the business logic?if I wanted to use the
Transaction
-less version, I would have made my "business logic" functions take polymorphic functions (the acquire, release and act) to usethat sounds very uncomfortable!
I think you can't actually call (in a "meaningful way")
runTransaction
with that type signaturesince you have to provide a
runDb :: (forall r'. t -> InterpreterFor Db r')
that means that yourrunDb
has to work for anyr'
, or in other words, you can't rely on any effects (other thanDb
) being in the stack :thinking:I can add a constraint like so
but now I can't write the "send" function :thinking:
yeah, that's what I meant earlier with "look at the other thread" :smile:
why can't you write the function?
when I so much as mention
InTransaction
e.g.I get a "can't find the effect
eff
" (Could not deduce LocateEff eff r ~ '()
)maybe my signature should be different
Member eff r =>
?:facepalm:
but I think you need to change the signature of the interpreter parameter in the Transaction interpreter
going to sleep first, this is not productive :big_smile:
:big_smile: I'll take a look later as well
Torsten Schmits said:
from experience with trying a similar thing?
exactly
https://funprog.zulipchat.com/#narrow/stream/216942-Polysemy/topic/cross-interpreter.20errors/near/212495795
might be sufficient to just use
r
instead of an existentialI took another look and I don't see how it could be possible for this to work. we can only hope @Love Waern (King of the Homeless) has some dark magic up his sleeve
Seems so. Here's my first go:
Yet again
interpretH
fails me and I need to work withWeaving
s directly.This might or might not be safe depending on the semantics of the runner. Is it ok if it's used multiple times for a single token -- once per
Db
action -- as long as the block within a specific token is used is delimited byacquire
andrelease
? If so, then this is safe. If not, then this is not safe.Note that
Transaction
now needs a type variable. This is likeCont
, where application code is expected to keep it fully polymorphic.There's probably a more elegant solution. Maybe. I'll look into it more later.
what is the
Trade
for?so I guess there's no "easy" way to "limit" what effects you can use inside
InTransaction
(or any HO effect for that matter) :thinking: and hide them from the "outside world" so that it can't interpret them however it pleasesThe way my definition of
Transaction
works is thatInTransaction
provides the means of executing effects ofDb
locally within a region through some token. Executing actions ofDb
is done throughTrade
. Combining these, you can create theinTransaction
action, where you gain access to a token locally so that you may execute actions ofDb
, and then you interpret theDb
effect by usingTrade
.This is an implementation detail: users should never use the constructors
InTransaction
andTrade
directly.The only way to "limit" what effects you can use inside a
InTransaction
is to have a parametrized monad:This is as easy to interpret as it is difficult for application code to use. It's not even a higher-order effect. It's like defining:
or the "wooden" way of
:big_smile:
Eh. That leaks implementation details of
Transaction
toDb
. I prefer theTrade
approach.yep, as a library writer!
in
inTransaction
you're replacing all theDb
actions with the same actions but withTrade
s around them, right?Yes.
cool, thanks!
oh yeah I can use
transform
for that. Forgot that existed.so mayber
interpretH'
should also be somewhere in the library, especially if you've had to rely on it more than once?I've been humming and hawing about that. Honestly, I'm not satisfied with
interpretH
. It's too complicated for it's own good.In fact, personally, I learned how
Weaving
s worked before I learned howinterpretH
worked!Maybe this could be added as an alternative:
Which basically inlines the
Weaving
.Alternatively, maybe the design of
Tactical
could be revamped. Not sure.You know what would be really neat? This:
Where
Opaque
is simply a newtype-wrapper to prevent the polymorphicq
from messing withMember
.No weird state threading required! Believe it or not, this is almost possible! You can switch from the
weave
abstraction to a different mechanism with all the same restrictions, but is able to supportinterpretHGood
. Still, there's so much tied to theweave
abstraction that doing that switch would cause massive breakage. Something for v2.0 maybe.since we now had two discussions of that kind within a few days, I think we need an
interpretH
that specializes ine (Sem r1) a -> Tactical e (Sem r2) r x
, or whatever would make sense.or at least
r1 ~ eff : r2
and nice solution with the
Trade
!!!