[SOLVED] Lambda calling other lambda? (JSR223/JYTHON)

I have some bits of code that I want to execute multiple times so I created few lambda functions. But the problem I run into is that when lamdaX calls inside lambdaY the lambdaY have to be passed, and the number of arguments is limited.
Instead of keeping track what lambda needs to be passed where I tried creating array where I would put all my lambdas and just pass whole array to each one as only parameter. So I would get something like myArray.lambdaX.apply() //pseudocode but I failed at doing that :frowning_face:
Next thing I thought of is creating a simple class - it cant be that hard, right? :grinning: Well… after a quick research it seem that it is not possible :confused:

examlpe (pseudocode-ish) of situation I’m talking about:

val lambdaX = [ |
  logInfo( "my.rules", "Doing some stuff!!!" )
]

val timerLambdaX = [ |
  var _sunset = new DateTime( Sunset.state.toString )
  createTimer( _sunset .plusMinutes( Integer::parseInt( offset.state.toString ) ), [
			lambdaX.apply();
		])
]

val checkIfAfterSunset = [ |
	var _sunset = new DateTime( Sunset.state.toString )
	if ( now.isAfter( _sunset ) ) {
		logInfo( "my.rules", "Doors should be already closed!" )
		lambdaX.apply()
	}
	else if ( now.isAfter( _sunset.plusMinutes( -120 ) ) ) {
		//timers should be set at this point - set them again
		logInfo( "my.rules", "Creating timers! (after powerup)" )
		timerLambdaX.apply()
	}
]

rule "Initial setup"
when
  System started
