Proactively Handle Errors

In spite of all the efforts, some errors will sneak past the compiler. Compilers are a great first line of defense, but we shouldn’t solely rely upon them—that would be naive.

Let’s revisit the meeting scheduler DSL we saw in Use Inner Classes to Share State Between Contexts. If the user makes a syntax error, the Kotlin compiler will catch that right away. Also, if an incorrect data type is passed, as we saw in Leverage Type Checking, the compiler will promptly alert the user. The multiple contexts we created limit the scope of the functions that may be called when using the DSL. All these are good, but the user may still make some mistakes, like not setting the end time or setting an end time that is prior to a meeting start time. We, as DSL designers, need to anticipate and handle these situations.

Let’s take a stab at dealing with some errors that the compiler won’t be able to detect. Starting with the contents of the config.kt file from Use Inner Classes to Share State Between Contexts, we’ll add some error handling code to the Meeting class and the meeting() function. We’ll first change the meeting() function in the schedule singleton:

 object​ schedule {
 infix​ ​fun​ ​meeting​(block: Meeting.() -> Unit) {
 try​ {
  print(Meeting().apply(block).validate())
  } ​catch​(ex: Exception) {
  println(ex)
  }
  }
 }

In addition to invoking the given lambda in the context of the Meeting instance, we invoke a yet-to-be written validate() function that will perform some final checks on the instance populated by the lambda.

We’ll now implement the validate() function in the Meeting class and, while at it, also add an errors private property.

 class​ Meeting {
 private​ ​val​ errors = mutableListOf<String>()
 
 fun​ ​validate​(): Meeting {
 if​(startTime.isEmpty()) {
  errors.add(​"start time not set"​)
  }
 
 if​(endTime.isEmpty()) {
  errors.add(​"end time not set"​)
  }
 
 //more checking...
 
 if​(!errors.isEmpty()) {
 throw​ RuntimeException(
 """
  |Meeting not set up properly:
  |${errors.joinToString(System.lineSeparator())}"""​.trimMargin())
  }
 
 return​ ​this
  }
 
 //...rest of the code in this class

In the newly added validate() function, we check if the startTime or the endTime are empty and set an appropriate message into the errors list. We can add more checks, as necessary, in this function. Finally, if the errors list isn’t empty, then the validate() function blows up with an exception.

In addition to the checks in the validate() function, we can add error checking to other parts as well. For example, we can add this to the Starts inner class:

 inner​ ​class​ Starts {
 infix​ ​fun​ ​at​(time: IntRange) {
 if​(!startTime.isEmpty()) {
  errors.add(​"duplicate start time"​)
  }
 
 //more checking...
 
  startTime = time
  }
 }

If the start time is set more than once, the error checking code we added just now will make an entry to the errors list.

Let’s create a DSL snippet with a couple of errors in it:

 schedule meeting {
  assign name ​"Meeting to discuss why meetings aren't effective"
  starts at 14..30
  starts at 14..15
  on date 15 March 2020
  participants include ​"Sara"​ and ​"Jake"​ and ​"Mani"
 }

In this example, the start time is set more than once and the user has forgotten to set the end time. Let’s execute this DSL snippet using these commands:

 kotlinc-jvm -d meeting.jar meeting.kt
 kotlinc-jvm -classpath meeting.jar -script schedule.kts

The output from the execution is the following:

 java.lang.RuntimeException: Meeting not set up properly:
 duplicate start time
 end time not set

Even though the compiler couldn’t detect these logical errors, we’ve handled them gracefully.

You’ve learned several techniques to create internal DSLs in Kotlin. Designing DSLs is an exciting activity that calls upon both our creativity and knowledge of the host language. It’s a skill that we can improve with practice. In the next chapter, we’ll bring it all together to design and implement two DSLs from start to finish.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.177.223