Reusable Functions: A simple lambda example with copious notes

Added a section with updated syntax to get rid of the ESH Designer warnings.

Interesting, is the ESH Designer working again?
If I am trying to use this tool it already fails when pointing it to the new folder structure.

The older 0.8 version works. That is currently the version available for download on the ESH website. If you have the 0.9 you can/should downgrade and the problem with Items and Actions will go away.

Thanks. Indeed this is “somehow” working.

I am getting tons of false error message.
“say”, “push notification” and other key word are flagged and
every statement which contains “as DecimalType” is also flagged with an error.

But the good news is that this version seems to be able again to cope with longer rule files. In my case 2400 lines of code. At least on MacOS. I did not test Designer on Windows 10 yet.

Rich Koshak, thanks for this example. I’m finally getting a glimmer of understanding lambdas. :relaxed:

In follow up, I have summary of how I see this function & rule working & a tweak I wanted & worked out.

How I understand this: The function named “log” expects an item name & will cast strings. s stands for the rule-supplied item. The function creates a string which is the state of a rule-supplied item (here, MyItem) That string is sent to the log file named lambda & will display in a terminal window by the command tail -f /var/log/openhab2/openhab.log. According to a comment in the OLD syntax, the function’s last line, the string version of MyItem’s state + " logged" is returned to the rule that called the function & that created string populates val loggedStr in the rule.

With some research, I learned that MyItem.name will supply the item’s name.
If I tweak the function’s 2nd line to
logInfo(“lambda”, s.name + " = " + s.state.toString)
the lambda file will get a line like MyItem = OFF & a tail -f command will display that in Terminal.

I like logging not only the state, but also the name of the item.

From my experiments, the function’s last line (which returns to loggedStr in the rule) could also be tweaked to include things like MyItem.name. loggedStr could in turn be used after the function call in the rule.

This function could be a ready made method to call for logging items’ states . It also opens other possibilities for using functions to handle other often used methods.

How am I doing on understanding this lambda & adapting it?

Thanks again for the tutorial & launching me on a good learning process.

Incorrect. The lambda expects an Item. Not an Item name (which would be a String, not of type GenericItem). It doesn’t cast anything. It calls the toString method on the State returned when calling the .state method on the passed in (I.e. s).

Sorry of. Lambdas take 0 or more arguments. Reach argument is given a unique name. This name is exactly like a valid in your rules. So s is a val that contains the passed in Item.

Yes, see my description above.

Sort of. The string is sent to the logInfo action which is, as far as the rule is concerned is a function call. What happens from the depends on how logging is configured. By default the line will be written to openhab.log.

That is true in both the old and new syntax.

[quote=“papabrenta, post:10, topic:15888”]
I like logging not only the state, but also the name of the item. …
This function could be a ready made method to call for logging items’ states . It also opens other possibilities for using functions to handle other often used methods.[/quote]

Using a lambda for something like the example above is not a very good idea. I chose this example because it was the easiest to understand that I could think of. In practice you would almost never want a lambda for something this trivial. See my Separation of Behaviors for a better approach. And even then that use case is if you have complicated logic you want to apply (e.g. log to different places based on time of day). If all you are doing is logging out an item’s state it should be in line, not separate.

1 Like

Thanks, Rich, for the detailed responses & corrections. Having not “come from Java,” I am (obviously) inexperienced in this type of programming & using the right terms, but I’m trying to learn. I am curious about lambdas, how they work, where they might be useful. I found your “Reusable Functions…” to be more approachable than other stuff that used lambdas without much explanation.

Followup on “A simple lambda example”: So inside “< >,” GenericItem means the lambda expects the call to pass it an item. Sounds like “String” inside < > means the function expects to pass back a string as a return value. Is that correct?

I looked again at “Separation of Behaviors” & see you recommend use of proxy items & the like instead of lambdas. I was already using proxy items & lambdas confused me. Maybe I should usually stay with what I was doing before.

Be careful thinking about the Rules as Java. While Java classes are available, the Rules language is very unlike Java in many ways, including lambdas.

You are correct, the last class listed inside the <> is the return type.

Thanks. That was the intent of the posting.

Note the two are not just two different ways to do the same thing. Both have problems they are better suited for than the other. Separation of Behaviors (SOB) is great when you have logic that you need to use globally across all your rules files (e.g. alerts). Lambdas are best when you have common logic that applies to more than one rule within the same rule file and there is no way to handle the common logic by merging rules.

