docs/faq: Add a section on user defined functions and macros [skip ci]
Fixes
@@ -552,3 +552,80 @@ This means that you HAVE to write your `library(...)` calls in the order that th
dependencies flow. While ideas to make arbitrary orders possible exist, they were
rejected because reordering the `library(...)` calls was considered the "proper"
way. See [here](https://github.com/mesonbuild/meson/issues/8178) for the discussion.
+
+## Why doesn't meson have user defined functions/macros?
+
+The tl;dr answer to this is that meson's design is focused on solving specific
+problems rather than providing a general purpose language to write complex
+code solutions in build files. Build systems should be quick to write and
+quick to understand, functions muddle this simplicity.
+
+The long answer is twofold:
+
+First, Meson aims to provide a rich set of tools that solve specific problems
+simply out of the box. This is similar to the "batteries included" mentality of
+Python. By providing tools that solve common problems in the simplest way
+possible *in* Meson we are solving that problem for everyone instead of forcing
+everyone to solve that problem for themselves over and over again, often
+badly. One example of this are Meson's dependency wrappers around various
+config-tool executables (sdl-config, llvm-config, etc). In other build
+systems each user of that dependency writes a wrapper and deals with the
+corner cases (or doesn't, as is often the case), in Meson we handle them
+internally, everyone gets fixes and the corner cases are ironed out for
+*everyone*. Providing user defined functions or macros goes directly against
+this design goal.
+
+Second, functions and macros makes the build system more difficult to reason
+about. When you encounter some function call, you can refer to the reference
+manual to see that function and its signature. Instead of spending
+frustrating hours trying to interpret some bit of m4 or follow long include
+paths to figure out what `function1` (which calls `function2`, which calls
+`function3`, ad infinitum), you know what the build system is doing. Unless
+you're actively developing Meson itself, it's just a tool to orchestrate
+building the thing you actually care about. We want you to spend as little
+time worrying about build systems as possible so you can spend more time on
+*your code*.
+
+Many times user defined functions are used due to a lack of loops or
+because loops are tedious to use in the language. Meson has both arrays/lists
+and hashes/dicts natively. Compare the following pseudo code:
+
+```meson
+func(name, sources, extra_args)
+ executable(
+ name,
+ sources,
+ c_args : extra_args
+ )
+endfunc
+
+func(exe1, ['1.c', 'common.c'], [])
+func(exe2, ['2.c', 'common.c'], [])
+func(exe2_a, ['2.c', 'common.c'], ['-arg'])
+```
+
+```meson
+foreach e : [['1', '1.c', []],
+ ['2', '2.c', []],
+ ['2', '2.c', ['-arg']]]
+ executable(
+ 'exe' + e[0],
+ e[1],
+ c_args : e[2],
+ )
+endforeach
+```
+
+The loop is both less code and is much easier to reason about than the function
+version is, especially if the function were to live in a separate file, as is
+common in other popular build systems.
+
+Build system DSLs also tend to be badly thought out as generic programming
+languages, Meson tries to make it easy to use external scripts or programs
+for handling complex problems. While one can't always convert build logic
+into a scripting language (or compiled language), when it can be done this is
+often a better solution. External languages tend to be well-thought-out and
+tested, generally don't regress, and users are more likely to have domain
+knowledge about them. They also tend to have better tooling (such as
+autocompletion, linting, testing solutions), which make them a lower
+maintenance burden over time.
