Is there a **simple** lambda example?

Hi folks,

I came across this post as I struggled with my “simple” lambda as well and I think it would make sense to summarize the working script and another trap a newby (like me) struggled with while adapting the lambda.

first of all, the entire rule (copy from above with typo’s removed, I hope)

import org.eclipse.xtext.xbase.lib.*
// imports the Functions object. This is the base object of a lambda

val Functions$Function1 Foo = [ String s |
    // a lambda is a global variable like any other. By using val as opposed to various we are saying 
    // that Foo is final and cannot be reassigned. If you try to have a line that starts with "Foo =" you
    // will get an error.

    // Next you will see Functions. This is the root object of a lambda. Then you have $ which 
    // is a way to reference a subclass of Functions. Finally there is Function1. This is the subclass
    // and the important part is the 1. This numbe must match the number of arguments the lambda
    // will accept. You can have up to 7.

    // The [ denotes the start of a lambda. You may have seen these square brackets before in
    // timers and forEach. Those are lambds too. The only thing we are doing differently here is
    // we are giving the lambda a name so it can be reused.

    // Immediately after the bracket you list your arguments. In this cas we have only one and it 
    // is a String.

    logInfo ("lambda", s)
    true // return true in any case as last statement in lambda

    // All this lambda does is log the passed in argument. You can do almost anything in a 
    // lambda t b at you can do in a rule.however, the lambda has no context so if you need
    // to reference global vars or vals or rule local vars and vals you must pass them to the
    // lambda as an argument.
]

// The closing bracket denotes the end of the lambda 

...

    // somewhere  in a rule
    Foo.apply ("bar")
    // to call a lambda use its name followed by apply and the arguments in parents.

important:

  • the import should be on the very top of your rule file.
  • the lambda function can only called from the same rules file. It’s not globally available
  • the number after Functions$Function1 specifies the number of parameters to handle
  • lambdas seem to ignore global variables. So ensure everything you need inside of the lambda is given as a parameter.

And below my adapted version of above. My purpose is to change all the 3 items from a RGB switch with a single line for a given item name.

import org.eclipse.xtext.xbase.lib.*

val Functions$Function4 changeLED = [ 
	String itemname,
	int red,
	int green,
	int blue |
        sendCommand( itemname + "_R", red.toString() )
        sendCommand( itemname + "_G", green.toString() )
        sendCommand( itemname + "_B", blue.toString() )        
	true
	]

rule "LED on blue"
when
   Item V_kitchen_led_b changed to ON
then
   changeLED.apply("kitchen_led", 0, 0, 100 ) 
   logInfo("Kitchen","LED on in blue")
end


rule "LED on red"
when
   Item V_kitchen_led_r changed to ON
then
   changeLED.apply("kitchen_led", 100, 0, 0 ) 
   logInfo("Kitchen","LED on in red")
end

my items are called according a pattern like
kitchen_led_R
kitchen_led_G
kitchen_led_B
kitchen_led2_R
kitchen_led2_G
kitchen_led2_B

so all I give to the lambda is the unique name for the item in front which than is added with the 3 options for the RGB channels plus the 3 values I like to set the channel to (represents the percentage of brightness so a 100 goes to 100% brightness for that channel)

I hope above helps others as I struggled through it for several hours. All I was missing the right number after the function doh
Should I have overlooked a typo or made a new one: please let me know and I’ll edit it :slight_smile:

5 Likes

Hello!

Sorry for hijacking an old thread, but I couldn’t find an answer to a simple question. What is the syntax for a lambda without parameters? Is it

val Functions$Function0
val Functions$Function

or is it something else?

Best regards,
Davor

what’s the point in a lambda with no parameter ?

The point of lambda is not parameter, but possibility to reuse the code. That’s why almost every programming language has procedures and/or functions. I have a part of the code that’s being repeated 6 times, and I would like to put it in lambda. I don’t need any parameter for that code, because every call is exactly the same. I just want to be able to change it in one place, and not all over my rules file.

Best regards,
Davor

I don’t know the proper answer to your question, but you could always pass in something and then throw it away. if (true) blahblahblah…

Hello!

I know I could use some kind of “dummy” parameter, just thought someone might know a proper way to do it. Thank you for your reply.

Best regards,
Davor

If you don’t have a parameter, you should use a Script. The advantage is you can call a script from multiple .rules files whereas a lambda can only be called from the same file it is defined. But scripts won’t take arguments like lambdas.