SOB is asynchronous and runs at the same time as the rest of your rule. Lambdas are synchronous and prevents the rest of your rule from running while the lambda is running.

SOB can be “called” from any rule file. Lambdas can only be cashed from the same rule file.

1 Like

Rich, I believe that in the conversation here, I’m getting a bit clearer on understanding & using the Rules language, lambdas, & proxy items, Thanks again for taking time to respond soon & in detail. I’ll definitely bookmark “Reusable Functions” for future reference.

Great demo, thanks! I’m new to lambdas, and haven’t found the info elsewhere, so forgive me if it’s been covered, but is it possible to pass multiple items in one argument, and loop through them? Kinda like how you’d loop through grouped items? Say…

Lambda:

val Functions$Function1 lightsOff= [ GenericItem s |
      s.members.filter( x | x.state == ON).forEach[Switch |
            Switch.sendCommand(OFF)
      ]
]

Rule:

val loggedStr = lightsOff.apply((itemSwitch1, itemSwitch2, itemSwitch3))

You can create a Map or a List and pass the List as an argument. That is about all you can do. I know of no way to define a lambda that takes an arbitrary number of arguments.

That would looks something like:

import java.util.List

val Functions$Function1<List<SwitchItem>, Boolean> lightsOff = [ lights |
    lights.filter[light | light.state == ON).forEach[light | light.sendCommand(OFF)]

    true
]

NOTE:

  • The above follows the updated syntax and will avoid errors in your logs
  • A Functions lambda must return a value. The return value is the result of the last executed line in the lambda. As written, OH will complain that the lambda doesn’t cause and side effects or something like that because the forEach doesn’t return anything, hence the addition of the true.

Perfect, I’ll give it a try. Thanks!

@rlkoshak:
I tried your last example. But it does not work:

I think there is an syntax error in your post?

import java.util.List

val Functions$Function1<List<SwitchItem>, Boolean> lightsOff = [ lights |
    lights.filter[light | light.state == ON].forEach[light | light.sendCommand(OFF)]

    true
]

Behind the == ON there must be an ‘]’ instead of ‘)’?

But how to start the function. If I do it like this in rule file

val loggedStr = lightsOff.apply((itemSwitch1, itemSwitch2, itemSwitch3))

I get in log:

2018-01-31 14:43:32.186 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model 'lambda.rules' has errors, therefore ignoring it: [28,28]: missing ')' at ','
[28,53]: mismatched input ')' expecting 'end'

Looks like syntax is wrong.

Yes there is a typo. You have it correct. It should be a ], not a ).

Why the extra parens around the arguments? Are you trying to create a List? If so I don’t think you can do it like that. You have to do something like:

val List<SwitchItem> args = newArrayList
args.add(itemSwitch1)
args.add(itemSwitch2)
args.add(itemSwitch3)
val loggedStr = lightsOff.apply(args)

Even if you can create a List like that, it would be a List, not a List so even if the syntax were right, the types would have been wrong.

The problem that i can not call this function in other rules. It there a way to solve it?

Sure you can.

But you cannot call a function in one xxx.rules file from rules in some other zzz.rules file.

Put all the rules that require some function in the same xxx.rules file as the function.

Wow… two years later! Nowadays, you just use Jython and put the function in a module that can be called from any rule in any file or even from a UI rule.

1 Like

I mean right this. From another rule files.
I would like to make a logging function, that sends logs to syslog, email and telegramm. Of course logs comes from different rules files.

Jyton. I have not heard about it. I asked google and it looks like another language and there is not much examples how to use it.
Maybe u know a good tutorial or thread here with explanation?

Thanks!

You can use the new rule engine and do scripted automation using Jython, which is Python for the JVM. The OH docs are a bit meager. It has some important information, but don’t follow those installation instructions. The raw automation API is extremely rough to use without living in the source code. Fortunately, there are helper libraries that make it very easy to use and are a game changer for OH automation. There is a lot of documentation to help and most design patterns now also have Jython examples. The installation isn’t too difficult, but I’m working on an addon to simplify things. Even @rlkoshak has migrated all of his rules from the rules DSL to Jython and @vzorglub is in process.

https://openhab-scripters.github.io/openhab-helper-libraries/index.html

Let me know if you have an questions or issues… but probably should be in a new topic :slightly_smiling_face:.