https://en.wikipedia.org/wiki/Common_Object_Request_Broker_A...
In my 25+ year career I haven't seen it in use anywhere. I only ever encountered it while getting my CS degree.
https://en.wikipedia.org/wiki/Common_Object_Request_Broker_A...
In my 25+ year career I haven't seen it in use anywhere. I only ever encountered it while getting my CS degree.
Too bad you didn't join the company I left in the 90s. It was used in production.
I learned about it in my CS degree too, and (because I was a fulltime dev at the time as well as a part time student) I went ahead and used it at work[1].
From what I remember of using it in production, the only difficulty I had was in sourcing a free ORB (server software) that supported inter-ORB routing so that I could load-balance.[2]
My employer eventually shelled out a small fortune (it was the 90s, we were drowning in VC money) for something from, IIRC, IBM[3] that ran on Sun Enterprise Servers (another small fortune).
My experience with CORBA was GREAT! I mean, compared to the way we do it now with browser tech:
1. I could use any language to write the client software, not limited to only Javascript.
2. Making a server call from the client was transparent. It looked like any other function call, unlike how it has to be done now using promises/futures or callbacks.
3. The tech supported exceptions which were also transparent to the programmer. In C++ you could do the following and it would work as expected.
try { myObj->foo() } catch { /* ... */ }
4. It was all strongly typed; if you used an argument with the wrong type you'd get either (in compiled languages) a compilation error or (in Interpreted languages) a runtime exception before the call is made.5. Developer velocity was great. I wrote my object specification, the tools generated both the server-side and the client-side wrappers, and all I had to do was call them.
[1] I was young, still in the phase of resume-driven-development.
[2] I used a free ORB written in C++ called either Mico or Micro; I don't remember the specifics.
[3] Maybe Sun, I'm not sure.
That is a very bad idea and one of the reasons this kind of thing rightfully died out.
Because a server call isn't like any other function call. It has orders of magnitude higher latency, and additional failure modes that you actually have to take care of.
It shouldn't look like any other function call.
I agree, but, like everything else that is a bad idea, naming conventions help.[1] Namespacing helped too.
I used naming conventions to ensure that network calls looked different, and namespaces that kept network objects in their own module.
[1] Right now we rely on naming conventions in most codebases to differentiate between #define'd literals and variables (C), between constants and variables (Java), between variables and methods (Kotlin, Java, everything else), between interfaces and classes (C#, C++, everything else too, probably), for everything in Python (pep8).
Using naming conventions to identify remote calls is no different than using naming conventions to identify interfaces.
I'm sorry, This is the stupidest thing I see commonly repeated in public discourse about software.
Every single distributed application I've ever worked on in my 30-year career (including working for multiple companies you've heard of) wrapped remote calls in something that looks like a normal function call. It doesn't matter if your low-level RPC stub throws RemoteException or returns RemoteError, somewhere up the call stack someone has wrapped this into a simple method that looks like this:
doSomethingUseful();
In real life, you make a call to a function and you live with the consequences. If you're lucky, the docs let you know the performance and failure characteristics. If not, you make some assumptions. When those assumptions are wrong, you spend some time debugging and profiling.Adding a bunch of syntactic noise to the callsite doesn't help. The first thing any competent programmer will do is abstract your noise away in convenience methods. Because 99% of the time, it doesn't matter that your call is remote.
Take a look at your own codebase that makes client REST calls to some other service - you may hand-wire a bunch of http calls, but somewhere up the stack there's a function that hides the http mess. Everything below that function is accidental complexity.
This isn't a problem as long as the returned value provides the right failure semantics (like futures). The problem with trying to encapsulate the network is that deep call chains lead to cascading failures for problems that are common in networks (partitions, latency, etc.). These failure modes also lead to more pervasive use of timeouts in deep call chains, which then introduces non-determinism, which itself makes issues impossible to debug.
In the rare case you need to harden a particular call, you add caching or retries or whatever other logic fits your use case. It matters not one bit whether you're using futures or synchronous rpc stubs. Actually it does - synchronous code is easier to harden because it's easier to reason about.
Even javascript added await because it's better to pretend that async code looks synchronous. The failure semantics of "throws an exception" are just fine.
Uh-huh, but did the message actually get through? Can they safely just retry? These are very uncommon failure modes on local systems but very common on networked systems. Without a proper stateful abstraction beyond just "procedure call", like a promise, you can't address these failure modes properly.
> In the rare case you need to harden a particular call, you add caching or retries or whatever other logic fits your use case
Which now makes your system nondeterministic like I said.
> Even javascript added await because it's better to pretend that async code looks synchronous
Yes, linear code is easier to read. I don't see what this has to do with anything. The use of promises and await indicates a possibility of failure semantics that would otherwise not be apparent in the program's control-flow.
Yes, you can superficially make this look like synchronous code, but it's not synchronous code.
Again, total nonsense. There's nothing special about a promise. Whatever logic you can build on promises is easier to build synchronously. Everything that applies to building distributed systems applies whether you use rpc stubs or promises. Promises are just noisier and harder to reason about.
Trying to hide the promises behind a synchronous client interface is unnecessarily constraining and inefficient, like requiring large stack contexts that can't be restarted or persisted, and so can't be simply resumed after partitions.
...
> The use of promises and await indicates a possibility of failure semantics that would otherwise not be apparent in the program's control-flow.
They don't, though. They don't indicate if the message got through. They don't indicate if you can safely retry. Their failure mode is exactly as opaque or as transparent as synchronous calls.
The reason for their existence and mandatory use in Javascript is due to a deficiency of the platform (single thread, so all synchronous calls block).
If the platform was better they would never have existed.
The promise is at the remote end. Promise resolution is idempotent, so retries always resolve to the same value. These are correct promise semantics as pioneered in the E language.
What? The promise is local, so what do you mean "the promise is at the remote end?"
> Promise resolution is idempotent,
No. It is only run once (whether resolve or success), that does not mean it's idempotent.
Just like exceptions for synchronous code, or error returns.
Using promises in JavaScript is a hack around the fact that the platform has some pretty large deficiencies.
Idempotent operations are implicit promises at the protocol level. We're talking about distributed systems here where abstractions and semantics cross machine boundaries, so your local-only focus is not valid. I suggest reading up on the E language via the link I provided.
> Using promises in JavaScript is a hack around the fact that the platform has some pretty large deficiencies.
A single threaded event loop is not necessarily a deficiency.
4 and 5 are limited to the schema-based ones.
3: again implementation issue.
4: swagger/openapi will help on the compile side (sadly not runtime with all languages but again, language/impl issue)
5: generators are still a thing and available if you look.
My complaint is not "these things don't exist now", it's "they're not part of the standard". For each "it's an implementation issue", you have multiple incompatible competing mechanisms. With CORBA, while the standard was large and stupid, the very basic thing (make an RPC call, get the response and handle any errors) was supported by the standard in a non-ambiguous and practical way.
So, yeah, the fact that something that was available in the 90s is now available depending on implementation is the problem.
And, yeah, if memory serves CORBA worked well enough for Gnome but not KDE because the C++ story was not great. Whatever bindings being used were dependent on features not well supported by G++, templates being painfully slow to compile, etc.
kde in it's turn were heavily reliant on it (mico) for embedding and communication. but compilation was indeed painfully slow (i still remember never ending kde compiles in 99-2000) so they came up with dcop/kparts after having few drinks and deciding that they can do better
In the end, both teams decided that assuming components running in remote processes was the wrong default. I'm not sure what GNOME replaced them with, but KParts rescued KDE 2.0
in gnome i think they did something like "gparts" based on glib. but because gnome desktop was composed in large part from applications written in gtk and not in gnome libs it never had too much uptake (unlike in kde). but i might be wrong - i been kde user from 1.0 alpha4 and gnome was parallel universe
https://www.linuxjournal.com/magazine/kdemdashthe-next-gener...
Eventually it was replaced by a new server infrastructure based on Java EE and Red-Hat Linux.