There's famous Design Patterns for OOP book. Wonder if there's Design Patterns for Nim?
A collection of patterns and solutions for common architectural problems.
Nim is mix of functional/OOP and so if you already know any functional / OOP language - you will be able to use Nim right away. Yet, Nim also has some unique features, like methods etc. and it's usage is different and not immediately obvious.
For example - say you need to provide slightly different behaviour for set of similar objects - animals, dog say "woff" and cat say "meow", used as animal.hi().
Should you use object hierarchy with method hi(dog: Dog) to allow custom implementation? Or should you use a single parameterised Animal object Animal(hi: dog_hi)? And so on.
Reading Design Patterns helps to expand the understanding of the language and know better what solution are available and which one would be a better fit for your situation.
I'm a firm believer that the design patterns as popularized by the Java/C++/C# folks reveal flaws in the language design and not how to address architecture problems.
Flaws like:
There are very few that I genuinely consider actually addressing architecture instead of language flaws:
I'm a firm believer that the design patterns as popularized by the Java/C++/C# folks reveal flaws in the language design and not how to address architecture problems.
Agreed. Many OOP design patterns in Java are the boilerplate code needed to compensate for lack of language features. Also (especially in Java) after you read Design Patterns, it's important to know when not to use it, and avoid writing code like factory.mediator.adapter.facade.builder.build().doSomething() :).
Yet, I feel it's very useful to read Design Patterns to improve your understanding of language capabilities. Maybe you should call it not Patterns but like "Solutions of Common Problems".
It's kinda like a short and effective way of reviewing tens of real world programs. But instead of spending lots of time reading actual code of actual programs, you look at its simplified skeleton version in short example in form of Pattern/Solution.
Here are some interesting skeletons
Thanks, good reading for weekends :)
I think a common name for a concept, one that's descriptive (NOT prescriptive), is very useful - for example, "hash function" or "hash table" or "lexicographical stable sort" or "gossip protocol" are extremely useful for human communication and documentation. They cannot be implemented as macros/templates etc, because they are too general (and even Nim's "concept" which comes closer isn't enough). Concrete, specific versions may be implemented, of course, but that's just a type/class/generic; no need for a new "pattern" distinction which adds nothing.
This is, I think, the equivalent to what Alexander's original "Patterns" referred to (w.r.t city architecture), and what the software patterns e.g. as advocated by the GoF claim to do - but as @mratsim says, just show how limited C++/C#/Java/Friends are.
for example, "hash function" or "hash table" or "lexicographical stable sort" or "gossip protocol"
Those are not design patterns, just… programming terms.
Concrete, specific versions may be implemented,
That's implied by the word “implement”. Of course you can't implement something abstract. But when someone says “implement a hash table”, it means a concrete version of a hash table.
We write AI for StarCraft. Each Player has lots of different objects, like Marine unit and Building.
Inheritance is not a good option here, because we going to have lots of procedures working with specific types, and don't need extra complexities like method.
Yet, those objects still share some common things, and sometimes we need to apply the same operation to all of them.
Nim can't store different objects in same collection, but instead the macros could be used:
import sugar, sequtils
type
Marine = object
id: string
Building = object
id: string
Player = object
id: string
marines: seq[Marine]
buildings: seq[Building]
template map*(player: Player, operation: untyped): untyped =
player.marines.map(operation) & player.buildings.map(operation)
let alex = Player(
id: "Alex",
marines: @[Marine(id: "Jim")],
buildings: @[Building(id: "Barrack")]
)
echo alex.map((o) => o.id)
Many games have switched to entity component system:
https://youtu.be/wqpxe-s9xyw?t=639
This is kind of close to what you are trying to do?
Yes, it's similar to Entity Component System used in Games. In this case it's like Entity Component System based on Composition. But without the disadvantage to have nesting like entity.foo.something.
P.S.
By the way, Kotlin has interesting approach to overcome that nesting problem in composite object. You can delegate object behaviour to one of its nested objects. And use object.something and compiler would expand such calls to object.foo.something Not suggesting it for Nim, but it's a handy thing in Kotlin.
I wonder if there's a way to avoid untyped and use a more strict types?
template map*(player: Player, operation: untyped): untyped =
player.marines.map(operation) & player.buildings.map(operation)
I would imagine something like that, but it won't compile:
proc map*[U, R](player: Player, operation: (U) -> R): seq[R] =
player.marines.map(operation) & player.buildings.map(operation)
I tried, but it's not working, if it's changed to proc Nim can't infer the o type in the alex.map((o) => o.id).
import sugar, sequtils
type
Marine = object
id: string
Building = object
id: string
Player = object
id: string
marines: seq[Marine]
buildings: seq[Building]
proc map*[U, R](player: Player, operation: (U) -> R): seq[R] =
player.marines.map(operation) & player.buildings.map(operation)
let alex = Player(
id: "Alex",
marines: @[Marine(id: "Jim")],
buildings: @[Building(id: "Barrack")]
)
echo alex.map((o) => o.id)