That would be the “proper way”.

Hello!

Thank you for the answer. That’s exactly what I was looking for.

Best regards,
Davor

Hi!

Thank you for this nice explanation on lambdas. I followed this but cam across some problems. I use OpenHabian with OpenHAB 2.2.0.

In my items there is:

String Relay_StairPWR_LED  "LEDs for stairs"  <switch2>  {mqtt=">[mosquitto:Relay:command:ON:23/ON], >[mosquitto:Relay:command:OFF:23/OFF]"}

My rule file is as follows:

import org.eclipse.xtext.xbase.lib.*

var int PowerOnDelay = 700
var boolean ComponentCanPowerOn = true


//Lambda function:
val Functions$Function3 PwrCheck = [
    org.openhab.core.library.items.StringItem RelayLED,
    boolean CanPowerOn,
    int PwrDelay |
        if((RelayLED.state != "ON") && CanPowerOn)
        {
            sendCommand(RelayLED, "ON")
            Thread::sleep(PwrDelay) 
        }
        true
]

//I'm using in my rules than this call:
PwrCheck.apply(Relay_StairPWR_LED, ComponentCanPowerOn, PowerOnDelay)

The Relay_StairPWR is a string because it takes also other states than ON and OFF.

After saving this rule I get an info:

[INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'test.rules', using it anyway:
The use of wildcard imports is deprecated.
Function3 is a raw type. References to generic type Function3<P1, P2, P3, Result> should be parameterized

After firing a rule containing the lambda call i get:

[ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Test rule lambda call': 'state' is not a member of 'org.eclipse.smarthome.core.library.items.StringItem'; line 19, column 13, length 14

Of course the lambda is not executing. Where am I wrong here?

[SOLVED]
Found that this was written for OpenHAB 1.x, in OpenHAB 2.x the org.openhab.core.library.items. is not needed, just leave TypeItem name

but the INFO still appears in the log:

[INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'test.rules', using it anyway:
Function3 is a raw type. References to generic type Function3<P1, P2, P3, Result> should be parameterized

This two posts helped me out:
https://community.openhab.org/t/reusable-rules-via-function-in-openhab-2/14571/4?u=erzyk
https://community.openhab.org/t/reusable-rules-via-function-in-openhab-2/14571/6?u=erzyk

I followed also this example later on. Thank you for this tip, but there is another thing that bothers me.
Am I right that lambdas are executed in a separate thread and the calling rule just triggers the lambda and goes on with is own code? I have in my lambda a Thread wait statement but the rule that call this lambda never waits this delay time. Is there a way to make the calling rule wait for lambda to finish it’s work?

This is incorrect. The calling rule will block until the lambda returns.

Do you mean Thread::sleep? Thread::wait will not work on this context.

Yes I meant Thread::sleep(ms).
OK, I checked i carefully again and it is indeed working fine.

I also changed Functions to Procedures since I do not need a return value. But everytime I get the INFO:

Procedure6 is a raw type. References to generic type Procedure6<P1, P2, P3, P4, P5, P6> should be parameterized

Look at the 2.0 syntax in the link I provided above. Procedure and Function work the same way.

After using Procedures for several months I can’t get rid of this issue on rules file loading:

[INFO ] [del.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'X.rules', using it anyway:
Procedure2 is a raw type. References to generic type Procedure2<P1, P2> should be parameterized
 [INFO ] [del.core.internal.ModelRepositoryImpl] - Refreshing model 'X.rules'

It’s about this section of the lambda. It’s asking it to be presented in a more formal way, defining the paameter types in <> and the alias separately.
For Functions it also wants a return type defined in that <> list, void if there isn’t one, but I suppose that’s not needed for Procedures

//Lambda function:
val Functions$Function3 < StringItem, boolean, int, void > PwrCheck = [
    RelayLED,
    CanPowerOn,
    PwrDelay |

Thank you rossko57! That solved the annoying problem.

But sometimes I notice that a rules file does not load or stops working, maybe this will solve that issue.

That’s almost certainly something else, or two something elses, and it’d probably be best to start a new thread with details when you think it happens.

There is a cleaner syntax you can use.

val PwrCheck = [ StringItem RelayLED, Boolean CanPowerOn, Integer PwrDelay |

It will figure out whether it is a Functions or Procedures based on the value of the last line.

2 Likes