2

Here is a snippet from https://github.com/anura-engine/anura/blob/55bc02244f0faba5e0831578a3c1c9a82e7bf569/src/formula_function.cpp#L923-L931:

    FUNCTION_DEF(if, 2, -1, "if(a,b,c)")

        const int nargs = static_cast<int>(NUM_ARGS);
        for(int n = 0; n < nargs-1; n += 2) {
            const bool result = EVAL_ARG(n).as_bool();
            if(result) {
                return EVAL_ARG(n+1);
            }
        }

As we can see, if there is more than one condition evaluating as true, only the then-expression corresponding to the first condition evaluating as true is returned.

Actually, only the first condition evaluating as true gets to be evaluated as true.

Meta discussing FFL, (1) would it make sense that the expression should evaluate all the conditions and return a list of all then-expressions corresponding to true conditions, as long as the inside of the whole conditional expression is free from execution triggers (bind_command, ;, etc.)? Also (2) does this exist already (obviously with a different signature)?

1737973
  • 159
  • 18
  • 42

2 Answers2

2

I think it could make sense for the if function to evaluate and return passing. I've only seen the 2- and 3-parameter if functions used in the wild, so I can't comment on the n-parameter version as I just found out about it. (It seems to be equivalent to "if, else if, …, else". Hunh.)

The closest syntax I know of for the list of condition/statements is something like:

on_create: "filter([
    true and debug('cmd 1'),
    false and debug('cmd 2'),
    true and debug('cmd 3'),
], value)",

This will print 'cmd 1' and 'cmd 3' to screen.

This works because and short-circuits, and only returns the first thing to pass or the last thing to fail. The filter() call is required only if you want to use true and false, otherwise you can use true and null and drop filter(). Note that you can also use ; with this construct, I think it evaluates to a command sequence which you can use and process like a normal command. For example:

on_create: "filter([
    true and debug('cmd 1'),
    false and (debug('cmd 2'); debug('cmd 3')),
    true and (debug('cmd 4'); debug('cmd 5')),
], value)",

Prints 'cmd 1', 'cmd 4', and 'cmd 5' to screen.

This is equivalent to:

on_create: "[
    if(true, debug('cmd 1')),
    if(false, debug('cmd 2'); debug('cmd 3')),
    if(true, debug('cmd 4'); debug('cmd 5')),
]",

Which also prints 'cmd 1', 'cmd 4', and 'cmd 5' to screen. If you need to eliminate the null elements from the list, filter can be used as in the first example.

Zoe
  • 27,060
  • 21
  • 118
  • 148
DDR
  • 392
  • 1
  • 4
  • 14
  • I really appreciate your pragmatic answer as a useful resource for the future. But you missed the answers. While undoubtedly Anura already has enough FFL primitives to **easily** build any intermediate tool we might need (e.g. the discussed), in (2) I was asking for a single primitive to return the list. I suppose the answer is _no_ (of course the language provides primitives to build the tool using other primitives as you tell here), well, in some way we all knew (but the engine is big and full of surprises so one never knows). – 1737973 Nov 23 '18 at 23:06
  • (1) is more about the _meta_ reasons answering why conditional expressions should stop evaluating after the first `true` hit or not. _”You have to”_ imagine there is no `if(...)` implementation in Anura yet. Why would you implement the conditional expression as the current _short-circuiting_ fashion, instead of an _exhaustive_ fashion? Note that this is no easy question to answer. :( – 1737973 Nov 23 '18 at 23:06
  • Hm, if I understand better (I might not), the reason the if expression is short-circuited is that there's simply no reason to compute the branch not taken. It's simply a waste of resources, pure and simple, so why would we evaluate all branches? In the concrete, if you wanted to evaluate both branches, I suppose you could use `[dump(1),dump(2)][1=2]`. However, even in this case, there's no primitive that returns both branches of the branch. You'd have to store it separately. – DDR Dec 09 '18 at 03:05
  • What are you trying to do? – DDR Dec 09 '18 at 03:06
  • `[...] there is simply no reason to compute the branch not taken. [...]` - this is utterly right. `[...] why would evaluate all branches? [...]` - there would be no reason to evaluate the _then_ expression corresponding to a _condition_ evaluated to `false`. The question remains though, would there be any reason to evaluate and return the _then_ expressions corresponding to other potential `true` _conditions_ too? As there is no possible `else if` wording in an `if(c0, e0, c1, e1, c2, e2, ... ee)`, it results inviting to think about it outside the box. – 1737973 Dec 11 '18 at 09:15
0

What you want is a chain of if statements. That is already easily achieved by putting multiple if statements in a row. And it is very clear and common to see in other languages. Having a custom structure for this would thus be redundant and only reduce clarity.

On the other hand, a structure to implement repeated else-if's is very useful to reduce nesting. (Excessive nesting causes readability issues and creates opportunities for confusion.)

if condition, then action
else-if condition, then action (optional)
...
else-if condition, then action (optional)
else action (optional)
Patrick Parker
  • 4,863
  • 4
  • 19
  • 51