So, JP asked my opinion privately and he thought it would
be better for me to paste what I replied here. I  didn't want
to make this discussion longer, however I have to make
these points explicit so we can move on. Sorry in advance
for the huge email. So I'll paste my answer here and then
make some considerations of other points afterwards:

""
Hello JP,

Now I see I should've voiced myself more in the discussion. But it has been
too lengthy and too much things to do before and during the feature freeze,
so I kinda absteined myself from it. Which was probably an error because
now nobody knows my opinion on the matter.

My personal stance on promises being Eo objects is that we're hammering
things to be OOP more than it should. IMO, Promises are nothing more
than a one-element-containers that warns when the value is available.

It is not too much different from Eina_List/Eina_Array, etc. Could we
make Eina containers Eo objects? We can, would it have benefits?
It would. However, it is always a trade-off and the problems it brings
would bigger than the benefits. Java does it and it works, but it slow
and invites people to inherit classes in ways they can't possibly do
because they, inadvertadly, break the class invariants by changing
how the user interacts with the class (by overriding methods).

I'm sure in C++, JavaScript and Lua they won't be used as an Eolian
class at all, that's even why Kolesa has suggested that it should
not even be an Eolian class (but be a Eo class implemented directly),
that way people can't inherit from it and bindings don't have to
special case the class, like we have to do for Eo.Base.

Now, why classes shouldn't generate Promise as an Eolian class?
Because it makes no sense and it is _not_ convenient to users.
Registering events? What does event have to do with anything?
And how do you chain promises like in JavaScript with events?
Well, you don't. And how about Lua? Why should Lua even
generate a Promise object? In Lua I'd expect the Promise
to be used to connect the Lua event loop with co-routines,
so the current execution would just get scheduled-out and
return execution when the Promise is now fulfilled.

The idea that we're going to create the _uber_super_ Promise
class for all languages is, IMO, completely impossible. Each
language will have its own asynchronous-way to deal with
how values are going to be available in the future and which
syntax better conveys that concept. Promises in our API,
OTH, allow us to _talk_ about asynchronous operations
in a way that is well-defined and which bindings can rely
enough to do proper code generation without knowing
specific semantics of each asynchronous operations. That
is not possible with events per se, because bindings
don't know anything about them.

However, making promises an Eo object is not a problem
per se, as long as we have _very_well_defined_
semantics for everything, it works exactly the same,
but, at the same time, to have very well defined
semantics we need to limit on how extensible everything
is, otherwise we just get normal events again and
that is not well-defined enough to be translatable
properly to every language.

So, in the end, if we make Promises an Eo/Eolian
object with very well defined semantics, we get
a "normal class"* that works the same as an
Eina_Promise, but uses the Eo syntax
for things on C (only on C!), and with that we
start paying for things that doesn't seem
to make sense IMO, such as the cost of
late binding in Eo; the cost of allocating
and instantiating a Eo object; etc. Without
reaping any benefits. And, IMO, if asynchronous
operations are to be _really_ used in EFL, we
need promises to be _fast_. Asynchronous
operations already have a inherent cost of
having to use heap memory where synchronous
operations can just use stack allocation, if
we make the cost considerably bigger, then
asynchronous operations becomes way too
costly to be useful for more and more
scenarios, and then it just won't be as useful.
Or maybe worse, each sub-component
start creating its own way of doing asynchronous,
because promises now are too expensive.

We can optimize things other ways ofc, like
caching and etc. But I fear this becomes
another eo_do, where we want to be
faster than C++ and end up creating a
huge infrastructure to make it faster,
and end up being even slower.

* not so normal, because we need to be able
to mark the type information statically when
used, as we do for containers, but that is only
on usage, so not a huge problem.

So, tl;dr; I think we need to have fast
promises so we can use it in more places,
have a proper C API that is convenient
to use and remove any frivelous features
that aren't extremely necessary for
normal asynchronous operations. And
let bindings deal with asynchronous their
own way by being users to EFL Promises,
and not necessarily expose EFL Promises
directly.
""

On Fri, Jun 17, 2016 at 9:38 AM, Tom Hacohen <t...@osg.samsung.com> wrote:
> On 16/06/16 22:55, Cedric BAIL wrote:
>> Being against promise, is only advocating for no asynchrone behavior
>> in Efl. This is a position we can't take. So either you have a better
>> pattern to handle asynchronous behavior and synchronisation or we have
>> to provide promise. Objecting to promise is clearly not helping
>> anything here.
>
> This is FUD and raster called you out on it a few times already, please
> stop stating it as fact all over the place, because it's anything but.
> You can say "in my opinion it's better to use promises", but that's as
> far as I'll go.

It is not FUD, making asynchronous operations ad-hoc with events
is not just a huge _pain in the ass_ to implement and use in C, but
also makes it impossible to translate that into anything that
would properly ressemble asynchronous operations in any
other language. Just simply ignoring the relationship that is
intrinsic on the asynchronous action and the events that
are bound to happen and in which order and how many times
they do makes it impossible to convey the information to bindings
as an asynchronous operation. All you can do is document things
and say, hey, you want to have an asynchronous operation?
Then first register events here, here and here first, then make
a seeming synchronous operation. This is awful for any language
and is even worse for Lua for the point I made about on top
and to which I'll get in deeper details below.

Asynchronous operations _must_be_well_defined_to_be_useful
and be consistent. It _doesn't_ have to translate to the same
thing everywhere and should not translate to the same thing.

We should not Eolize everything just to get automatic generation,
this is being super lazy. We need to offer good bindings and
that means translating concepts. What we really need is
a simple Promise/Future/Simple-Callback (the simple-callback
being the harder one to implement) so we can have actual
asynchronous operations and not just some events that
happen to return data as result from some operation that
looks synchronous.

