[HN Gopher] Why Architecture Oriented Programming Matters (2019)
___________________________________________________________________
Why Architecture Oriented Programming Matters (2019)
Author : mpweiher
Score : 77 points
Date : 2021-12-20 10:45 UTC (12 hours ago)
(HTM) web link (blog.metaobject.com)
(TXT) w3m dump (blog.metaobject.com)
| jph wrote:
| The article leads to "Mary Shaw: Procedure Calls Are the Assembly
| Language of Software Interconnection: Connectors Deserve First-
| Class Status".
|
| Fixed link is https://resources.sei.cmu.edu/library/asset-
| view.cfm?assetid...
|
| Abstract: Software designers compose systems from components
| written in some programming language. They regularly describe
| systems using abstract patterns and sophisticated relations among
| components. However, the configuration tools at their disposal
| restrict them to composition mechanisms directly supported by the
| programming language. To remedy this lack of expressiveness, we
| must elevate the relations among components to first-class
| entities of the system, entitled to their own specifications and
| abstractions.
| mpweiher wrote:
| Thanks, fixed!
| karmakaze wrote:
| This 1994 paper is the important bit rather than the 2019 post.
| What it describes isn't much different than the interfaces that
| we have in operating system loadable modules or application-
| ecosystems with plugins. The only distinction is the
| requirement of first-class status. This would require a
| unification of language, operating system, and application. The
| closest I've seen to something like that is NeXT with
| Objective-C and the NS framework/Kits. The evolution of that
| into macOS/iOS, Swift, and *Kits are less so.
|
| The other thing that comes to mind when reading this is the
| adage about any configuration file format evolving to become a
| poorly implemented subset of lisp.
|
| As long as software development is a commercial endeavor I
| don't see how we'd ever end up with full interoperability with
| the goals being counter to it.
| GuestHNUser wrote:
| This blog reminds me of something John Carmack calls architecture
| astronauts. It talks about "glue" / connectors integrated with a
| programming language, which at a high level, sounds really nice.
| But I am skeptical of how well one could implement it in
| practice. Interoperating is far from standardized and I
| definitely want concrete examples illustrating what "Architecture
| Oriented" is in code and not theory. I hope there is a part two
| for this.
| mpweiher wrote:
| Funny, I call it "architecture without the astronautics",
| because you get to express architecture in a very simple and
| straightforward manner.
|
| http://objective.st/
|
| Sample scripts:
|
| https://github.com/mpw/Objective-Smalltalk/tree/master/scrip...
| xvedejas wrote:
| > Objective-S is the first general purpose programming
| language.
|
| You should consider removing this claim, since it is a flat-
| out lie, or at least a willful disregard for the language
| that other people use. If you want to communicate what is
| novel about this programming language, you need to remove
| this. It heavily detracts from the architectural mechanisms
| that you start to explain on the "About" page
| (http://objective.st/About) which seems to be the actual
| meat, where you could flesh out further explanation.
| mpweiher wrote:
| It's not a lie, though it obviously is controversial.
|
| The languages we _call_ "general purpose" are nothing of
| the sort. They are Domain Specific Languages for the domain
| of algorithms.
|
| We press them into service for all sorts of things they
| aren't actually very good at, hence the problems. We do so
| because we don't have any alternatives.
|
| Took me a long time to figure this out. And an _much_
| longer time to communicate it concisely. So yes, I can deal
| with it not being immediately obvious.
| lvncelot wrote:
| Just my opinion: this sentence really does you no favors.
| I read it, then re-read it because I wasn't sure whether
| I missed a word, and then I closed the tab, because
| writing something like this seems incredibly dishonest.
| xvedejas wrote:
| Communicating badly and then acting smug when you're
| misunderstood is not cleverness.
| srcreigh wrote:
| How does all this relate to algorithm theory, Turing
| Completeness, turing machines, etc?
| mpweiher wrote:
| It doesn't.
| srcreigh wrote:
| Fair enough. I respect that.
|
| I always thought CS theory misses out on the more
| interesting area of computation. As if algorithms are
| just one "dumb" way to map the giant reality of creative
| potential into itself. Practically speaking it often
| fails to do that, despite our theoretical
| understanding...
|
| Even though your language may be "turing complete" like
| other "general purpose" languages in theory, if it
| elicits more interesting algorithms and creative
| expression, I do kinda get your point.
| w-j-w wrote:
| agumonkey wrote:
| here's the pdf mentioned in the influences
|
| https://resources.sei.cmu.edu/asset_files/TechnicalReport/19.
| ..
| cpeterso wrote:
| btw, I believe the term "architecture astronauts" was coined by
| Joel Spolsky in 2001 or earlier:
|
| https://www.joelonsoftware.com/2001/04/21/dont-let-architect...
| Zababa wrote:
| I've said a few times that modern microservice architectures feel
| a lot like what traditional object-oriented programming was
| about, and I think this is another confirmation of that. I think
| that these days the connectors are JSON over HTTP, RPC and things
| like that. When a service exposes a JSON over HTTP API, I can use
| it from any programming language, from any program.
|
| So "first class connectors", to me, would be something like a
| language describing how to interact with a service, and a code
| generator that takes that language and transform it into code in
| your language. For example, the aws-sdk-rust [1] says:
|
| > The SDK is code generated from Smithy models that represent
| each AWS service. The code used to generate the SDK can be found
| in smithy-rs.
|
| Here's a link to Smithy's homepage:
| https://awslabs.github.io/smithy/.
|
| To me, Smithy with a code generator looks like a connector that
| would increase your productivity. But I'm not sure if we can call
| something that relies on code generation "first class". I'm not
| sure what "first class connectors" would look like either.
|
| [1]: https://github.com/awslabs/aws-sdk-rust
| Jupe wrote:
| Very cool - thanks for sharing; I'd not seen Smithy before (and
| was quite curious how AWS manages it "all"!)
|
| From the web page:
|
| > A Smithy model enables API providers to generate clients and
| servers in various programming languages, API documentation,
| test automation, and example code.
|
| Yes. This. Please.
| chriswarbo wrote:
| Another thing to keep in mind is that the usefulness of "glue"
| depends on what it can stick. For example function composition is
| very useful in functional programming, precisely because (first-
| class) functions are used for so many things in FP.
|
| Relatedly, "composition" (of functions or anything else) is a
| powerful idea precisely because the result of a composition is
| the same kind of thing as the components; hence we can keep
| composing more and more.
| Supermancho wrote:
| > Therefore, to increase one's ability to modularize a problem
| conceptually, one must provide new kinds of glue in the
| programming language.
|
| > He just made the important point that "one must provide new
| kinds of glue" and then the amount of "new kinds of glue" is the
| smallest number that actually justifies using the plural.
|
| The real issue is the appeal to authority. John Huges didn't
| _make a point_ ; he _made a claim_.
|
| Specifically "new kinds of glue" is a troubling phrase, that has
| likely been misused more than just this blogpost. The statement
| implies there are more than was common at the time of writing
| (maybe?). It doesn't specifically mean there need be more than
| lambdas, closures, and mixins or more implementations of the same
| in different ways (runtime composition via aspects, copy-paste
| composition via traits, etc).
|
| I would think this needs more study, as it's more of a casual
| observation than a strong claim. Was Perl the most modular? Maybe
| so. I don't think that made it the most useful, implying an upper
| boundary, as Marcel noted.
| mpweiher wrote:
| > The real issue is the appeal to authority.
|
| No appeal to authority here as I don't claim this as true, just
| interesting.
|
| > John Huges didn't make a point; he made a claim.
|
| Actually, he did make a point, as he provided reasoning for his
| claim.
|
| > The statement implies there are more than was common at the
| time of writing (maybe?)
|
| There are.
|
| _Towards a Taxonomy of Software Connectors_
| https://dl.acm.org/doi/10.1145/337180.337201 /
| https://csis.pace.edu/~marchese/CS865/Papers/p178-mehta.pdf
|
| I count > 100 leaf nodes in their classification tree. Not sure
| I agree with all of them, and there are probably others they
| missed. But more than 2 is trivially fulfilled.
|
| > Was Perl the most modular?
|
| Hmm...Perl wouldn't come to mind as something with a way for a
| user to define and refine new connectors.
| Supermancho wrote:
| > Perl wouldn't come to mind as something with a way for a
| user to define and refine new connectors.
|
| Anyone can add a new features to a language that users want
| to use. The source of the new connectors are incidental (user
| defined vs native syntax). That aside, operator overloading
| is fits the specific "user glue" you are focusing on. Perl
| added this in 1999. Perl pretty much had everything (Moose
| object system, etc).
| mpweiher wrote:
| > That aside, operator overloading is fits the specific
| "user glue" you are focusing on
|
| I thought so as well, for a long time, but it turns out not
| to be the case, because you can't properly do "connection"
| via call/return. You _can_ do it, but not "properly".
|
| The Problem: Architectural Mismatch ------
|
| The crux is that all mainstream and virtually all non-
| mainstream languages aren't really general purpose, though
| we obviously think of them and use them that way. They are
| DSLs for the domain of algorithms, for computing answers
| based on some inputs. Obviously with many variations, but
| the essence is the same. a := f(x). Or a := x.f(). Or a: x
| f. And the algorithmic mechanism we have for computing
| answers is also our primary linguistic mechanism for
| structuring our programs: the procedure/function/method.
| (Objects/classes/prototypes/modules are secondary
| mechanisms).
|
| (see also: the ALGOrithmic Language, predecessor to most of
| what we have, and allegedly an improvement on much of
| that).
|
| As long as our problems were also primarily algorithmic,
| this was perfectly fine. But they no longer are, they are
| shifting more and more away from being algorithmic.
|
| For example, if you look at how we program user interfaces
| today, there hasn't really been much progress since the
| early 80s or 90s. Arguably things have actually taken huge
| leaps backwards in many areas. I found this very puzzling,
| and the common explanation of "kids these days" seemed a
| bit too facile. The problem became a bit clearer when I
| discovered Stephane Chatty's wonderful, and slightly
| provocatively named paper "Programs = Data + Algorithms +
| Architecture: consequences for interactive software
| engineering".
|
| https://dl.ifip.org/db/conf/ehci/ehci2007/Chatty07.pdf
|
| He explains very well, and compellingly, how our
| programming languages are architecturally mismatched for
| writing GUIs.
|
| Once you see it, it becomes really hard to un-see, and this
| problem of algorithmic programming languages, or in the
| architectural vernacular I prefer, call/return programming
| languages pops up everywhere.
|
| Or how Guy Steele put it:
|
| "Another weakness of procedural and functional programming
| is that their viewpoint assumes a process by which "inputs"
| are transformed into "outputs"; there is equal concern for
| correctness and for termination (and proofs thereof). But
| as we have connected millions of computers to form the
| Internet and the World Wide Web, as we have caused large
| independent sets of state to interact-I am speaking of
| databases, automated sensors, mobile devices, and (most of
| all) people-in this highly interactive, distributed
| setting, the procedural and functional models have failed,
| another reason why objects have become the dominant model.
| Ongoing behavior, not completion, is now of primary
| interest."
|
| ---
|
| I also thought plain old metaprogramming facilities would
| do the trick, but in the end those are also insufficient.
| (For example, my HOM was entirely done via
| metaprogramming).
|
| Architecture Oriented Programming is a generalisation of
| call/return programming, not an extension.
|
| https://blog.metaobject.com/2020/04/why-any-fundamental-
| impr...
| Supermancho wrote:
| > He explains very well, and compellingly, how our
| programming languages are architecturally mismatched for
| writing GUIs.
|
| I'm not sure I agree this was the takeaway. It does
| highlight that handling of concurrency is percieved as
| anathema to an algebraic perception of realtime
| processing by some. Just as one might say that
| programming languages are architecturally mismatched for
| writing a biological system, GUIs are a more constrained
| exercise of the same concepts. I believe this is largely
| related to a "distributed setting" and the "ongoing
| behavior" that Steele refers to.
|
| (^4.3 paraphrased) GUIs are a set of small programmatic
| interactions that represent state manipulation.
|
| Shortly thereafter:
|
| > With interaction, state is essential in behaviors and
| the locality principle would require that all code that
| changes it is grouped. That pushed researchers to propose
| programming patterns based on finite state machines,
| Statecharts or Petri nets, but: when using a computation-
| oriented language, the transitions are implemented as
| functions or methods and the principle of locality is not
| met;
|
| Transitions (eg text fade-out) are not implemented as
| functions, as everyone knows. Transitions are performed
| concurrently alongside the state change. Losing control
| of the transition (or not being aware of it) by
| initiating a form of parallelism is not optimal to a
| developer who wants to maintain control, but is
| undeniably effective in performing the work. The fact
| that any module (say a label's fadeOut method) can behave
| concurrently at any time is a side-effect of information
| hiding, rather than a loss of main/subrouting empiricism.
| It does require a new set of concerns and architecture
| additions, rather than a full-blown teardown.
|
| P.S. What I always found most interesting about that
| paper is the side-criticisms about encapsulation.
| abernard1 wrote:
| I read the Shaw 1994 paper in full, and have a rebuttal paper
| that makes me confident the author's goal of an architectural
| programming language will fail.
|
| The paper is A Note on Distributed Computing (1994) [1], and a
| choice quote is: Historically, the language
| approach has been the less influential of the two camps. Every
| ten years (approximately), members of the language camp notice
| that the number of distributed applications is relatively small.
| They look at the programming interfaces and decide that the
| problem is that the programming model is not close enough to
| whatever programming model is currently in vogue (messages in the
| 1970s, procedure calls in the 1980s, and objects in the 1990s). A
| furious bout of language and protocol design takes place and a
| new distributed computing paradigm is announced that is compliant
| with the latest programming model. After several years, the
| percentage of distributed applications is discovered not to have
| increased significantly, and the cycle begins anew.
|
| Even in 1994, failures at achieving this dream were cliche. I
| will not resummarize the paper, but I will explain the crux of
| the problem with architectural languages.
|
| The problem with this dream is that to achieve it, (1) everything
| would need to be rewritten in a single language and (2) to
| support remote distribution, every procedure/interface/method
| would need to have full remote semantics. Many languages have
| tried this, including Smalltalk-sympathetic object message-
| passing languages like Erlang or Akka Actors. What you quickly
| realize when working with these languages is that the semantics
| of ordering, retries, idempotency, synchronization (all of the
| concepts that fit under the CAP theorem) are not easily expressed
| in a language. If you have anything remote whatsoever, whether
| that's an API or a database, the whole thing breaks down. Naive
| programmers using languages with message passing that say "when
| you need to scale, you can just make that function remote" find
| out very quickly that you cannot.
|
| The industry solution to this problem is one of interfaces and
| patterns, not all-encompassing languages. Use of an RPC or
| serialization data format (even heterogenous ones!) across
| subsystems provides transport glue, and another piece of software
| can provide semantics to make software work in concert together.
| If systems are connected together _actively_ , that software is
| called an _orchestrator_ , and it makes all the calls directly.
| If systems are connected together _passively_ , the subsystems
| themselves engage in _choreography_ , and need to support a
| partial protocol of idempotency, awareness of ordering, retry
| semantics, etc.
|
| [1] https://scholar.harvard.edu/waldo/publications/note-
| distribu...
| zvrba wrote:
| > The problem with this dream is that to achieve it, (1)
| everything would need to be rewritten in a single language
|
| Not true. Look at COM. It has a reputation for being "ugly" and
| "legacy", but having worked with it, it's a marvel of
| engineering. There's still no "modern" system that can compare
| with it, IMO. In a C++ project of mine I implemented a COM
| server (not _that_ difficult) and could control the program
| from Powershell "for free". IOW, I got scripting with no glue,
| no bindings, nothing -- just instantiate the component from
| Powershell and invoke methods on it. (Courtesy of IDispatch,
| default implementation provided by ATL.)
|
| > (2) to support remote distribution, every
| procedure/interface/method would need to have full remote
| semantics.
|
| COM and DCOM implemented that. Where it came short -- or where
| I failed to find the documentation -- is about customizing
| timeouts of remote calls. I found some documentation about
| defaults, but, IIRC, the setting was system-wide and not
| applicable to a particular "connection".
| abernard1 wrote:
| COM is an abject failure when you're dealing with a remote
| system. Like, I challenge you to find anyone with experience
| in heterogeneous systems software that thinks COM functions
| well.
|
| And so if we're talking about software that will always be
| hosted one one machine and memory space, fine, this can work.
|
| The 1994 paper brings up Spring which was implemented in a
| successful way with DI and AOP principles later in Java. It
| supported things like transactions across thread contexts,
| modules, etc. and those concepts were later adopted in other
| VMs like RoR or any other framework that used components +
| thread propagation.
|
| The problem always comes down to this megalomaniacal vision
| of a "unified"-anything. Aside from the agreement problem of
| adopting those concepts in new and existing languages, some
| of these concepts simply cannot be adopted in certain
| languages: thread context propagation, concurrency
| mechanisms, error handling. And even if we did not have the
| limitations of certain languages not supporting certain
| behavior, we have the problem that composition semantics are
| deeply rich in a domain.
|
| Creating primitives for composition and execution of code
| that persist across languages and systems does not work
| without consensus adoption of a language. The same reason why
| I can not go into IntelliJ, right-click, and "refactor"
| across my service architecture is the same reason why COM
| failed and why this will fail.
| mpweiher wrote:
| Hmm...interesting that you view this is a "rebuttal", as I
| generally regard it as further bolstering my point. I am
| certainly aware of it :-)
|
| First, I don't view Architecture Oriented Programming as being
| primarily about distributed programming, and so far my work at
| least has not been focused on it at all, though that is
| starting to change.
|
| Second, "everything would need to be rewritten in a single
| language" is not where I am going. At all. In fact, _not_
| requiring a single language is one of my central tenets, and
| particularly one of the reasons I reject Smalltalk-80, which
| very much wants everything to be in a single language in a
| single image.
|
| Third, "to support remote distribution, every
| procedure/interface/method would need to have full remote
| semantics". Nope, not at all. Only if you have a monomorphic
| view of language. The "Everything-is-a-"-ism. Also, almost all
| of these techniques went local-first, and then tried to add
| distribution. Which gives you the problem that remote semantics
| are not a trivial extension of local semantics. If you read the
| Note carefully, you will, er, note that it never talks about
| the reverse case, going from remote to local. For good reason,
| because the reverse case is fine.
|
| Which is one of the reasons In-Process REST works so well: it
| takes a distributed architecture and applies it (not 1:1, gotta
| adapt!) to the local case.
|
| Anyway, once you figure out that you can have polymorphism not
| just at the data-modelling layer, but also at the language
| meta-layer, you also start to understand that local/remote all-
| encompassing/not are false dichotomies. We have commonality and
| variability analysis, let's use it! These different mechanisms
| have some things in common, and others where they are
| different. If we open the language up sufficiently, we can
| capture both the commonalities and the differences in a way
| that is convenient and flexible.
|
| So when we get a new interaction mechanism, we write a new
| connector. Done.
|
| Going back to TFA and the John Hughes paper it quotes, the
| situation rightly critiqued in the Note is that of having one
| or maybe two "connectors" canonised into the language, one kind
| of glue. But as I write, we need _lots_ , not one or two. So
| adding different kinds of connectors is _the_ defining feature
| of Architecture Oriented Programming, and the exact opposite of
| the situation of baking one or maybe two into the language.
| igouy wrote:
| > ... Smalltalk-80, which very much wants everything to be in
| a single language in a single image.
|
| Except that Smalltalk implementations became a way to
| implement (and experiment with) other programming languages
| --
|
| "Metalinguistic Abstraction in Smalltalk-80 (or: Making
| Smalltalk with a Lisp)"
|
| https://www.researchgate.net/publication/2574069_Metalinguis.
| ..
|
| "Why Smalltalk Wins the Host Languages Shootout"
|
| http://scg.unibe.ch/archive/papers/Reng09bLanguageShootout.p.
| ..
|
| "Squimera: A live, Smalltalk based IDE for dynamic
| programming languages"
|
| https://hpi.de/fileadmin/user_upload/hpi/dokumente/publikati.
| ..
| Jupe wrote:
| > So when we get a new interaction mechanism, we write a new
| connector. Done.
|
| Interesting idea... but I'm struggling to get the full
| picture here... (I might be way-off here, but):
|
| Is this about "formalizing" connectors? Such that, once a
| connector technology comes along (RPC, REST, Web Sockets,
| GraphQL, who knows what next), such connectors could be
| implemented on either side of the boundary layers and then
| "selected" via some architectural language? I think the
| semantics of each "connector" demands different design
| constraints on either side of the equation.
|
| An overly simplified example: Java logging "appenders" allow
| dispatching log messages to different targets (stdout,
| filesystem, database, etc.) But, STDOUT doesn't "fill up",
| while file systems/databases do. DB's support querying, while
| STDOUT does not. DB's do get overwhelmed; should processing
| halt until such "overwhelmed" situations get resolved?
| Databases (should) include security; STDOUT does not. Etc.
|
| If there were different "types" of connectors defined, each
| with different behaviors, and those could be easily chosen,
| then maybe. But even the "types" would be solution-dependent.
| Retry rules, idempotency, serialization rules, compensating
| transaction support, etc. These topics (to me) are far more
| important than simply creating new connectors.
| abernard1 wrote:
| > These topics (to me) are far more important than simply
| creating new connectors.
|
| Yes. What this vision requires is pushing down a shared
| library or coordinated behavior into every new language or
| future technology that requires it.
|
| If the behavior is not actually required, and is more of a
| suggestion, then it's a "protocol" and requires interface
| modifications everywhere. And now we're back to "why do we
| need this shared code/language?" and "how is this different
| than patterns?"
|
| This model has been tried many many times. We just got rid
| of the ignominious stench of CORBA and its removal from
| Java in the last year after a 25 year failure. The
| architecture astronauts require their language to be
| guaranteed across wire boundaries which is a fancy way of
| saying that their uber-language needs to be implemented in
| all the subsystems for guarantees to work.
| mpweiher wrote:
| > What this vision requires is pushing down a shared
| library or coordinated behavior into every new language
| or future technology that requires it.
|
| NO IT DOES NOT. Please do not repeat this false claim.
|
| > This model has been tried many many times
|
| No. What YOU describe has been tried many times. It
| doesn't work. We agree.
|
| > The architecture astronauts require their language to
| be guaranteed across wire boundaries
|
| Yep. This does not.
|
| > which is a fancy way of saying that their uber-language
| needs to be implemented in all the subsystems for
| guarantees to work.
|
| Yep. Which is why that is a bad idea.
| mpweiher wrote:
| > Interesting idea...
|
| Thanks!
|
| > but I'm struggling to get the full picture here...
|
| Me too, but I've been struggling much longer :-)
|
| > Is this about "formalizing" connectors?
|
| _Operationalizing_ more than formalising, although that is
| a variant of formalising. "Let's actually program using
| architectural abstraction", rather than just (variants of)
| procedural abstraction. Or rather than just using
| architectural abstraction to describe systems that we then
| still have to program procedurally.
| Jupe wrote:
| Ok, call it "operationalize" vs. "formalize".
|
| I'm still completely unsure how my logging example above
| would play-out. I've briefly looked at your code samples,
| but still not getting it. I just don't see how this is a
| "programming language" thing.
| abernard1 wrote:
| > But as I write, we need lots, not one or two.
|
| And those connectors need to be pushed down into the
| subsystems. This is the problem. Whatever the language used,
| you've effectively described a shared library, and one that
| needs to be handled by external cooperating services
| (including data stores).
|
| > For good reason, because the reverse case is fine.
|
| It is not, as per my above comment. Consistency requires bi-
| directional participation between actors if done at a uniform
| language _or_ interface level, and good luck getting MySQL or
| Postgres to push your semantics into their software.
|
| For any HN users who are reading this and think "hey, writing
| everything in one language is totally fine!" please consider
| the first day you acquire a company / are looking to be
| acquired by another company that didn't use your language.
| mpweiher wrote:
| > you've effectively described a shared library
|
| You're creating a false dichotomy. Shared libraries are
| totally fine.
|
| > It is not, as per my above comment.
|
| Yes it is, as per my comment.
|
| > good luck getting MySQL or Postgres to push your
| semantics into their software.
|
| No luck needed as there is no such requirement.
|
| > "hey, writing everything in one language is totally
| fine!"
|
| Once again: you are completely making up this "everything
| in one language" bit out of nothing. There is no such
| requirement or even goal. Though once you find out how nice
| it is to program in that language, you might _want_ to
| write everything with it.
___________________________________________________________________
(page generated 2021-12-20 23:01 UTC)