chexxor [4:08 PM]
Oh my, a libuv wrapper in PureScript? That’s neat. Node has that built-in, in a way. It might make sense to write programs which support Node and C backends such that the libuv effects are behind extensible effects, so there can be an interpreter for those effects for each of the backends. So just don’t compile the JS interpreter for the C backend, only the C effect interpreter.
natefaubion [4:16 PM]
Run doesn’t work well with evented things
at least, as is
chexxor [4:43 PM]
Is it possible to make it nicer for evented things while staying within the current design of Run?
natefaubion [4:46 PM]
@chexxor it depends on what you mean. Effects in Run are first-order, which means Effects can’t be parameterized by other effects. Evented things usually require callbacks, and thus doesn’t work well with Run (these things are higher-order). You can have evented things at the edges though which feed into some Run program though.
You can have a version of Run with higher-order effects, which is what I was playing around with this weekend
It does mean you need a different abstraction than Functor though
chexxor [4:55 PM]
@natefaubion What’s an example of an effect which is parametrized by other effects?
natefaubion [4:56 PM]
chexxor [4:56 PM]
Seeing a type signature of s/t similar might help me understand.
natefaubion [4:57 PM]
an effect that takes another effect, and has control over how that effect is run and dispatched
it’s not always apparent in the type signature
catch looks like it takes a higher-order effect, and it kind of does
but it’s implemented as AST substitution
and a proper catch effect, rather than an interpreter, would be higher-order
have a problem with this too
I cannot encode
:: forall a
-> Aff a
-> Aff (Either PGError a)
function to use with purescript-run
I have tried
module FeatureTests.FeatureTestSpecUtils.Db where
import Data.Exists (Exists)
import Data.Functor.Variant (FProxy(..))
import Database.PostgreSQL as PostgreSQL
import Run (Run)
import Run as Run
-- | NOT WORKING, Exists is not a functor
-- | data WithTransaction' r next exists = WithTransaction' (Run (db :: DB r | r) exists) (Either PostgreSQL.PGError exists -> next)
data DbF next
= WithTransaction ???? next
derive instance functorDbF :: Functor DbF
type DB = FProxy DbF
_db = SProxy :: SProxy "db"
withTransaction :: forall r a . Run (db :: DB | r) a -> Run (db :: DB | r) (Either PostgreSQL.PGError a)
withTransaction action = Run.lift _db (WithTransaction ???? ????)
:: forall r
( aff :: Run.AFF
( aff :: Run.AFF
runDb connection = Run.interpret (Run.on _db handleDb Run.send)
handleDb = case _ of
WithTransaction next -> do
result <- Run.liftAff $ PostgreSQL.withTransaction connection ?????
pure $ next result
@srghma Run is a first-order, algebraic effect system. Basically, you can’t have effects that directly hold other effects, since this requires a fix-point over the effect type. Sometimes these effects can be made first-order by splitting it into enter/leave (or push/pop)-like effects. So instead of
WithTransaction, you might have
StartTransaction ... | CommitTransaction ..., and then your interpreter must handle a stack itself in a safe manner.
@srghma I worked on this recently in the webrow “framework”.
I think I’ve started with something similar to the model described by @natefaubion above but I encountered problems related to “the native js error handling” mixed with transaction / connection resource management (here are some related tests).
In the current iteration I’ve moved to the new resourcet lib by @robertdp . So instead of an interpretation to the
Aff I’m interpreting everything to the intermediate
ResourceT layer where all resources are cleaned up.
So finally I have this working version of pg integration with transaction handling embedded in the run app. Additionally we provide a tiny layer of helpers for selda and everything seems to work well during testing and on our dev server (we monitor pg
Pool connections and entries from postgreSQL pg_stat_activity table). The final API seems to be quite nice and guards on the type level against transaction nesting - here are pieces of some basic tests for our selda sugar.
Please be aware that I’m using devel branches of the postgresql-client and selda here (we can talk on priv about the details of course ).
Please don’t judge the quality of the code pieces above - I’m doing a lot of quite intense prototyping recently.
This seems cool
But I dont understand
but I encountered problems related to “native js error handling” mixed with transaction / connection resource management (here are some related tests)
Run.liftAff $ liftEffect $ throwError $ Effect.Exception.error "Aff throw before commit" error wasn’t handled?
the Run.liftAff $ liftEffect $ throwError $ Effect.Exception.error "Aff throw before commit" error wasn’t handled?
I’m not sure if it is impossible, I just wasn’t able to implement this kind of error handling and keep my interpreter polymorphic on the effects tail (I want to be able to possibly handle interpretation of multiple “resource managing” effects - I want to have possibility to manage redis transactions, files handlers etc. by using other effects). In other words I wanted to interpret my
Run (Pg + Aff + eff) into
Run (Aff + eff) and be able to rollback transaction and cleanup connections “somehow”. But in my interpreter I wasn’t able to
Aff exceptions and "remain in the
Aff exceptions broke the “interpretation chain” and escaped my cleanup logic.
When I’m using
ResourceT as a base layer this is no longer the problem. Additionally I wasn’t forced to touch nearly any other pieces of the app when switching to it and it provides a really generic layer for resource management!
I hope that I’m not talking too much bull*** here and again I’m not proud of the past code snippet linked above
Today I have learned that our
purescript-free library is not actually
Free from haskell, but rather
Freer from https://hackage.haskell.org/package/freer
because the Monad instance of our
Free doesnt require
f to be functor