Tail Recursion in Dataweave

Viewed 1010

Is there a way to take a recursive function (like the following) and make it tail recursive? I have an input like this:

{
    "message": "Test   ",
    "read": [
        {
            "test": "   t   "
        }
    ]
}

and this Dataweave function

fun trimWS(item) = item match {
    case is Array -> $ map trimWS($)
    case is Object -> $ mapObject {
        ($$): $ match {
            case is String -> trim($)
            case is Object -> trimWS($)
            case is Array -> $ map trimWS($)
            else -> $
        }
    }
    case is String -> trim($)
    else -> $
}
2 Answers

I reworked a little bit your existing function to simplify it and I also run a few tests under Mule 4.2.1.

By building a data structure with over 840 levels deep, I was able to navigate and trim the fields. My guess is because of the structure of the data and lazy evaluation I am able to get past 256 depths which is the default value where DW 2.0 is throwing StackOverflow.

You can also increase the default value by passing a runtime parameter, its name is com.mulesoft.dw.stacksize (e.g. com.mulesoft.dw.stacksize=500) or any other number provided your system can handle it.

As I said creating a tail-recursive version is not easy, it will complicate the code, it will be way less maintainable as compared to the existing version, etc.

I hope it helps even if I am not directly answering your question.

%dw 2.0
output application/json
var ds =  {
    "message": "Test   ",
    "read": [
        {
            "test": "   t   "
        }
    ]
}
var deepData = (0 to 840) as Array reduce (e, acc=ds) -> {value: " TO_TRIM ",next: acc}

fun trimWS(item) = item match {
    case is Array -> $ map trimWS($)
    case is Object -> $ mapObject {($$): trimWS($)}
    case is String -> trim($)
    else -> $
}

---

trimWS(deepData)

I noticed that this function passes the @TailRec() annotation test.

%dw 2.0
output application/json

var in1 = [
  {"id": "1", "environment": "dev"},
  {"id": "2", "environment": "test"},
  {"id": "3", "environment": "uat"},
  {"id": "4", "environment": "prod"},
  {"id": "2", "environment": "test"}
]

@TailRec()
fun format(x: Any, formatter: (String)-> String) =
x match {
    case obj is Object -> obj mapObject (format($$,formatter)): format($,formatter) 
    case arr is Array -> arr map  format($,formatter) 
    case key is Key -> formatter(key as String) as Key
    case str is String -> formatter(str)
    else -> format(null, formatter)
}
---

format(in1,  upper )

I can also get the orig function to pass the test with this:

 %dw 2.0
output application/json
var ds =  {
    "message": "Test   ",
    "read": [
        {
            "test": "   t   "
        }
    ]
}
var deepData = (0 to 700) as Array reduce (e, acc=ds) -> {value: " TO_TRIM ",next: acc}

@TailRec()
fun trimWS(item) = item match {
    case is Array -> $ map trimWS($)
    case is Object -> $ mapObject {($$): trimWS($)}
    case is String -> trim($)
    //case other if( !(other is Null ))-> other
    case other if(true) -> other
    else -> trimWS($)
}
---

trimWS(deepData)

But neither of these are tail recursive.

Related