Earlier quoted context omitted.
Or any other major commercially used language right now? Look, I'm glad you work in a language where you don't need to describe those things any more, but the vast majority of people still do. And for that group of people, understanding that "Design Patterns" is a dictionary, not a recipe book, is a really important thing.
What modern version of a "commercially used language" needs patterns a la GoF? Most of them simply disappear as the language is made more expressive than the horror that was pre-standard C++. Modern Java or C# are not at all like that. It's not a bad book if you read it as positive account rather than a normative prescription, I have the dead tree version on my bookshelf, but it's also a narrow, historically-informed…
The plan-execute pattern
51–60 of 81 posts
Re: The plan-execute pattern
#52"I feel uneasy about design patterns. On the one hand, my university class on design patterns revived my interest in programming. On the other hand, I find most patterns in the Gang of Four book to be irrelevant to my daily work; they solve problems that a choice of programming language or paradigm creates." My relationship with design patterns changed when I stopped viewing them as prescriptive and started viewing t…
Descriptive of the kinds of workarounds that a lack of proper abstraction capabilities made necessary in ancient Java.
One form of defunctionalization is the well-known transformation of a recursive function into one that uses an explicit stack.
The thing is, I don't think defunctionalization (from the equivalent functional form) present in object-oriented languages is strictly worse. Defining things with explicit objects may sometimes be more understandable, and more extensible; for example, the command pattern could be implemented as a closure, but then you can't later add an "undo" method to it later.
Here's an example where defunctionalization makes the code more readable, not less.
Haskell has a difference list [0] data structure; the purpose of it is that you can concatenate two difference lists in constant time, and then turn it into one long normal linked list in O(n) time. This gives much better performance than concatenating lists directly all the time. It's a lot like a rope, except for lists, not strings, and it's immutable.
But the code is somewhat cryptic; it makes use of partial application and stores the tree in terms of partially applied functions. The "Demystifying DList" [0] article I shared shows a defunctionalized form explicitly defined as a tree in concrete data types.
To those interested in it, if you're familiar with the syntax of ML-like languages, I'd recommend the papers [2], [3], and [4].
[0]: http://h2.jaguarpaw.co.uk/posts/demystifying-dlist/
[1]: https://blog.sigplan.org/2019/12/30/defunctionalization-ever...
[2]: "Defunctionalization at work": https://www.cs.purdue.edu/homes/suresh/502-Fall2008/papers/d...
[3]: "Refunctionalization at work": https://www.brics.dk/RS/07/7/BRICS-RS-07-7.pdf
[4]: "Continuation-Passing Style, Defunctionalization, Accumulations, and Associativity": https://www.cs.ox.ac.uk/jeremy.gibbons/publications/continue...
Re: The plan-execute pattern
#53Earlier quoted context omitted.
At the level where you're executing assembly every language is the same. Which is a way of saying that assembly is homoiconic. And even in C you're more than capable of W&Xing your binary to change behavior at runtime but you're doing it at a level below the language. It's the (not completely unrestricted) self-modifying bit that's cool and unique. You can go into an existing function and change what it does by manip…
How about monkey patching in languages like Javascript and Python? I'd also say "homoiconic" is a less important concept than people would think. A while back I was writing a lodash-like library for Java because I don't like the Streams API (though in the end I just used the collectors from the Stream API because they were easy to fit into my system) One thing I found frustrating about that was that I didn't want to…
Re: The plan-execute pattern
#54Earlier quoted context omitted.
What modern version of a "commercially used language" needs patterns a la GoF? Most of them simply disappear as the language is made more expressive than the horror that was pre-standard C++. Modern Java or C# are not at all like that. It's not a bad book if you read it as positive account rather than a normative prescription, I have the dead tree version on my bookshelf, but it's also a narrow, historically-informed…
How are (for example) Adapter and Proxy not fundamental patterns applicable in most programming languages? Many people take them for granted today and maybe don’t think about them as design patterns, and a modern description will use different examples than the GoF book, but they are patterns nonetheless, and the GoF book contributed a lot to them having universally agreed and well-recognized names.
Adapter is an OOP-ism. If you take, for example, any ML-family language, you won't find anything of the sort. Even staying in OOP-land, in a language where inheritance goes on the prototypal chain, you wouldn't solve the problem like that.
Proxy is a more general concept, but the way it's described in GoF is still tied to a class/interface/implementation mechanism. In other languages it would often just be a function. I don't know what overarching lesson you would draw from it except "write code to do things".
As I said I don't think it's a bad book, it's definitely something you should read, even just for the meta-level language it (successfully!) introduced. Where I differ is that I don't believe the book is quite as successful at transcending the "traditional" OOP mechanisms. It's mostly "how to accomplish things in Java 1.5".
Re: The plan-execute pattern
#55Earlier quoted context omitted.
I suppose that, if I'm asked "What is something that makes homoiconic languages special?" and the answer can't be that it makes macros easy, then I would say it's that it's easy to write an interpreter for that language in the language itself, because the data structures that form the syntactic structure of the language are themselves first-class objects. That's why the interpreter from the appendix of the LISP 1.5 m…
I like that answer, and want to add to it. Scheme was the first language with lexical scoping of variables, and after Scheme was published in 1975, most new languages have had lexical scope and most languages used now (Java, C#, Javascript, Rust) have it. I think the qualities you describe make it easier to experiment with (Lisp-like) languages and language implementations, which is why lexical scope appeared in a Li…
Re: The plan-execute pattern
#56Earlier quoted context omitted.
In principle. Most people find somebody else's metaprogramming-heavy Common Lisp difficult to work with. When you look at classic programs from the golden age of AI you find they either rewrote their Lisp prototype in C++ for speed or the Lisp program (like an expert system shell) is much smaller than you imagine possible but it looks like the engine of a UFO.
Do you have a link to one of these UFO engines that I can study up on?
Re: The plan-execute pattern
#57Earlier quoted context omitted.
I like that answer, and want to add to it. Scheme was the first language with lexical scoping of variables, and after Scheme was published in 1975, most new languages have had lexical scope and most languages used now (Java, C#, Javascript, Rust) have it. I think the qualities you describe make it easier to experiment with (Lisp-like) languages and language implementations, which is why lexical scope appeared in a Li…
It's funny, because I had just recently read about there being a bug in that interpreter due to dynamic scoping, but forgot about when writing that.
Re: The plan-execute pattern
#58I spent the first bit of this article going "Wait isn't that just what a finite state machine is?" and then was subsequently super satisfied by the next bit where they say that this is indeed a really good way to handle the general case Reducing program logic to a small state machine and business logic that interacts with it in predefined ways is a pattern I learned while doing asynchronous netcode for a game project…
It's quite sad that generał purpose language designers are not yet at the stage of daring to provide any convenient syntax for high level concepts like state machines or entity component systems.
[0]: http://mbeddr.com/
Re: The plan-execute pattern
#59There are a wide range of “patterns” that derive from compilers that are the higher teachings behind functional programming and OO patterns. People don’t usually call them patterns. (Scheme isn’t special because it has closures, it is special because you can write functions that write functions.) Reminds me of a thread a few days back when someone was talking about state machines as if they were obscure. At a systems…
When Scheme was developed, it was special, because it used lexical binding and provided lexical closures. That was new. Writing functions that write functions would not have been new, since LISP had that already -> including Maclisp, which was used for the first SCHEME implementation. Later Scheme got used for exploring different macro systems.
Re: The plan-execute pattern
#60Earlier quoted context omitted.
How are (for example) Adapter and Proxy not fundamental patterns applicable in most programming languages? Many people take them for granted today and maybe don’t think about them as design patterns, and a modern description will use different examples than the GoF book, but they are patterns nonetheless, and the GoF book contributed a lot to them having universally agreed and well-recognized names.
Neither of them is a fundamental idea about programming in general, unless the only languages we are considering are Java, C#, and Python spoken with a thick Java accent. Adapter is an OOP-ism. If you take, for example, any ML-family language, you won't find anything of the sort. Even staying in OOP-land, in a language where inheritance goes on the prototypal chain, you wouldn't solve the problem like that. Proxy is…
This is incorrect. For example in SML you have signatures and structures, which are roughly analogous to interfaces and implementing classes in OOP. And SML functors are adapters that can adapt a structure of one signature to a different signature. Or they may create a proxy structure for a given structure. Furthermore, you could easily imagine a derived language where functors are just regular functions and structures are regular values.