2

(Mandatory Newbie Disclaimer)

I'm trying to write a rule that fires whenever an object within a (scala) list matches a condition. The issue here is that the list is actually an Option(List[TypeA])... (Also, I realise it isn't best practice to store lists in working memory, but I can't do otherwise given the circumstances)

The case classes I'm using have the following sort of structure:

TypeA {
    arg1 : Option[List[TypeB]]
}

with

TypeB {
    value : String
}

I've written a rule similar to this:

when
    $a : TypeA($l : arg1)
    $b : TypeB() from $l.get()
then
    System.out.println($b)

I've tried this out without the ".get()" only to get an object of type Some().

Using the ".get()", I have managed to return the contents of the Option but it doesn't seem to match the expected type (List[TypeB]). Instead the type of the value returned seems to be scala.collection.immutable.$colon$colon

Any ideas on what the problem is? And if there is any proper way to handle Options in Drools?

GroomedGorilla
  • 920
  • 2
  • 10
  • 30
  • 1
    It's you again! Didn't @laune and I warn you about using Scala with Drools. It has not been officially verified by the Drools people that these two tools are compatible. –  Feb 09 '15 at 16:58
  • Yep, me again ;) Warning received but I'm working with Scala objects (no choice at the mo) and interacting with them using Java methods this time round...it's more a proof of concept at this point. I've managed to use Scala objects in Java nicely so far, it's only this little annoyance that's keeping me back at the moment... Ideas? – GroomedGorilla Feb 09 '15 at 17:09
  • There is nothing evil with a List being some objects *property*. It's a List (or any other Collection, or a Map) being used "as is" as a fact that isn't good design. – laune Feb 09 '15 at 17:57
  • How important is it from your wrap your list with `Option`? Have you tried to see the type you get back with doing so? I would be very interested to know the type that is returned. Can you do that? –  Feb 10 '15 at 11:15
  • Unfortunately the case classes aren't my own, but its what I have to work with (can't change them). I have given that a shot using .get() but all I got was something of type scala.collection.immutable.$colon$colon ...which I took to be a generic list with head and tail. Looking into your answer re: javaconverters at the mo...seems very useful! Thanks – GroomedGorilla Feb 11 '15 at 08:59

2 Answers2

1

Since you are doing a lot of Java and Scala interop, I suggest you make yourself very familiar with the Scala's javaconverters functionality. This handy collection of utilities allows you to convert Scala collections to Java collection and vice versa.

In your case, I think you need to convert from a Java collection to a Scala collection. Try the following:

import scala.collection.JavaConverters._

val myScalaList = $b.asScala.toList

Example from the documentations:

import scala.collection.JavaConverters._

val sl = new scala.collection.mutable.ListBuffer[Int]
val jl : java.util.List[Int] = sl.asJava
val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala
assert(sl eq sl2)

An additional problem you have is that of mutable and immutable data structures. The standard list structure in Java is mutable but by default Scala offers you an immutable list unless you explicitly indicate that you want a mutable list. Therefore, there will be some impedance mismatch when doing naive conversions between the two worlds.

As I have mentioned in an earlier post, you can avoid yourself many issues by creating Java classes for the entities that you need to push into Drools. Mixing Java classes with Scala classes in Scala based projects is not an issue.

An alternative method is to create a function in your Scala case class which converts the Scala collection to a Java collection using the asJava method and returns it. In your DRL file whenever you need to reference that scala collection, call this method so that you get a Java collection instead.

Ideally, JBoss Drools, if they so choose, need to either enhance their current compiler to deal with Scala types better or make a dedicated Drools Scala compiler which will not mangle the Scala types.

  • Not much luck. The problem being that I can't get to the List in the first place. I've tried calling `$l.get().asJava() / asScala()` on the RHS of the rule but keep getting errors related to strict-mode : `[Error: unable to resolve method using strict-mode: java.lang.Object.asScala()]` (which I haven't found a solution for since 6.1 KieSessions). I have tried `.get()` on Option arguments which aren't lists and Drools seem to return an object of the Scala case class, but then once again I can't dig into its arguments (i.e. I'd get a TypeB but get errors reading typeBObj.value). – GroomedGorilla Feb 11 '15 at 14:18
  • did you import those methods i.e. `import scala.collection.JavaConverters._` –  Feb 11 '15 at 14:47
  • yep, imported both scala.collections.JavaConverters.* and scala.collections.JavaConversions.* in the drl file – GroomedGorilla Feb 11 '15 at 14:52
  • Drools cannot find those methods because it doesn't know where to look. If you look at the error message it is trying to locate those methods in the Java API when actually they are in the Scala API. –  Feb 11 '15 at 14:56
  • Right you are, been staring at this problem too long to realise... So..no way of converting between the two within the rules themselves? – GroomedGorilla Feb 11 '15 at 15:04
  • 1
    Try this hack: Create a function in your Scala case class which converts the Scala collection to a Java collection using the `asJava` method and *returns* it. In your DRL file whenever you need to reference that scala collection, call this method so that you get a Java collection instead. –  Feb 11 '15 at 15:34
  • Holy crap that' seems to be working! Going to be a hefty task to write wrapper functions for each Option I need to deal with but at least it's a work around! If you'd like to post this as an answer I'll mark it as a solution. Thanks! – GroomedGorilla Feb 11 '15 at 15:57
  • 1
    LOL. Updated the answer. Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70723/discussion-between-i-k-and-groomedgorilla). –  Feb 11 '15 at 15:58
0

The only thing I can think of to try:

when
    $a : TypeA($l : arg)
    $b : TypeB() from (ArrayList)$l.get() // or some other Java *-List
then
    System.out.println($b)

Worth another try:

when
    $a : TypeA($l : arg)
    $b : TypeB() from $l.get()asJava()*-List
then
    System.out.println($b)
laune
  • 31,114
  • 3
  • 29
  • 42
  • What else - here there's only Java. If you know a better target class for the cast, use that. – laune Feb 09 '15 at 18:13
  • 1
    @laune, the OP also has issues with mutability and immutability. From the post, it appears that the Scala list is immutable. However ArrayList is mutable as you know. Getting a one-to-one mapping with Scala's data structures from Java is going to be tricky to say the least. –  Feb 10 '15 at 11:14
  • Thanks @laune. Tried that too but it's returning an empty list and not matching in the LHS at all then. – GroomedGorilla Feb 11 '15 at 15:35
  • Is it possible to write `$l.get().asJava()`? This should return a mutable Java List (if I've understood I.K. correctly). – laune Feb 11 '15 at 15:55
  • Not within the drl file though. I'm calling a scala method that returns $l.get.asJava instead. Much like the Java wrapper you mentioned on another question @laune – GroomedGorilla Feb 11 '15 at 17:45