There are some terms you guys used.
"Scalar" I see it in many places. (In flatMap for instance)
What is it and why it call that way?
What is the concern of NotificationLite what kind of code should be there?
To me it look like a class where you store a bunch of helper methods with parameterized value.
The methods there look almost static except it is parameterized at instance level.
I am in the middle of exploring things here. It looks like some spaghetti and I just pick on random part of it without knowing which part is the beginning or end.
NotificationLite was the consolidation of a pattern that I saw. To implement some operators like merge it is convenient to serialize the onComplete and onError in the same buffer/queue as the onNext values. The simple way to do this is with a .materialize() that turns all three of those into Notification. The problem with doing that is there is huge overhead for the allocation of each notification object for each onNext.
NotifcationLite gets around this by putting a type safe wrapper on some very unsafe operations. It uses the value object of type T _as_ the onNext notification avoiding the allocation. Using instanceof, and == to check for onError(t), onCompleted() and onNext(null) notifications. In the end the only time NotificationLite does an allocation is for wrapping onError which we hope is exceptional.
NotifcationLite is intended only for use inside operators where one piece of code has complete control of the values going in and out of internal buffers/queues to ensure there are no ClassCastExceptions.
We refer to "scalar" in respect of constant references to a single object. For example, Observable.just(1) is a scalar that is immediately available and thus can be optimized away in certain situations. Note, however, that the reference might not point to an immutable data structure.
NotificationLite is an internal class that does light wrapping of onXXX events in a more light-weight fashion than Notification does by avoiding allocation in the most common onNext case. It turns null and onCompleted into a constant Object and wraps onError into a private inner class while leaving non-null onNext as they are. However, lately many operators don't use all of its feature set but only the null wrapping to work with queues that don't support null elements. The other events now end up in fields of the operator, Throwable error; volatile boolean done; and are "moved" outside the queue. As @abersnaze mentioned, one should not leak NotificationLite values because it can have unexpected consequences: early sequence termination or ClassCastException. A small inconvenience with NotificationLite is its instance-style, that requires a local variable or field for it when required for "type safety". However, after 100s of operators, I never found a case where a queue's entry and exit environment differed in type, plus any mistake would show up as ClassCastException in test almost right away.
I'm closing this issue due to inactivity. If you have further input on the issue, don't hesitate to reopen this issue or post a new one.
Most helpful comment
We refer to "scalar" in respect of constant references to a single object. For example,
Observable.just(1)is a scalar that is immediately available and thus can be optimized away in certain situations. Note, however, that the reference might not point to an immutable data structure.NotificationLiteis an internal class that does light wrapping ofonXXXevents in a more light-weight fashion thanNotificationdoes by avoiding allocation in the most commononNextcase. It turnsnullandonCompletedinto a constant Object and wrapsonErrorinto a private inner class while leaving non-nullonNextas they are. However, lately many operators don't use all of its feature set but only thenullwrapping to work with queues that don't supportnullelements. The other events now end up in fields of the operator,Throwable error; volatile boolean done;and are "moved" outside the queue. As @abersnaze mentioned, one should not leak NotificationLite values because it can have unexpected consequences: early sequence termination orClassCastException. A small inconvenience withNotificationLiteis its instance-style, that requires a local variable or field for it when required for "type safety". However, after 100s of operators, I never found a case where a queue's entry and exit environment differed in type, plus any mistake would show up asClassCastExceptionin test almost right away.