This is a great thread with a looping color light rule Rich whipped up which demonstrates several cool techniques. It very well documents the use of timers, in this case to loop a light thru all the colors.
among other gems, it illustrates the: modulo operator %
currColor = (currColor + increment) % 360
// remainder operation, when it gets over 360 currColor goes back to 0
The real trick is the use of the modulo operator %. This is sometimes called the remainder operation. Essentially we divide (currColor + increment) by 360 and set currColor to the remainder of that operation. The remainder of 150 / 360 = 150. The remainder of 360/360 = 0. The remainder of 370/360 = 10. So the value will never get above 360 and it will continue to loop through the values until the loop is canceled.
here is some stuff I’ve gleaned by reading thru the Xtend documentation
Short-Circuit Boolean Operators
the operation is inlined and evaluated in short circuit mode. That means that the right hand operand might not be evaluated at all in the following cases:
in the case of || the operand on the right hand side is not evaluated if the left operand evaluates to true .
in the case of && the operand on the right hand side is not evaluated if the left operand evaluates to false .
in the case of ?: the operand on the right hand side is not evaluated if the left operand evaluates to anything but null .
null !== NULL
This is a quote by Rich from an old thread (Aug 2018) explaining how lambda work. Here is link to the thread but be forewarned, it is 60 posts and a lot of the discussion is about other things. Rich’s post is #7old thread here
How Lambda work:
Every time you see [ ] in Rules DSL you are looking at a special type of function called a lambda. What makes it special is that it is also an Object. This means we can assign it to a variable (var, val), pass it to other functions as an argument, put them into a collection, etc.
But the key take away is [ | ] defines a function/method (some languages call this a function, some a method, I’m not sure what Rules DSL calls it so I usually use lambda). The Rules DSL is somewhat unique in that the only way to define your own function/method is through a lambda.
Whenever you call a function or method in any language you pass it zero or more arguments. For example createTimer takes two arguments, a DateTime and a lambda. The stuff before the | is defining the names and sometimes the types of the arguments.
val myLambda = [ String foo, Number bar |
logInfo("test", "Foo = " + foo)
bar + 1
we have defined a lambda with two arguments, a String named foo and a Number named bar. Inside the body of the function we can reference the arguments and do work with them. In this case we log out foo and return the result of adding 1 to bar.
When we call the lambda using apply, we need to pass in those two arguments:
val result = myLambda.apply("Foo", 5)
result will be set to 6 since the lambda returns the result of 5 + 1.
If we try to call
val result = myLamnbda.apply(5, “Foo”)
we will get an error because the first argument must be a String and the second must be a Number.
In this case we defined an argument to the lambda named i that forEach will call with each element of the list of members. This is one area where there is some magic going on. What type is i ? The Rules DSL is usually pretty good at figuring this out which is why we didn’t give it a type. In this case we know members is a List of Items so i will be of type Item. If we know the Group is all Switch Items, and it makes a difference we could use:
Most of the time it doesn’t matter so you will almost always see the type left off.
Behind the scenes, the forEach is looping through each of the members and calling .apply(member) for each of them.
If there is no argument for the lambda, you don’t need to put anything before the |. You see this in calls to createTimer since rarely do we need to pass anything to the Timer body. That is why you often see
createTimer(now.plusMinutes(1), [ |
// do stuff
I think that in this case the | may even be optional. But I try to be explicit and consistent and always include the | when I post to the forum because it helps avoid the “it’s magic” feel that beginners can experience when the language allows shortcuts like that (I should probably always supply the type in forEach as well for the same reason).
This is also why I always put the lambda passed into createTimer inside the parens even though the language allows one to put the lambda outside. The following is equivalent to the above:
createTimer(now.plusMinutes(1)) [ |
// do stuff
Now for the after the |. A function/method/lambda needs to do something. The code representing what the lambda does goes between the | and the ]. So when we call val result = myLambda.apply("Foo", 5) the code in the body of the lambda (between the | and the ]) executes with the values passed in as arguments.
link to implicit variables documentation
Rules DSL/Xtend calls this a “lambda”. And the meaning is a little more subtle than this statement. Rules DSL doesn’t have methods or functions that we can define. There are methods (the name Java uses). Every time you see Something.something you are calling a method. For example now.getHourOfDay is calling the getHourOfDay method on the now Object. Other languages will call these functions but for the purposes of this discussion a method and function can be used interchangeably.
As I said, Rules DSL doesn’t have support for us to create our own methods.
Lambdas are something slightly different. Objects have methods. Lambdas are Objects. Because a lambda is an Object, there are some limitations with lambdas that do not exist for methods.
When you call a method, that call is given it’s own context. Thus you can have have two methods running at the same time that don’t interact with each other because they each are running with a separate context (assuming that there isn’t some external resource like a variable defined outside the method being used). Lambdas do not support this. If you call a lambda twice so both are running at the same time, they are not running separately in two different contexts. The second context replaces the first context which will often result in errors.
This is all really deep down set of details and nuance, but I don’t want people to just think that a lambda is like a function or a method from other languages. I also want to make clear that I recommend avoiding the use of global lambdas unless absolutely necessary, and even then done in such a way that calling them so more than one runs at a time isn’t likely.
Lambdas defined within a Rule like those used for forEach or createTimer are not a problem because the next time the Rule runs it will create a new lambda Object. So the two Objects are what keeps them separate.