Jump to page: 1 2
Thread overview
Promises in D
5 days ago
Calvin P
4 days ago
Calvin P
4 days ago
Calvin P
4 days ago
Calvin P
5 days ago
Andre Pany
5 days ago
Max Haughton
5 days ago
Andre Pany
5 days ago

Hi,

Here is an implementation of promises in D:

https://github.com/cybershadow/ae/blob/next/utils/promise.d

Docs: https://ae.dpldocs.info/ae.utils.promise.Promise.html

It attempts to implement the Promises/A+ standard as closely as possible.

Some thoughts about promises in D:

JavaScript's path towards asynchronous programming was callbacks -> promises -> async/await. Promises in JavaScript are one of the basic building blocks (and the next step down in terms of lowering) of async/await: async functions transparently return a promise, and await accepts a promise and "synchronously" waits for it to resolve, converting failures into thrown exceptions.

D doesn't have async/await (and, probably adding it would require a significant amount of work in the compiler), but D does have fibers. An interesting observation is that the same principles of async/await and promise interaction also apply to fibers: a task running in a fiber can be represented as a promise, and, in the fiber world, await is just a simple function which yields the fiber and wakes it up when the promise resolves (also converting failures into thrown exceptions).

Fibers do have overhead in terms of requiring the stack allocation per task and the cost of context switching, so they may not be the best solution all of the time. So, although an asynchronous networking / event loop library could just build everything on fibers (as older versions of Vibe.d have), it would seem that you could instead use promises as the lower-overhead "glue", and make fibers opt-in.

5 days ago

On Wednesday, 7 April 2021 at 06:41:36 UTC, Vladimir Panteleev wrote:

>

Hi,

[...]

Sorry, I sent this too soon.

Going even lower, there are callbacks/delegates. In JavaScript, there are few reasons to use callbacks today, because they are much less composable and don't provide much other benefit. However, there is one reason in D where callbacks trump both fibers and promises: a callback's caller controls the lifetime of the passed values, whereas promises require that their held value either is owned by the promise, or has infinite lifetime (i.e. is immutable and on the heap). So, if you were to write a low-level high-performance networking library, you would probably want to have:

void delegate(const(ubyte)[] bytes) onDataReceived;

where bytes is owned by the caller and is only valid until onDataReceived returns. You can't do the same with promises, because promises may be then'd at any point in the future, and you can't really do this with fibers because await returns its value as an lvalue and the network library doesn't know how long the user program needs the buffer for. (In case of fibers, you could instead do ubyte[4096] buf; auto bytes = socket.receive(buf[]);, but that's less flexible and may involve an additional copy.)

I see that eventcore uses delegates, which is probably the right choice considering the above.

In any case, here is the code that inspired me to write the promises module:

https://github.com/CyberShadow/ae/blob/ab6cb48e338047c5a30da7af7eeba122181ba1cd/demo/x11/demo.d#L76-L86

(Some good old callback hell.)

Here is the version with promises:

https://github.com/CyberShadow/ae/blob/3400e45bc14d93de381136a03b3db2dac7f56785/demo/x11/demo.d#L82-L88

Much better, but would be even nicer with fibers. :)

5 days ago

On Wednesday, 7 April 2021 at 06:51:12 UTC, Vladimir Panteleev wrote:

>

Going even lower, there are callbacks/delegates. In JavaScript, there are few reasons to use callbacks today, because they are much less composable and don't provide much other benefit. However, there is one reason in D where callbacks trump both fibers and promises: a callback's caller controls the lifetime of the passed values, whereas promises require that their held value either is owned by the promise, or has infinite lifetime (i.e. is immutable and on the heap). So, if you were to write a low-level high-performance networking library, you would probably want to have:

[...]

I use refCount to pass resource with promises provide by QuickJS, It also work well with async IO code based on D.

5 days ago

On Wednesday, 7 April 2021 at 16:14:15 UTC, Calvin P wrote:

>

I use refCount to pass resource with promises provide by QuickJS, It also work well with async IO code based on D.

Reference counting doesn't change the situation. With delegates, the resource provider can pass a temporary view into an internal buffer owned by the provider. With reference counting, you still need to allocate memory dynamically, and possibly do an additional copy from the internal buffer to the reference-counted one.

5 days ago

On Wednesday, 7 April 2021 at 06:51:12 UTC, Vladimir Panteleev wrote:

>

On Wednesday, 7 April 2021 at 06:41:36 UTC, Vladimir Panteleev wrote:

>

Hi,

