Live data from Hacker News

The plan-execute pattern

mmapped.blog

71–80 of 81 posts

Re: The plan-execute pattern

#71
post #49
post #46

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…

These are still relevant even in modern Java or C# or C++. Just because the language can express things in a more modern way doesn't mean you always will. Or should. Or can.

And I agree that it's a historically informed perspective. But guess how much code is modern code versus historic^W legacy code. Nobody is arguing (well, at least I'm not) it's a foundation of CS. Nor is any reasonable person arguing it's a normative prescription - see the comment about "not a recipe book".

It's the equivalent of a dictionary/thesaurus for a writer - necessary, but not world-changing. Always has been. Still is.

Re: The plan-execute pattern

#72
post #6

Earlier quoted context omitted.

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.

UnrealScript has (had?) built-in syntactic support for state machines. I think Jonathan Blow had some ideas for language support for ECS in Jai, or at least support for structure-of-array stuff. I'm not sure if it panned out. My experience with both of those patterns is that each implementation tends to have enough unique requirements that it's hard for a language to find a sweet spot where built-in syntax can cover…

We needed a state machine to manage app startup in Ardour, and initially (since we're using C++) we used a boost library for this.

It was pretty cool - you just create a text based table describing state transitions and various methods that execute before, after and as a result of the transition, and it generates all the code for you. It was very, very quick to get things basically working with this template library.

But then ... we had to start dealing with corner cases and some fiddly little details of precisely how this FSM was supposed to work.

We still have the table in a comment in the code, because it's probably one of the easiest things to read to understand the FSM, but the implementation is now just the usual C/C++ nested switch statements.

Re: The plan-execute pattern

#74
post #66
post #43

[ninja author] I like how their example is a build system, given Ninja constructs an object literally called "Plan"[1]. However, when I later revisited the design of Ninja I found that removing the separate planning phase better reflected the dynamic nature of how builds end up working out, where you discover information during the build that impacts what work you have planned. See the "Single pass" discussion in my…

Indeed plans get hard when the plan has to be dynamic. Pulumi and terraform have similar troubles.

Cast that thought in terms of state. If all the state can be represented up front then plans are great. But if the understanding of the problem changes half way through a project and previously un-identified state is found during the runtime then a plan-execute pattern will get ugly quickly.

It is good to name things, but the challenge to manage isn't in the implementing of a plan-execute architecture. It is in identifying the threshold where there is enough runtime state in the problem domain that a plan-execute architecture can't be sustained.

Re: The plan-execute pattern

#75
post #41

Earlier quoted context omitted.

Do you have a link to one of these UFO engines that I can study up on?

See https://www.cs.unm.edu/~luger/ai-final/code/LISP.ess.html and the more modern http://www.clara-rules.org/

That code looks readable to me. It is well formatted and the functions are small, with simple control paths. No variable assignments or loops to unravel.

It defines no macros, but refers to some defined elsewhere; the instructions say you are supposed to use a certain other source file related to Chapter 14 of a book.

I could probably ramp up on this in a day, if I had reason to.

Re: The plan-execute pattern

#76

Earlier 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.

Though I've only seen other people's Lisp projects online and in books, I haven't seen metaprogramming that made it less readable - usually, it makes the program more readable. At most, it defines a DSL, e.g. COMFY 6502, which is a thin wrapper over 6502 assembly implemented as a DSL in Emacs Lisp. But most of the time, macros provide a readable interface over a bunch of hard-to-understand boilerplate generated code.…

Right. The reliable reference point for comparing the program with macros is the same program in which the macros have been expanded.

Comparing it to some completely different, nonexistent program which solves the same problem without macros is fallacious.

Re: The plan-execute pattern

#77
A key motivator of this pattern is identifying invalid configurations early, and in particular preventing execution if a known invalid configuration is requested (e.g. fail-fast). For example, if you delete a resource from a configuration, and it ends up being one that is depended upon by another, a "plan-execute" pattern should identify this at the start and prevent any execution from happening.

Re: The plan-execute pattern

#78
post #5

There 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…

> Scheme isn’t special because it has closures, it is special because you can write functions that write functions. I swear this informational parasite will never die, any language with eval can write functions that write functions. What makes Scheme and other homoiconic languages special is that the code that is currently executing can be manipulated live like any other data structure and it works.

No, Scheme does not make self-modifying code defined.

See this example in R7RS for the set-car! function:

  (define (f) (list 'not-a-constant-list))
  (define (g) '(constant-list))
  (set-car! (f) 3)              ⟹  unspecified
  (set-car! (g) 3)              ⟹  error
It is an error in Scheme to modify a constant list. The implementation is not required to diagnose it (an exception is not required), which basically means that it's undefined behavior.

In Common Lisp, it is likewise undefined behavior.

Running Lisp code is not necessarily still made of nested lists. That may be the case if it is interpreted, but Lisp code is often compiled. Modifying the original nested lists that comprise the source code will do nothing to the object code, except if those parts are modified that serve as literals, which the compiled code may be sharing with the source code.

In short, your understanding is wrong. When Lisp people talking about modifying the running program, that just refers to redefining functions and other entities. Common Lisp has strong support for redefining classes of which there are existing instances. Existing objects can be migrated to new representations in the redefined classes.

None of that is related to homoiconicity or related topics.

Re: The plan-execute pattern

#79
post #25
post #5

Earlier quoted context omitted.

> Scheme isn’t special because it has closures, it is special because you can write functions that write functions. I swear this informational parasite will never die, any language with eval can write functions that write functions. What makes Scheme and other homoiconic languages special is that the code that is currently executing can be manipulated live like any other data structure and it works.

>code that is currently executing can be manipulated live like any other data structure and it works. I heard from someone who did this with x86(-64?) assembly in production code, but I don't know the details.

[deleted]

Re: The plan-execute pattern

#80
I don't see how that is a pattern. Rather, it's a new name to an old thing: writing a virtual machine or an JIT-compiler. And no, interpreter is not a "special case of plan-execute pattern": by definition, if data structure can be run, it means it is a program for some virtual machine. And "a plan" is a lousy definition of "something that can be run", i.e. a program. So it's not a "special case", it's synonymous.

That also explains why it isn't a good and useful "pattern": inventing new DSLs with custom compilers all the time is not a trivial thing to pull off without shooting yourself in the leg, so if are writing a VM (e.g. an RDBMS query planner) you must be already perfectly aware that you are writing a fucking VM with its own programming language (e.g. SQL). And if you are not, inventing a data structure that encapsulates a whole program is probably a bigger task than the problem you are actually trying to solve.

Post reply on HN