Making OH Rules Easier for Everyone

It is a hard concept to grasp so don’t be hard on yourself.

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.

So, given

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.

Here’s another example:

MyGroup.members.forEach[ i | logInfo("test", i.name + " = " + i.state) ]

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:

MyGroup.members.forEach[ SwitchItem i | logInfo("test", i.name + " = " + i.state) ]

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.

I’m not convinced there is any programming language that will solve this problem. Ultimately no matter what the language, writing Rules is programming and once you get beyond a certain level of complexity you will either need to become fluent in whatever programming language you are writing in or resort to StackOverflow and this forum for help.

One can debate whether the Rules DSL was successful (I’m not sure it was), but one of the goals in removing a lot of features from the Xtend language was to minimize how much one needs to learn to become fluent in the language.

One advantage of less esoteric languages like Kotlin, Jython, et. al. have over the Rules DSL though is that even though there is more to learn to become fluent in the language, there are far more resources on the Internet at large to help you learn it. I’m encouraged by the Experimental Rule’s Engine use of JavaScript for this reason.

There is a perennial fight between boiler plate and required structure and terseness in languages. Compared to many languages (e.g. Java) Rules DSL has less boiler plate. But less boiler plate doesn’t mean easier to write, read, or understand. Anyone who has coded in Lisp can tell you that. There are only about a dozen commands in Lisp.

The Rules DSL has less boiler place than many languages and from what I’ve seen it has maybe a little more than Python, about the same as JavaScript, and far less than Java.

And you don’t have to type out most of the Rules DSL boiler plate anyway. If you are using VSCode, kuba added a lot of code snippets (including a lot of the Design Patterns) so you don’t have to type them.

But ultimately a computer programming language is always going to be less intuitive compared to human language because there can be no room for ambiguity. Computers can only do what they are told and they have no common sense nor the ability to handle nuance. All that boiler plate is there to eliminate ambiguity. the computer never has to guess what you mean because there is only one thing you can mean.

The boiler plate may seem pointless but every symbol written in code is in fact a command sent to the computer. Every { is actually telling the computer to do something. So you can’t just skip it (unless you are writing in a language that uses some other way to send that command to the computer, e.g. Python uses a : and indentation).

This is one of the places that the Rules DSL fails. Should Kotlin ever become a Rules programming language it will face the same problem because like the Rules DSL, it will rely upon Java libraries for some features.

Ultimately most of your complaints and problems could be transferred to ANY programming environment. Defining Rules will always be a programming exercise. And if you are not a programmer then you will struggle more than you would otherwise. I don’t think there is any language in the world that will be able to fix that. The developers can try, have tried, and will continue to try to make it better. But we will never get away from the fact that you are programming.

This is why I’m pondering the book discussed earlier in the thread. It doesn’t really belong in the OH docs themselves but it might help users like you get some foundational knowledge that hopefully would help the non-programmer users become successful more quickly, no matter what language Rules end up being written in.

@lipp_markus, I’d love to hear some more specifics and what other parts of the Rules DSL you find confusing, frustrating, or hard to understand. Perhaps I can address some of them here.

If that were the only goal, I would think you would pick a language somewhere in the top 20 TIOBE Index.

Kotlin is 43. Other candidate languages with more popularity (I’m using the August list) include:

  • 4 Python
  • 8 JavaScript
  • 21 Scratch
  • 27 Scala
  • 30 Lua

(Java is number 1).

No. They BOTH target users.

I’ve said it several times. Rules DSL != Xtend. DSL stands for “Domain Specific Language”. It is based on Xtend but the difference are large and Rules DSL is basically a language unto itself.

Currently, the only language in OH that targets Devs is Java. You are working on adding Kotlin and that would be pretty cool for developers. But you’ve made it clear that you are not adding Kotlin as a Rules Engine. Which is fine. But it doesn’t do anything to address the problems or limitation of the “pathetic” Rules DSL or Experimental Rules Engine.

“If you build it they will come” is not a very convincing argument for how it will get better for users like Lipp.

But in OH the “users” have to be devs too. That isn’t the case for Windows/Apple/Android.

That’s the problem that needs to be solved. How do we present a Rules environment that lets non-programmers develop and code their own Rules?

You are presenting a solution to maybe get more developers involved with developing more add-ons to OH but have thus far proposed nothing that solves this problem.

Kotlin doesn’t solve this problem. JSR223 doesn’t solve this problem. At least the “pathetic” Rules DSL and Experimental Rules Engine are trying to solve this problem.

I will. But you might understand why I get annoyed when you tell me to not write anything to help non-coders be successful in Rules DSL (or any other language) because you are working on adding Kotlin, only you are focusing on doing so to make it easier for coders to write add-ons. That has nothing to do with making it easier for users to write Rules.

If everyone who has said this would contribute just one or two articles to the OH docs, maybe that would be possible. Until then this is basically a “go f$%^ yourself” to any user like Lipp.

That is the goal of my idea (minus the kids part). :wink:

Assuming they apply. I’m under no illusion. Many of them are ways to work with the Rules DSL rather than against it. They may not be needed in some other language.

We better not be. OH is not and cannot be a real time system. There is no guarantee in the order of processing of events. There are no transactions. OH is not written to handle these sorts of constraints which, at least in my world, are required to build a mission critical system. Though in my world mission critical means people may die if the system fails.

And EMACS is an operating system masquerading as a text editor. :wink:

8 Likes