Having been inspired by the Senders/Receivers C++ proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0443r14.html I started an implementation here https://github.com/symmetryinvestments/concurrency [*]

I initially dismissed the proposal completely, and it took me at least a few months before I realized the beauty of it.

Now, I see them as fundamental building blocks in asynchronous code. They are cancelable, they avoid unnecessary allocations and synchronizations, and above all, they adhere to the principles of structured concurrency.

This is a good talk from eric niebler about them:

https://www.youtube.com/watch?v=h-ExnuD6jms

[*] it are still early days but it implements a fair bit of useful asynchronous algorithms.

5 days ago

On Wednesday, 7 April 2021 at 21:20:11 UTC, Sebastiaan Koppe wrote:

>

On Wednesday, 7 April 2021 at 06:51:12 UTC, Vladimir Panteleev wrote:

>

On Wednesday, 7 April 2021 at 06:41:36 UTC, Vladimir Panteleev wrote:

>

Hi,

Having been inspired by the Senders/Receivers C++ proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0443r14.html I started an implementation here https://github.com/symmetryinvestments/concurrency [*]

I initially dismissed the proposal completely, and it took me at least a few months before I realized the beauty of it.

Now, I see them as fundamental building blocks in asynchronous code. They are cancelable, they avoid unnecessary allocations and synchronizations, and above all, they adhere to the principles of structured concurrency.

This is a good talk from eric niebler about them:

https://www.youtube.com/watch?v=h-ExnuD6jms

[*] it are still early days but it implements a fair bit of useful asynchronous algorithms.

The library looks majorly useful. I just noticed it has the license "proprietary" which makes usage just a little bit more complex in a business environment. Is there any reason for not using a common license?

Kind regards
Andre

5 days ago

On Wednesday, 7 April 2021 at 21:37:10 UTC, Andre Pany wrote:

>

On Wednesday, 7 April 2021 at 21:20:11 UTC, Sebastiaan Koppe wrote:

>

[...]

The library looks majorly useful. I just noticed it has the license "proprietary" which makes usage just a little bit more complex in a business environment. Is there any reason for not using a common license?

Kind regards
Andre

https://github.com/symmetryinvestments/concurrency/blob/master/LICENSE ?

5 days ago

On Thursday, 8 April 2021 at 03:40:50 UTC, Max Haughton wrote:

>

On Wednesday, 7 April 2021 at 21:37:10 UTC, Andre Pany wrote:

>

On Wednesday, 7 April 2021 at 21:20:11 UTC, Sebastiaan Koppe wrote:

>

[...]

The library looks majorly useful. I just noticed it has the license "proprietary" which makes usage just a little bit more complex in a business environment. Is there any reason for not using a common license?

Kind regards
Andre

https://github.com/symmetryinvestments/concurrency/blob/master/LICENSE ?

Yes, I noticed this file. Within dub.sdl it is declared as proprietary. Therefore, from a business perspective, you have to read the license file very careful. This is little more effort compared to a common license like apache, mit, bsl...

Kind regards
Andre

4 days ago

On Wednesday, 7 April 2021 at 16:43:59 UTC, Vladimir Panteleev wrote:

>

Reference counting doesn't change the situation. With delegates, the resource provider can pass a temporary view into an internal buffer owned by the provider. With reference counting, you still need to allocate memory dynamically, and possibly do an additional copy from the internal buffer to the reference-counted one.

I don't think so.

A buffer instance and it' derivative Slice instance can shared one refCount pointer.

If the Promise no long need own the Buffer just release the refCount.

If Caller finish with the Buffer, just release the refCount.

If the Buffer need be handle by multi callback cross thread, then it is passed by const. If callback need modify it then it should copy the Slice.

A const temporary view (in my case a const Slice) or a non-const temporary view (a Unique Slice) can work with Atomic refCount with multi thread safe code.

A Unique Slice will borrow the ownership from Buffer and return to Buffer when it released.

If the Buffer refCount is zero when Unique Slice released, the resource is release to object pool for reuse.

I don't see a case your Promise solution can void copy Slice but refCount need do copy.

4 days ago

On Thursday, 8 April 2021 at 04:53:47 UTC, Calvin P wrote:

>

I don't see a case your Promise solution can void copy Slice but refCount need do copy.

The paragraph you quoted discussed delegates, not promises.

Promises do need a copy always, as I stated in my post.

>

If the Buffer refCount is zero when Unique Slice released, the resource is release to object pool for reuse.

You don't need an object pool at all with delegates. You can just pass a slice of an array on the stack.

« First   ‹ Prev
1 2