Interesting, I never knew that old style existed.
In Oz which is a quite unknown programming language, you can declare a variable by stating it’s name and then you can eventually bind a value to it, if two different threads try to bind a value to it, the first one binds the value and the second one runs a “unification” algorithm to unify the value it wants to bind with the value that is already bound to it, if the values are incompatible/not-unify-able, then that is a unification error, which basically is a runtime error in the thread that tried the unification, but if the unification works then the values effectively get merged.
This allows you to write programs that deal with partial values quite easily, infinite lists and lazy data structures and so on.
You can write different parts in different styles, have eager parts, or lazy parts, it also has some logic and constraint programming things (I haven’t tinkered with those a lot yet).
You can write a lazy function and in case it returns a lazy data structure, that depends on some code that eventually results in a runtime error, the error gets wrapped in a “failed value” which then basically packages the error as a value so that it is then triggered wherever that lazy data structure is consumed.
It’s variables are data-flow single assignment variables, that means that you can declare a variable that will become determined at some future point in time, but once it becomes determined it never changes. This allows for interesting programming patterns where you can use these variables to cooperate between cooperative threads and you can write programs that act deterministically while also being concurrent without explicit synchronization operations (except just the default semantics of these variables).
Overall I find that language quite interesting, it also has a huge text book that explains all the core features of the language and how they interact to create a language that allows for many different paradigms to co-exist and be combined in different ways within one language.
I find it a bit sad, that almost nobody seems to know that language, I found it helped me understand a lot of programming concepts better and it is a lot of fun to play around with some of its ideas and concepts. The language also seems quite dead, it seems like it is an academic language that never found somebody to continue its maintenance or evolve its ideas.
I sometimes tinker on an interpreter that is based on the language (implementing a small part of it), but I currently don’t know when that will reach a point of quality/usability, where I will consider making that available.
To bring it back to the topic, with that language you have procedures and functions, functions are just an expression syntax on top of procedures that have a single out parameter.
And because of the crazy/interesting dataflow variables, every parameter can be undetermined or bound to a specifc value, if the program hits a variable that is needed (the value needs to be determined so that evaluation of the program can continue) then that thread is suspended until the variable becomes determined.
Then when the variable is determined it is const from that moment onward, but if it is an aggregate data structure it could still contain holes which are still undetermined.
This means you can write programs that just go to sleep/deadlock quite easily, but on the other hand it is harder to write programs that fail because of evaluation order. (And you could write an interactive editor for the language where such a deadlock would act more like a breakpoint, where you could then edit the program in some way so that it can continue)
So in a way in that language everything can be asyncronous or syncronous and it all interacts with another via these variables. (I guess you could say everything is asyncronous but a good implementation could make a lot of important parts syncronous for speed improvements) I guess the tricky thing is how to write an efficient implementation for that, that actually does the thread scheduling etc. behind the scenes without causing huge slowdowns compared to languages that use eager evaluation, it calls its own evaluation strategy dataflow.
Then additionally Oz doesn’t really have static types, it is one of those uni-typed dynamic languages, however in later parts of the book it also discusses constraint programming and if you tilt your head and squint your eyes, then constraints on variables in an interactive editor could potentially look awfully similar to static types if that constraint information was also used to optimize the program behind the scenes, just that the constraints could be more general than types, more like dependent types and probably also very hard to implement well.
So I guess to summarize, if we are already discussing all kinds of things, why not also discuss evaluation methods: eager, lazy, dataflow