Why do publishers subscribe instead of subscribers?

Hello,

Could someone please help me understand why linguistically a publisher connects to a subscriber instead of the other way around?

For example in Chapter 2: Custom Subscriber, we do:

let subscriber = IntSubscriber()
publisher.subscribe(subscriber)

Because, as in the Apple documentation:

Publisher>>subscribe(_:)
Attaches the specified subscriber to this publisher.

Can anyone explain that language to me? It would make more sense to me if a subscriber subscribed to a publisher…in other words, here I am, some publisher of values, waiting for someone to “subscribe” to me.

I would kind of understand it if it was:

Publisher>>subscriber(_:)

instead, where we would be saying, ok, publisher, here is another subscriber.

If anyone has a good way of thinking about this I would love to hear it because I can memorize it but it still trips me up!

Thank you!

I think that’s a fair question — when you read the code it doesn’t quite work. Long story short, I don’t know exactly, but I could speculate…

First of all, Combine implements the reactive streams spec and since that’s cross-language not all of it would work semantically as most Swift does, the subscribe method is defined in the spec here: reactive-streams-jvm/README.md at v1.0.3 · reactive-streams/reactive-streams-jvm · GitHub

The spec itself is “inspired” by the rx implementation where the terminology is a little different, instead of Publisher → Subscriber you have Observable → Observer (same things really) and you would usually not create observers at all but “subscribe” to the events you’re interested that the observable might emit like so:

myObservable
  .subscribe(
    onNext { print($0) },
    onError { print($0) }
  )

This, also, isn’t the more expressive Swift but since this was first implemented 20 years ago in C# — it was probably some of the most succinct and beautiful code at the time.

So, again, this is a very interesting question and this is my best guess how things ended up with a publisher subscribing a subscriber.

Hi @icanzilb, thank you for your thoughtful and historical reply…it makes sense if the language evolved from subscribing to events on an observable/publisher.

The following is a little off topic, but I think it serves to illustrate the benefits of learning Combine, from what I understand after only two chapters!

My experience with observables coming into this book is with Smalltalk, the latest incarnation being the Announcements framework.

There, you create a subclass of an Announcement, which I guess is sort of like the Input typealias in a custom Subscriber. It can hold specific information about the announcement.

Then, an observable object will [usually] create and hold on to an Announcer subclass. I guess you might have a global Announcer like NotificationCenter.

Finally, an observer listens for specific announcement class with either:

  • when: anAnnouncementClass do: aValuable : when anAnnouncementClass is raised, aValuable is executed.
  • when: anAnnouncementClass do: aValuable for: aSubscriber: when anAnnouncementClass is raised, aValuable is executed and define the subscriber.
  • when: anAnnouncementClass send: aSelector to: anObject: when anAnnouncementClass is raised, anObject should receive the message aSelector.

In other words, the simplest version would be something like:

Announcement subclass: #MyAnnouncement
	instanceVariableNames: '' 
	classVariableNames: ''
	package: ''.

announcer := Announcer new.
aSubscription := announcer when: MyAnnouncement do: [ Transcript show: 'Announcement received.' ].
announcement := MyAnnouncement new.
announcer announce: announcement.

As you can see, it is really very basic and doesn’t have any of the built-in chaining, streaming, completion, error handling, etc… that is present in Combine, so I am really looking forward to learning more from your book.

In then end, if I am bothered by it, I can always create a Subscriber extension that will turn around and connect to the publisher properly so my mind quits bending!

Thanks again!

If you’re still working through Chapter 2, let me put your mind at ease. Creating custom subscribers is really a very niche task — in all the code I’ve written, I’ve only ever had to create a custom subscriber very few times. The two publisher methods assign() and sink() will let you do mostly everything without ever thinking about subscribers.

Also, reactive code does read a bit strange at first, I’ll give you that. This is often the reason many developers quickly start to dislike reactive code — but as with everything, with a bit of practice things start to feel quite “normal” again.

Hi @icanzilb,

Thanks again for the clarification, and I’ll keep your experience in mind!

Take care,
Rob