About Aff's error channel in context of porting to C


Felix Schlitter [5:30 PM]
@natefaubion I got a basic makeAff going in pure-c:

testAff = void $ launchAff do
  msg <- makeAff \cb -> do
    cb (Right "Hello from Aff")
    pure (unsafeCoerce unit)
  liftEffect $ Console.log msg

i need to figure out how to do error handling next though as there’s no such thing as exceptions. I wonder if this will lead into Aff e a

chexxor [5:51 PM]
I think the error channel is Aff refers to the JS errorback-callback error channel
i.e. function someAsyncFn (arg1, arg2, cbFn, ebFn)

Felix Schlitter [5:52 PM]
yes, but in C there’s no exceptions at all, so all “errors” are essentially just values
usually indicated based on status codes

chexxor [5:52 PM]
No Exception type, you mean?

Felix Schlitter [5:53 PM]
yeah, no way to blow up like that
someone mentioned long jumps that could mimic that, but heck why bother. the simplicity of status codes is kind of beautiful

chexxor [5:54 PM]
Could just copy the JS type, something like { message :: String, name :: String }
status code is like 1 for success?

Felix Schlitter [5:55 PM]
usually it’s like negative = error, 0 = success, positive = may or may not have meaning

chexxor [5:56 PM]
ah, cool.

Felix Schlitter [5:56 PM]
and you’d have functions like strerr to go from some int to some message, so no need to carry that object around

chexxor [5:57 PM]
Yeah, but would be nice to “catch SomeError” rather than “catch -5”
idk what I’m talking about though. :slightly_smiling_face:

Felix Schlitter [5:58 PM]
yeah of course
still working through it though, Aff Int _ would be superuseless
for example, in libuv often u might get EAGAIN or sth, but only makes sense in the context of the function that tried to run
if you have a big Aff computation and it just comes back with EAGAIN that would be pretty useless

natefaubion [6:26 PM]
Aff just has a custom error channel. It just propagates values, but fixes the error channel to Error.
It uses Error for both exceptions and interruption

Felix Schlitter [6:28 PM]
but it seems like it only has to because exceptions are lingering in every function call
what i mean is that the Throw and Catch constructors don’t need Error at all

natefaubion [6:30 PM]
You don’t have to add it, but if you support cancellation/termination and bracket, you’ll have to deal with it anyway

Felix Schlitter [6:31 PM]
i’ll need to see where it leads me. it’s not that i choose not to throw, it’s just not a thing in C

natefaubion [6:31 PM]
Right, but I’m saying throwing in Aff is not throwing in JS
Aff handles it’s own “exception” propagation

Felix Schlitter [6:31 PM]

natefaubion [6:31 PM]
Aff will wrap Effect calls with a try/catch, but that’s about it

Felix Schlitter [6:32 PM]
yes that’s what i linked to above. because there’s no try/catch in C, there’s no way for Error to enter the game

natefaubion [6:33 PM]
Yes, it’s only if you want to model exceptions
You don’t have to add it

Felix Schlitter [6:33 PM]
it’d be nice if it was possible, if run_sync returned some Either e a instead

natefaubion [6:33 PM]
I’m just saying that, if you have cancellation and bracket, you’ll have to implement all the machinery anyway

Felix Schlitter [6:34 PM]
oh yes, i just haven’t gotten to it
i was wondering, by the way, why the scheduler exists. is it a performance optimization?

natefaubion [6:53 PM]
@Felix Schlitter it ensures fairness of async execution
it’s not strictly necessary
but it does mean that an async boundary always yields to other fibers

Felix Schlitter [6:54 PM]
i see, i’ll re-read the code with that in mind
i saw that join does not enqueue but just calls run directly

natefaubion [6:54 PM]
basically, whenever an async action resolves, it puts it in the scheduler (edited)
otherwise you can get into the situation where a single fiber starves the CPU
if it says its async and still resolves synchronously
you are using uv though, so it’s not the same

Felix Schlitter [6:56 PM]
same as node.js though
sorry, we use purescript for work a lot for node.js (and react-native), i forget it’s also used in the browser :stuck_out_tongue:

natefaubion [6:59 PM]
JS on node is still all single threaded, so the scheduler is useful there as well
I think the biggest place it comes up is AVar
but like I said, it’s not critical, just has some nicer properties for the system as a whole

Felix Schlitter [7:07 PM]
what i don’t understand though is that it seems like it’s doing everything synchronous, for example: https://github.com/slamdata/purescript-aff/blob/master/src/Effect/Aff.js#L112-L123 - there should be no way on earth that any code can run while it’s executing the while loop, for example
ooooh, unless in a thunk?

natefaubion [7:09 PM]
thunk() ends up being a callback that evaluates that fiber up until the next async boundary
it only pertains to Async

Felix Schlitter [7:11 PM]
yeah makes sense. tell me, how did you conceive aff???

natefaubion [7:12 PM]
jdegoes was working on the beginnings of ZIO at the time and we hashed out parts of the API
then it was just about making it fast, I guess
ZIO and Aff share similar APIs and concepts (edited)
stack-safety is a big part of what makes it complex
ParAff is like half the src, and if it didn’t need to be stack safe, it could be implemented in a very small fraction of the code
To me, lack of stack-safety is a showstopper

Felix Schlitter [7:26 PM]
yeah, agreed