then
 createTimer( now.plusMinutes( 2 ), [ |
   //wait for everything to initialize and...
   checkIfAfterSunset.apply()
 ]
end

rule "Close doors - set timers"
when
	Channel 'astro:sun:myLocation:set#event' triggered END
then
	timerLambdaX.apply()
end

At this point I’m confused what to do - is there some other clean solution?

Hi, I am not quite sure I understand what your are trying to accomplish. As general comment, lambda’s are frowned upon. The rules DSL is very limited in terms of doing much more than simple if then else type processing. No classes allowed. Additionally the rules DSL is not thread safe and any attempts at achieving thread safety with locks will result in failure. If you really need complex event processing consider Jython scripting.

Variabler declared in a rules file are not in scope of each other apparently. You need to pass the lambdas as inputs.

As the above poster said, it seems that doing java/xtend stuff in rules is not recommended.

As a maybe more lightweight alternative to Jython etc you can often achive the same thing as a lambda with items and rules. Item.sendCommand is then your equivalent of someLambda.apply while the rule defines the method body. Use string items if you need arguments (yes you would need to parse them).

I do this myself sometimes. You are currently chasing a solution to a problem but have you stepped back to ask yourself if there is a way to sidestep the problem entirely with some different approach.

IMHO lambdas are a code smell. They hint that you might be doing something wrong. Lambdas that need to call other lambdas is a code stench. This sort of coding structure is just now well supported by Rules DSL and there are some very serious problems with lambdas including:

  • type errors inside a lambda kills the whole Rule instantly
  • lambdas are not thread safe, if two or more Rules call a lambda at the same time they will stomp on top of each other
  • there is almost always a better way

Not really. Rules DSL is not going to let you structure your code in ways you may be used to if you are already a programmer, it is more than capable of supporting quite complex and robust Rules.

I wouldn’t take it that far. Lambdas for sure are not recommended in most cases. But use of other Java classes or other XTend concepts are absolutely encouraged where they are supported. See Design Pattern: Working with Groups in Rules which is all Xtend stuff you can do in Rules that are absolutely vital.

This is close to the Design Pattern: Separation of Behaviors which is one of the ways to avoid lambdas.

Looking at the code and the sorts of things your lambdas are doing, absolutely rules DSL can handle this but you need to structure it differently. For example, instead of a lambda to calculate if it is after sunset that gets called from multiple rules, use [Deprecated] Design Pattern: Time Of Day to centralize your time calculations and comparisons and in your rules that care about time you just check if(vTimeOfday.state == "EVENING"). You can then trigger rules based on changes to vTimeOfDay. And there is no need for the lambdas.

checkIfAfterSunset gets calculated in the time of day rule, and the two timer lambdas become Rules that get triggered by changes to vTimeOfDay.

At a high level, what you want to do is centralize calculations of states like these into one Rule or set of Rules, store the result in Item(s), and use the Item(s) in your other Rules.

Another approach is to take advantage of Member of Rule triggers, Groups, and Design Pattern: Associated Items to write generic Rules. When all of your lights, for example, are handled by a single Rule, there is no need for lambdas because all the code is there in that one rule.

Having said all that, if you are a programmer already, I second Michael’s recommendation to use JSR223 rules. I find that most programmers can’t or wont try to restructure their code into a more Rules DSL appropriate way and they are much happier in a programming environment they are use to where they have functions and classes and such and are able to use a coding style and structure closer to what they are use to.

1 Like

Rich, don’t mean to be a complete downer on the DSL but the limitations of it are not clearly spelled out. It is not thread safe. I have even seen problems with iterators. Yes, with work you can accomplish more complex tasks but it is essentially a trial and error approach and endless digging for work arounds. How many times a week do we see someone trying to do something with lambas (I am one them) only after banging their head on the wall when one second it works and the next event it doesn’t. Its frustrating and almost led to me abandoning OH. Your DPs are a great guide to working around limitations of the DSL. Thank you.

Indeed. But to be honest, in the years that I’ve supported OH on this and the previous forum your case was the first where this non-threadsafeness was a problem. And I’m really happy that JSR223 was there to support what you needed. So your particular use case, or perhaps your preferred approach, is somewhat unique and the Rules DSL is wholly unsuitable for your needs.

But that doesn’t negate the fact that thousands of users can and do write exceptionally complex Rules that perform flawlessly. And that is all I’m saying. I don’t want future users to come across statements like the above and think that the Rules DSL is unsuitable for their needs, particularly if they are not already coders.

The whole issue is going to become moot in the relatively near future anyway (maybe OH 2.5?) when the PaperUI Rules get documented and the UI improved a bit. At that point new users will be able to get started with point and click rules creation, a NodeRed like UI (if Yannick finishes his awesome looking rule builder) and if/when they outgrow that transition to JSR223 using the same underlying rules engine and “normal” programming languages. Not to mention that with templates and libraries, the DPs can become something that gets installed, not something copied and pasted from the forum.

thanks for suggestions everyone :smiley:

I will definitely google the JSR223 and hopefully find some nice tutorials because I have a hard time finding information about OH (especially OH-newbie friendly info) :slightly_frowning_face: if not I may just rewrite everything to Rules DSL to keep my project going and later give JSR another try

have a look at

2 Likes

If JS is more your thing Experimental Next-Gen Rules Engine Documentation 4 of : Writing Scripts and lewie’s library can get you started.

Jython is by far the most used and the helper libraries are the most mature.

Groovy support is probably the weakest when it comes to helper libraries and documentation.

eh… why I can’t get anything working first time without any problems :disappointed:

I have followed the quick start guide from jython repo and run to few problems

  • I am usually copying files to my RPI (where OH is running) via FTP from my pc and when I copied /etc/default/openhab2 I missed failed file transfer warning - missing permissions, I did manage to find this out when log started printing errors, then ssh, sudo nano that file and problem was fixed
  • then I got error logs about things being NoneType. It turns out that modules loading is a bit messy but I found a script that will hold JYTHON initialization until it actually can initialize
  • and now I wanted to just run the hello_world.py as staded in quick start quide but I’m getting this error:
    [ERROR] [ipt.internal.ScriptEngineManagerImpl] - Error during evaluation of script 'file:/etc/openhab2/automation/jsr223/hello_world.py': ImportError: No module named core in <script> at line number 3
    I didn’t changed anything it the script, and even when I uncommented the commented code from the script it didn’t changed changed anything because problem lays in line 3 -
    from openhab.triggers import when, CronTrigger

I will try to investigate it tomorrow but maybe in the mean time someone can give me a hint what the problem may be :slightly_smiling_face:

Gimme a minute… it looks like code changes in PR #41 somehow got into master…

@sonko, I’ve looked through the master branch, and do not see any references to the core package (phew… you had me worried!). Maybe you downloaded some/all from this branch, which renames the openhab package to core? Stick with the master branch for now, and you can use this for the startup delay script, which is also included in PR 41 (a major overhaul). If you are still having issues, just let me know (preferably in a different thread), and I’ll help get you started with JSR223-Jython!

PS, I’ll add a comment to the docs in regards to setting permissions on /etc/default/openhab2.