You could @tag the s*** out of operations so bindings can
understand things so it is translatable, however, doing so,
doesn't remove the ad-hoc-ness of using events(without
a well-defined Promise concept). Which means that
1.  every class will do it differently; 2. Events are racy
by nature, overriding eo_event_callback_add to do
something completely different can solve the raceness,
but you can't just change documentation/expectations
from calling a function that behaves someway in
every other class. You're simply breaking the
function behavior for Promises and would be doing
even worse for several classes without a Promise
concept. So code that used to work one way for
_all_ Eo.Base classes, now only work for non-Promise
ones. Polymorphism is not about changing pre-conditions
and post-conditions of functions, but about extending
and interoperating with code.

So, Promises have to exist some way or another for
us to have asynchronous operations, otherwise
it will be a huge mess, it actually already is. We had
_at_least_ two ways to do async, one by setting
a property (which actually suffers from the same
problem as your proposal of overriding
eo_event_callback_add) which makes another
function to behave bizarrely different and which
any user of such function must now track. This is
not dynamic polymorphism, this is not even a
quack-is-a-duck dynamic typing, this is asking a duck to
quack and a dinosaur eating your computer instead.
And another was efl.model which wasn't even
asynchronous at all, it just had a function that asked
the data to be cached, there was nothing really
asynchronous about it, you just _couldn't_ have
asynchronous calculations because all calculations
had to be ready for fetching synchronously, all
it had was two-phase construction, basically.

On Fri, 3 Jun 2016 15:42:00 +0900 Carsten Haitzler (The Rasterman)
<ras...@rasterman.com> said:
>
> here's a rundown of efl promises vs other languages. basically our promises
> don't match any other languages. we can't just convert them to js promises or 
> c+
> + promise+futures... and lua just has NOTHING there in the language. so here
> is a table:
>
> FEATURE        | EFL | C++ | JS  | LUA |
> ---------------+-----+-----+-----+-----+
> create         |  X  |  X  |  X  |     |
> destroy        |  X  |  X  |  X  |     |
> then/else      |  X  |  X  |  X  |     |
> value          |  X  |  X  |  X  |     |
> cancel         |  X  |     |     |     |
> all            |  X  |     |  X  |     |
> race           |  X  |     |  X  |     |
> progress       |  X  |     |     |     |
> get status     |  X  |     |     |     |
> poll value     |  X  |     |     |     |
> wait           |     |  X  |     |     |
> wait+timeout   |     |  X  |     |     |
> ---------------+-----+-----+-----+-----+
>
> c++ futures/promises have features we don't. ti read up and they don't say
> wait/wait_for/until are optional and may not work. they seem to be
> requirements. so we can't map promises to c++ futures. it's not possible
> because we basically break how they work.
>
> we can't map them to js because js has no ability to cancel promises. also no
> progress too. so these features in core api's break then in js.
>
> it seems you can't ask if a promise or a future is done yet (without then
> blocking and waiting) in c++ or js, nor poll/get the value. (fyi in java you
> can poll the state to see if a future is done yet... for example - java not
> included above).
>
> lua has NOTHING AT ALL to map to.

I'm not developing the lua bindings so I can't say what is really going
to be mapped to. But I can say what I expect it to be by having used
Lua in a while and specially in a very asynchronous context (distributed
systems). Lua should map to co-routines! It will be the most beautiful
binding we will have!

You just write normal code, it stops and is descheduled (the binding
will use the Promise for that) and it is rescheduled when
the callback on promise_then is executed.

This is why I'm saying on the beggining of the email that we shouldn't
think of promises as our uber-asynchronous-communication-class that
will be exposed to everybody. We just need well-defined semantics
for asynchronous operations so we can translate it to every binding
in the most fucking nice way. People will likely _wow_ at our
co-routines in Lua! I'm sure I could at least make the lab that
created Lua in Brazil to wow at creating GUI interfaces with
co-routines.

For C++ I'll just implement wait, no big deal. Can it deadlock? Yes,
but this is C++, you can deadlock a future anyway, you must know
what you're doing. It will only be really usable in a threading
context. The other parts is just new member functions or free
functions on the efl::eina::future<T>. We use templates in C++,
not OO to represent most polymorphisms, this is exactly what
we did for efl::eina::list, efl::eina::array, etc. This is
expected for C++.

For JS, we will just implement a new class with the normal
Promise API with just a few more functions as well, people don't
care this is not a "standard" Promise as long as it is a
thenable. This works exactly like C++ but it is dynamic.

So, no problem at all for bindings, as long as we have a proper
Promise concept.

For all creation "problems" we just pass a efl::eina::promise<X>
to C++, in JS we pass two functions (probably three in our case):
a success, an error and a progress function that is called
by the code that wants to instantiate a promise. That's
the way to create promises in JS.

For Lua, it could be integrated to yield.

The All/Race composition in C++ can just be a free function
that gets multiple futures and return another future with
a tuple of all values, no big deal. It would work very nicely.

For Lua, you probably won't use all very much because a
coroutine is a promise_all instrinsically. The race thing
would be a composition on co-routines.

All problems can be solved if we define promises as a properly
defined concept that can be used by bindings and are not
intended to be used by binding users directly.

> --
> Tom.

Kind regards,
-- 
Felipe Magno de Almeida

------------------------------------------------------------------------------
What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic
patterns at an interface-level. Reveals which users, apps, and protocols are 
consuming the most bandwidth. Provides multi-vendor support for NetFlow, 
J-Flow, sFlow and other flows. Make informed decisions using capacity planning
reports. http://sdm.link/zohomanageengine
_______________________________________________
enlightenment-devel mailing list
enlightenment-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/enlightenment-devel

Reply via email to