Unfortunately OP submitted the About page rather than the home page. From https://objective.st:
> Objective-S is possibly the first general purpose programming language. It makes creating well-architected and efficient programs not only straightforward, but easy, fun and fast.
> Yes, this sounds insane, programming languages we call "general purpose" number in at least the hundreds, starting with C, Java, Smalltalk, Ruby, Python etc.
> However, these are actually DSLs for the domain of algorithms, and this architectural mismatch, the fact that we have to use algorithm DSLs for tasks that are not primarily algorithmic in nature lies at the heart of much of the pain we have in software development today.
I like what I've seen of Objective-S; I completely support and appreciate the thought you've put into it, but I don't think that's a great claim to make.
Implicit is the claim that C, Lisp, Modula-2 are not general purpose languages, which is ridiculous. You may not want to deal with C's various problems, and it may take pages and pages of code to do in C what one line of Objective-S could do, but doesn't mean it can't be done.
I read the rest of the About page and didn't see anything supporting this claim. What makes Objective-S more general than other languages? If it's the four architecture "styles" listed, I'm pretty sure other languages can do the same:
* OO and Call/Return: Supported by every OO language.
* Pipes and Filters: Supported by every functional programming language.
* REST: Supported by many modern languages. (Go springs to mind as a typical example.)
* Implicit Invocation / Event Broadcast: Supported by many modern languages.
I believe the argument is that this language has special syntax for these various "architectures", instead of other languages where you express those ideas with the syntax of calling/returning functions.
> What makes Objective-S more general than other languages?
They describe that on the site. The notion is that most of our modern languages are more geared towards expressing algorithms and data structures, and not so great for expressing programs better suited to different architectural styles that don't necessarily follow a strict call/return type of semantics.
That's not entirely wrong. You can build a library/eDSL in a call/return language that will implement other architectures, but it will still look and behave like a call/return abstraction. Ostensibly Objective-S's goal is to make programs written in other architectural styles look like they are expressed natively in those styles, rather than an embedding of that style in a call/return architecture.
I'd like to see Objective-S implement Prolog or other logic programming to put that claim to the test. The other architectures they list aren't too far from call/return style, but logic programming certainly is.
Thank you for getting it and expressing it so clearly.
> You can build a library/eDSL in a call/return language that will implement other architectures, but it will still look and behave like a call/return abstraction
EXACTLY!
We can build, but we cannot express. So it will look like a weird/complicated call/return abstraction, because it isn't "native". So you get a tradeoff between architecture and simplicity, a tradeoff that shouldn't be there. (And all sorts of other 2nd order effects).
> Ostensibly Objective-S's goal is to make programs written in other architectural styles look like they are expressed natively in those styles, rather than an embedding of that style in a call/return architecture.
EXACTLY!
We need to be able to express those alternative styles natively and naturally, otherwise we are always going to be stuck in the tradeoff between architecture and simplicity, or as I call it The Gentle Tyranny of Call/Return.
> I'd like to see Objective-S implement Prolog
That's actually on the list, as a stretch goal. One of my (many) inspirations was this great 1988 article about adding support for backtracking to Smalltalk, without modifying the kernel: https://dl.acm.org/doi/10.1145/62083.62094 A tour-de-force. And yet...not really something you'd ever use. The other problem is that Prolog, in my understanding, is actually too "algorithmic" or "procedural" to expend too much effort on, at least when you consider its interface After all, the basic interaction with Prolog is to ask it a question and then it rummages around in its database of rules and facts and spits out an answer.
I am personally more interested in constraints, particularly (one way) dataflow constraints. Those I am now able to get out of the other architectural components, and without significant algorithmic intermediation.
You don't seem to know prolog well if you consider more "algorithmic" or "procedural" than, say, smalltalk. Sure, it's not 100% declarative, because even prolog is bound by such down-to-earth concerns as performance and termination. But there is no such thing as a "function call" in prolog.
I for one would like to see you put your claims to the test and implement a prolog in objective-S, indeed. Half of it is backtracking; the other half is unification as the main connector. Good luck.
> You don't seem to know prolog well if you consider more "algorithmic" or "procedural" than, say, smalltalk.
Actually: I do. Which is why I gave the precise reasons for what I wrote, which in turn your critique does not touch on at all.
So again: Prolog is very much a system that computes answers to questions. If you have a Prolog-based system that does not, please point it out to me and I will be happy to have a look.
"computes answers to questions" applies to any system with a REPL. That's really not a technical term at all.
If you want to be more precise, you could say prolog tries to satisfy the goal it's passed (via unification and resolution). That's not the same as the call/return paradigm. The only unifying concept (heh) for these two is, well, "computes something".
Anyway, for me, that confirms that your statements are not grounded in anything but vague terms you throw around, like "algorithmic". If you want to convince people that your flavor of smalltalk is actually, qualitatively _more expressive_ than all the hundreds of other languages, you're going to have to define a precise notion of expressiveness and show that only your system matches it. Since you've recognized that it's just about being more expressive, since you can write anything in any language; you probably also need to have cold numbers on how much more expressive objective-S is.
You picked a different tool for each of the different entry, while the author is arguing their programming language is general purpose, because it allow to do each entry you listed, in a single language.
Right. A different _tool_ because they described paradigms aren’t language paradigms- they’re patterns or frameworks expressed more easily but also more commonly in some languages than others because of completely unrelated historical accidents.
We have different languages because we do different things with them, and any Turing complete language can achieve any desired program. So what we’re left with is a misunderstanding about what is the “language”, “runtime”, and … “pattern?”
There is no useful definition of “general purpose language” except to disqualify languages that are not general purpose. That’s a fine distinction to make when describing how SQL will never replace JavaScript, Java, or C- but a useless distinction when describing the characteristics of your new language that is also Turing complete.
In particular item 4 “Implicit Invocation / Event Broadcast” is one of those things that hides an enormous amount of implementation complexity that arguably shouldn’t be made magical. Like async / await or go channels, the details make all the difference and there’s not a one size fits all approach that you can take without creating serious problems for other cases. It has to be an opinionated and therefore (sometimes/often) approach depending on what you’re attempting to accomplish.
Yes, exactly: these architectural styles should not be built into the language. And (mostly) aren't, in Objective-S. You should be able to build them yourself within the language and then they should have linguistic support that is as good / indistinguishable from built-in stuff.
Just like Smalltalk does with, say collections: most languages have/had built-in support for one type of collection (or maybe two), typically arrays of a homogenous type. If you wanted to build your own collection, you could, but were limited with functional call syntax fora accessing that collection.
With Smalltalk, collections yourself create are on an equal footing with collections that are in the base library. And that's an idea that has slowly percolated through the PL community and to practice.
But not with architectural styles. Our mainstream programming languages typically support exactly one architectural style: call/return. That's the only one that allows abstraction. If you want a different one, you can build it, but you cannot express it. And that's a huge problem, as any non-default architectural style gets a huge expressiveness penalty.
Previous languages that support alternative architectural styles usually have exactly the problem you've described: they offer exactly one implementation of that architectural style baked into the language, and that's it (Go channels are an example). And if you need something even slightly different, you're back in implementing + expressing with call/return, because that's the only style that allows abstraction. Sigh.
With Objective-S, the idea is that you get to implement whatever you want in terms of architectural styles (connectors, components) and then get to surface it with syntax appropriate for architectural connection. And the implementation will usually also be call/return based, because that's what we currently have. Although I am starting to see places where I can actually implement a connector in terms of other connectors directly without mediating via procedure calls.
(The one-way dataflow connector |= can be built from a storage combinator and a notification mechanism, and these can be pluggable. Very neat, particularly because building dataflow constraints naturally was one of my goals and this exceeds that goal).
Of course there's a bit of a chicken/egg problem, in two variations: first, I must provide some "built-in" components and connectors that go outside the default set, otherwise the whole thing is useless. And there it turned out that the initial set (polymorphic write streams, storage combinators, dataflow constraints) turned out so useful and general that they sort of become "the thing", even though they aren't. The second, related chicken/egg problem is that both conceptually and from an implementation POV, we have to start somewhere concrete, because this is an abstraction mechanism that doesn't exist yet and thus nobody really has a clue how it should work. So things aren't as abstract/general yet as they should be.
Alright then, what if the architectural style one wants is type-level metaprogramming? Or even just having a rich type system with inference, like Haskell? Can objective-S do that? What about monads, typeclasses, etc. which are arguably pretty extensible and DSL-y too (see Xmonad for example)?
If not, maybe this whole definition of "general purpose" is just meaningless.
Hmm...not sure what you find gobbledygooky or jargon-y about this...it is about as clear as I can make it. I mean it is definitely a surprising even somewhat baffling insight, but once it is there it seems pretty clear.
1. Historically, computers were created to compute results (hence the name) using algorithms. So you have a function/procedure that you give parameters, it executes the algorithm, spits out the answer and terminates.
2. Programming languages reflect this. Heck, the granddaddy of most mainstream programming languages is ALGOL, the ALGOrithmic Language. https://en.wikipedia.org/wiki/ALGOL
3. The majority of programs/system today are not like (1). See for example Chatty:
"Another weakness of procedural and functional programming is that their viewpoint assumes a process by which "inputs" are transformed into "outputs"; [...] Ongoing behavior, not completion, is now of primary interest."
Happy to answer any more specific questions. Again, the insight is definitely somewhere between surprising and outright baffling, particularly because call/return is very much paradigmatic in the Kuhnian sense: a set of shared and implicit assumptions, so things we don't even think about but that form the basis of everything else.
Historically programming languages were designed to transform some initial state of the machine to some other state having some specified desirable properties. An algorithm is simply an “effective procedure” for instructing the state machine on how to go about modifying itself to achieve that end. Computing functions can and has been a use case sure, but it’s never been the only one. Simulation for example has a long history in computing. In the simulation use case there is no defined end, you just let it run and observe.
Of course one can model each state transition as a function from machine state to machine state, but that’s just a mathematical way of talking about the machine and not a programming paradigm.
You can see the legacy of the above in C, where many problems are best expressed directly as state machines. There is some confusion here because C chose to conflate functions, procedures, and subroutines, so code organization encourages use of C functions. The void return type is a workaround. It would also be idiomatic C to, for example, dispense with functions and use labels and switch/goto to organize state transitions.
Your confusion is understandable since virtually every programming language in widespread use today is at least in some sense a spiritual successor to C in the sense of adopting many of its conventions.
By the way I think Objective-S is interesting and I am glad it’s being developed. So please consider impressing readers with what actually is impressive about it.
And then there's some Lisp descendants, though those can't really be considered mainstream. Lisp's core is eval/apply.
Prolog is an interesting case, but in many ways it just embeds the algorithm in the language. From an outside perspective, you give it something to evaluate and it runs off and returns an answer.
The main outlier is the Unix shell, which, like Objective-S, is connect + run: you configure a pipeline out of filters, connect them up and then run the whole thing. And this can and will continue running indefinitely. Call/return is sort of a (common) special case. And that's interesting, because it seems to actually be easier to make call/return a special case of connect+run, but we do it the other way around.
It doesn’t appear that you understood anything I said.
This might be because you observably don’t know what an algorithm is. It has nothing at all intrinsically to do with call and return.
I’m afraid any further discussion will be fruitless, but on the off chance you tire of getting predictable responses to ridiculous assertions that promote what you believe to be a deep insight, then please refer back and search for the insight required to understand what you’re being told here.
"Sub-routines seem to have two distinct uses in programmes. The first and most obvious use is for the evaluation of functions, a simple example being the evaluation of sine x given x. The second use is for the organization of processes..."
I didn’t respond regarding Algol because you said nothing of immediate relevance requiring a response. I’m familiar with the history of Algol 60. I’m not interested in regurgitating wikipedia with you. Here[1] is a good primary source on the history of programming and significance of Algol in context for you to read. Note that call/return is nowhere mentioned.
Stop chasing tangents and address your core misunderstanding. Algorithms are not call and return.
OK, thanks for clarifying that you did not understand what I wrote in the least bit and have no intention of learning, not even so far as looking at the relevant information on Wikipedia, which would help clear up your misunderstandings.
Once again: ALGOL is the ALGOrithmic Language. It might have something to do with algorithms. Now please tell me what the main structuring mechanism for code is in Algol (and C, for that matter).
Do you at least understand that any language derived from C is therefore also derived from ALGOL?
And nobody claimed that Algorithms "are" call/return. They are just intricately related in both history and current practice, particularly once we stopped treating "primitive" operations separately and instead started understanding them as functions/procedures as well. (In C, something like multiplication is considered an operation (via the * operator), whereas most current languages treat multiplication is considered a function that the compiler understands and will directly emit machine code for).
So please stop chasing tangents that have nothing to do with what I wrote and what I've done and address your core misunderstanding(s).
Let's put your claim that Objective-S can be used to implement different architectural styles to the test: implement Prolog or some other logic programming language as an architecture!
I think the other architectures you've listed there aren't different enough from the typical call/return style of algorithm-centered languages, but logic programming certainly is.
"The Data stack holds parameters for and results of subroutine calls. This distinction between control and data minimizes the cost of subroutine calls.
"The benefit of having a stack reserved for return addresses was that the other stack could be used freely for parameter passing, without having to be “balanced” before and after calls"
If you can build Objective-S in a language then isn't the language that contains the set of Objective-S and other languages in theory more general purpose? I'm not sure I get the definition.
Yes and? EVAL isn’t call/return, it’s a mathematical function from type S-expression to type S-expression modulo some details. At least it was to start, Lisp now has side effects which considerably opens up the domain of easily expressed programs.
Perhaps you’re confused because you’ve never done anything nontrivial in a modern Lisp and the word function is so heavily overloaded?
Here[1] is an example of the Actor model expressed in Common Lisp. It shows a paradigm that is very much not call and return expressed cleanly and directly.
> EVAL isn't call/return, it's a mathematical function
Hmm...a "function" you say. What do you do with a function in a computer system? I mean, for it to actually do something?
Look at it?
Digest it?
Sing it?
Or maybe...call it?
And when that function is done, after you called it what does it do?
Dissolve?
Perambulate?
Or maybe: return?
Of course, a function that you call and that then returns has absolutely nothing to do with the call/return architectural style. Sorry, my bad, what was I thinking?
And no, your example does not invalidate what I wrote, as it doesn't implement it "cleanly and directly", at least not to my standards. Maybe to yours, but not to mine. The problem with Lisp DSLs is that the result always just look like Lisp, and Lisp is eval/apply.
So
(send my-actor message_args)
very much looks like a Lisp function call. It seems to be the function "send" with arguments "my-actor" and "message_args". (Of course the Actor model is close enough that it might not matter). Which is exactly the problem we have in other languages.
At the very least, it would have to be
my-actor async messge_args
For it to qualify as resembling an actual send and not just another function call.
I can also write:
source.connect( target );
And to you that would apparently mean that I have implemented pipes and filters "cleanly and directly" in, say, C++ and Java. And it would be just as wrong.
If you think that the operator in a Lisp form needs to be a function and if it were a function that it needs to return, then that's not the case in Lisp for probably 50+ years.
Lisp is not generally following an eval/apply model and/or call/return model.
(send foo :beep)
This can mean:
* SEND is a function, it's is called with the evaluated args, it may return or not
* SEND is not a function and does something arbitrary
Generally the model is:
(operator ...)
a) Where operator can be a special operator, something built-in, which is not a function.
For example (throw 'foo "hello"), where THROW is a built-in special operator which transfers control.
b) it could be a function, which is more like a procedure. The function could also do transfer control to some other place and never return, for example by calling THROW or by other operators which transfer control
c) it could be a macro. The args don't need to be evaluated. Thus no eval/apply. The macro returns code, which gets executed. Again this could use other primitives, which transfer control.
d) it could be a function itself like ((lambda (x y) ...) 13 14)
What it needs: the first element in a Lisp form needs to be an operator, which is usually a symbol with some meaning: special operator, function, macro.
Scheme then in the mid 70s added that the first element is evaluated and can be a general variable. Thus in Common Lisp:
(let ((actor (create-actor :name 'foo)))
(send-async actor :init))
could be written in Scheme
(let ((actor (create-actor 'foo)))
(actor :init))
given that the actor would be a function object.
> my-actor async messge_args
Writing it as
(send my-actor :async message-args)
Is basically just a syntactic transformation. The semantics could be the same.
If we want write
(my-actor async message-args)
then one would typically
a) switch to a model like in Scheme, where the actor would be a function object
or
b) write a translator for a new language to ordinary Lisp
Really? I stopped reading at this spot, since this claim is so ridiculous.
https://en.wikipedia.org/wiki/General-purpose_programming_la...