| <!DOCTYPE html> |
| <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> |
| <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> |
| <!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> |
| <!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--><head> |
| <meta charset='utf-8'/><meta http-equiv='X-UA-Compatible' content='IE=edge'/><meta name='viewport' content='width=device-width, initial-scale=1'/><meta name='keywords' content='domain specific languages, groovy, jsr385, units of measurement'/><meta name='description' content='This post looks at using the JSR 385 unit of measurement system and writing domain specific languages with examples including the Mars rover robot.'/><title>The Apache Groovy programming language - Blogs - Life on Mars: Units of Measurement systems, Groovy, and domain specific languages (DSLs)</title><link href='../img/favicon.ico' type='image/x-ico' rel='icon'/><link rel='stylesheet' type='text/css' href='../css/bootstrap.css'/><link rel='stylesheet' type='text/css' href='../css/font-awesome.min.css'/><link rel='stylesheet' type='text/css' href='../css/style.css'/><link rel='stylesheet' type='text/css' href='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css'/> |
| </head><body> |
| <div id='fork-me'> |
| <a href='https://github.com/apache/groovy'> |
| <img style='position: fixed; top: 20px; right: -58px; border: 0; z-index: 100; transform: rotate(45deg);' src='/img/horizontal-github-ribbon.png'/> |
| </a> |
| </div><div id='st-container' class='st-container st-effect-9'> |
| <nav class='st-menu st-effect-9' id='menu-12'> |
| <h2 class='icon icon-lab'>Socialize</h2><ul> |
| <li> |
| <a href='http://groovy-lang.org/mailing-lists.html' class='icon'><span class='fa fa-envelope'></span> Discuss on the mailing-list</a> |
| </li><li> |
| <a href='https://twitter.com/ApacheGroovy' class='icon'><span class='fa fa-twitter'></span> Groovy on Twitter</a> |
| </li><li> |
| <a href='http://groovy-lang.org/events.html' class='icon'><span class='fa fa-calendar'></span> Events and conferences</a> |
| </li><li> |
| <a href='https://github.com/apache/groovy' class='icon'><span class='fa fa-github'></span> Source code on GitHub</a> |
| </li><li> |
| <a href='http://groovy-lang.org/reporting-issues.html' class='icon'><span class='fa fa-bug'></span> Report issues in Jira</a> |
| </li><li> |
| <a href='http://stackoverflow.com/questions/tagged/groovy' class='icon'><span class='fa fa-stack-overflow'></span> Stack Overflow questions</a> |
| </li><li> |
| <a href='http://groovycommunity.com/' class='icon'><span class='fa fa-slack'></span> Slack Community</a> |
| </li> |
| </ul> |
| </nav><div class='st-pusher'> |
| <div class='st-content'> |
| <div class='st-content-inner'> |
| <!--[if lt IE 7]> |
| <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p> |
| <![endif]--><div><div class='navbar navbar-default navbar-static-top' role='navigation'> |
| <div class='container'> |
| <div class='navbar-header'> |
| <button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'> |
| <span class='sr-only'></span><span class='icon-bar'></span><span class='icon-bar'></span><span class='icon-bar'></span> |
| </button><a class='navbar-brand' href='../index.html'> |
| <i class='fa fa-star'></i> Apache Groovy |
| </a> |
| </div><div class='navbar-collapse collapse'> |
| <ul class='nav navbar-nav navbar-right'> |
| <li class=''><a href='http://groovy-lang.org/learn.html'>Learn</a></li><li class=''><a href='http://groovy-lang.org/documentation.html'>Documentation</a></li><li class=''><a href='/download.html'>Download</a></li><li class=''><a href='http://groovy-lang.org/support.html'>Support</a></li><li class=''><a href='/'>Contribute</a></li><li class=''><a href='http://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li class=''><a href='/blogs'>Blog posts</a></li><li class=''><a href='https://groovy.apache.org/events.html'></a></li><li> |
| <a data-effect='st-effect-9' class='st-trigger' href='#'>Socialize</a> |
| </li><li class=''> |
| <a href='../search.html'> |
| <i class='fa fa-search'></i> |
| </a> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div><div id='content' class='page-1'><div class='row'><div class='row-fluid'><div class='col-lg-3'><ul class='nav-sidebar'><li><a href='./'>Blog index</a></li><li class='active'><a href='#doc'>Life on Mars: Units of Measurement systems, Groovy, and domain specific languages (DSLs)</a></li><li><a href='#_units_of_measurement_systems' class='anchor-link'>Units of measurement systems</a></li><li><a href='#_jsr_385_units_of_measurement_api_2_0' class='anchor-link'>JSR 385: Units of Measurement API 2.0</a></li><li><a href='#_adding_some_metaprogramming' class='anchor-link'>Adding some metaprogramming</a></li><li><a href='#_a_dynamic_dsl_for_controlling_a_mars_rover' class='anchor-link'>A dynamic DSL for controlling a Mars Rover</a></li><li><a href='#_a_type_rich_dsl_for_the_rover' class='anchor-link'>A type-rich DSL for the Rover</a></li><li><a href='#_adding_custom_type_checking' class='anchor-link'>Adding custom type checking</a></li><li><a href='#_further_information' class='anchor-link'>Further information</a></li><li><a href='#_conclusion' class='anchor-link'>Conclusion</a></li></ul></div><div class='col-lg-8 col-lg-pull-0'><a name='doc'></a><h1>Life on Mars: Units of Measurement systems, Groovy, and domain specific languages (DSLs)</h1><p><span>Author: <i>Paul King</i></span><br/><span>Published: 2022-08-13 06:31AM</span></p><hr/><div id="preamble"> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p><span class="image right"><img src="img/duke_measure.png" alt="duke_measuring"></span> |
| The Mars Climate Orbiter was launched in 1998 as part |
| of a multi-faceted Mars exploration program. |
| It was lost due to a trajectory calculation error when |
| nearing Mars. An <a href="http://scholar.google.com/scholar?hl=en&q=Stephenson+A%2C+LaPiana+L%2C+Mulville+D%2C+et+al.+Mars+climate+orbiter+mishap+investigation+board+phase+1+report%3B+1999">investigation</a> attributed the |
| failure to a measurement mismatch between two software systems: |
| metric units were used by NASA and <a href="https://en.wikipedia.org/wiki/United_States_customary_units">US customary</a> units were used by spacecraft builder Lockheed Martin. |
| <span class="image"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Mars_Climate_Orbiter_2.jpg/1280px-Mars_Climate_Orbiter_2.jpg" alt="Mars Climate Orbiter - image credit: wikipedia" width="500"></span></p> |
| </div> |
| <div class="paragraph"> |
| <p>If the system in question had been developed using a units of |
| measurement system, perhaps the failure could have been avoided.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_units_of_measurement_systems">Units of measurement systems</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>All programming languages have types for representing numbers. |
| As an example, we may have three integers, one representing a |
| height, one a weight and one a temperature. We can write code |
| to add those three integers together, but the result may have |
| no useful meaning. We could start writing our own class |
| hierarchies for each integer type, but it can quickly become |
| cumbersome to use the resulting classes. Units of measurement |
| systems attempt to provide a range of commonplace units, ways |
| to construct quantities from those units and ways to manipulate |
| them. Manipulation might involve performing numerical calculation |
| or conversions. Goals of such systems include the ability to |
| provide runtime and/or compile-time type safety. So, we should |
| fail early when trying to perform the earlier mentioned addition |
| of three unrelated quantities.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Units of measurement systems aren’t new. There are existing |
| systems in F# and Swift and Java has had several versions |
| (and earlier attempts) around standardising such a system including:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><a href="https://jcp.org/en/jsr/detail?id=275">JSR 275</a>: Units Specification (rejected earlier attempt)</p> |
| </li> |
| <li> |
| <p><a href="https://jcp.org/en/jsr/detail?id=363">JSR 363</a>: Units of Measurement API</p> |
| </li> |
| <li> |
| <p><a href="https://jcp.org/en/jsr/detail?id=385">JSR 385</a>: Units of Measurement API 2.0</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>There are also existing Java libraries like |
| <a href="http://jscience.org/">JScience</a> which have been developed, and |
| <a href="https://objectcomputing.com/resources/publications/sett/june-2006-units-and-measures-with-jscience">shown early promise</a>, |
| but now don’t seem to be actively maintained. While the |
| <a href="https://onlinelibrary.wiley.com/doi/10.1002/spe.2926">jury is still out</a> |
| about whether units of measurement systems will spread further into |
| mainstream programming, now seems like a great time to check the |
| status. The JSR 385 maintenance release was approved last year and |
| the latest version of the reference implementation was released |
| earlier this year.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_jsr_385_units_of_measurement_api_2_0">JSR 385: Units of Measurement API 2.0</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>The first thing we need to do is bring in our dependencies (shown for <a href="https://gradle.org/">Gradle</a> - <a href="https://mvnrepository.com/artifact/tech.units/indriya/2.1.3">other options</a>). |
| The main one is the reference implementation (it brings in the |
| <code>javax.measure</code> API transitively):</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">implementation 'tech.units:indriya:2.1.3'</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>JSR 385 is extensible. We’ll also bring in some units from the |
| <a href="https://cldr.unicode.org/translation/units">Unicode CLDR</a> units, |
| e.g. `MILE`:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">implementation 'systems.uom:systems-unicode:2.1' |
| implementation 'systems.uom:systems-quantity:2.1'</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Let’s follow on with the theme of visiting Mars. We can create |
| variables for the mass and diameter of Mars as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">var massₘ = Quantities.getQuantity(6.39E23, KILO(GRAM)) |
| var diameterₘ = Quantities.getQuantity(6_779, KILOMETER) |
| println massₘ |
| println diameterₘ</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>JSR 385 has metric prefix qualifiers like <code>MICRO</code>, |
| <code>MILLI</code>, <code>CENTI</code>, <code>KILO</code>, <code>MEGA</code>, <code>GIGA</code> and many more. |
| The CLDR units also define some commonly used units like |
| <code>KILOMETER</code>. We could choose either here.</p> |
| </div> |
| <div class="paragraph"> |
| <p>When we run this script, it has the following output:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre>639000000000000000000000 kg |
| 6779 km</pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>If we try to compare or add those two values, we’ll see an error. |
| Groovy has both static and dynamic natures. Using dynamic code |
| like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">println massₘ > diameterₘ</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We’ll see a runtime error like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre><span class="maroon">javax.measure.IncommensurableException: km is not compatible with kg</span></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Or, with <code>TypeChecked</code> or <code>CompileStatic</code> in play for a statement like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">println massₘ.add(diameterₘ)</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We’d see a compile-time error like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre><span class="maroon">[Static type checking] - Cannot call tech.units.indriya.ComparableQuantity#add(javax.measure.Quantity<javax.measure.quantity.Mass>) |
| with arguments [tech.units.indriya.ComparableQuantity<javax.measure.quantity.Length>]</span></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>If for some strange reason we did want to compare or perform |
| calculations between incommensurable types, we can explicitly |
| get the value:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">assert massₘ.value > diameterₘ.value</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This escape hatch takes off a layer of type safety but requires |
| explicit work to do so. It is normally never the case that you |
| would want to do this.</p> |
| </div> |
| <div class="paragraph"> |
| <p>JSR 385 also supports ranges and conversions. We can look at the |
| minimum and maximum temperatures on Mars:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">var minTemp = Quantities.getQuantity(-128, CELSIUS) |
| var maxTemp = Quantities.getQuantity(70, FAHRENHEIT) |
| println minTemp |
| println minTemp.to(FAHRENHEIT) |
| println maxTemp |
| println maxTemp.to(CELSIUS) |
| println QuantityRange.of(minTemp, maxTemp)</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>It’s quite a bit colder than Earth! When run, |
| this script has the following output:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre>-128 ℃ |
| -198.400 ((K*5)/9)+459.67 |
| 70 ((K*5)/9)+459.67 |
| 21.1111111111111111111111111111111 ℃ |
| min= -128 ℃, max= 70 ((K*5)/9)+459.67</pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In case you’re wondering about the strange looking unit display |
| for Fahrenheit temperatures, the definition of that unit that |
| we are using, defines degrees Fahrenheit using a formula |
| calculated from the temperature in degrees Kelvin.</p> |
| </div> |
| <div class="paragraph"> |
| <p>We’d see the same thing if using the <code>MILE</code> unit:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">println diameterₘ.to(MILE)</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Which shows us that the diameter of Mars is a little over 4200 miles:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre>4212.275312176886980036586335798934 (m*1609344)/1000</pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_adding_some_metaprogramming">Adding some metaprogramming</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Groovy has various features which allow methods to be (apparently) |
| added to classes. We’ll use extension methods. This technique |
| involves writing static methods in a helper class using certain |
| conventions. The first parameter in all such methods is the target |
| of the extension. Groovy code referencing instances of the target |
| class have code that can call such a method as if it existed on |
| the target class. In reality, the Groovy compiler or runtime |
| funnels the call through the helper class. For us, it means we |
| will have methods like <code>getMeters()</code> on the <code>Number</code> class which |
| using Groovy’s shorthand’s for property notation allows for very |
| compact quantity definitions like <code>5.meters</code>. We’ll also add some |
| methods to allow Groovy’s normal operator overloading syntax to |
| apply:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">class UomExtensions { |
| static Quantity<Length> getCentimeters(Number num) { Quantities.getQuantity(num, CENTI(METRE)) } |
| |
| static Quantity<Length> getMeters(Number num) { Quantities.getQuantity(num, METRE) } |
| |
| static Quantity<Length> getKilometers(Number num) { Quantities.getQuantity(num, KILO(METRE)) } |
| |
| static Quantity<Length> getCm(Number num) { getCentimeters(num) } |
| |
| static Quantity<Length> getM(Number num) { getMeters(num) } |
| |
| static Quantity<Length> getKm(Number num) { getKilometers(num) } |
| |
| static Quantity<Mass> getKilograms(Number num) { Quantities.getQuantity(num, KILO(GRAM)) } |
| |
| static Quantity<Mass> getKgs(Number num) { getKilograms(num) } |
| |
| static Quantity<Time> getHours(Number num) { Quantities.getQuantity(num, HOUR) } |
| |
| static Quantity<Time> getSeconds(Number num) { Quantities.getQuantity(num, SECOND) } |
| |
| static Quantity<Time> getHr(Number num) { getHours(num) } |
| |
| static Quantity<Time> getS(Number num) { getSeconds(num) } |
| |
| static Quantity<Speed> div(Quantity<Length> q, Quantity<Time> divisor) { q.divide(divisor) as Quantity<Speed> } |
| |
| static <Q> Quantity<Q> div(Quantity<Q> q, Number divisor) { q.divide(divisor) } |
| |
| static <Q> Quantity<Q> plus(Quantity<Q> q, Quantity<Q> divisor) { q.add(divisor) } |
| |
| static <Q> Quantity<Q> minus(Quantity<Q> q, Quantity<Q> divisor) { q.subtract(divisor) } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Note that we have longer and shorter versions of many of |
| the methods, e.g. `kg` and <code>kilogram</code>, <code>m</code> and <code>meter</code>. |
| We didn’t need a method for <code>multiply</code> since it is |
| already using the name Groovy expects.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Now we can write very short definitions to declare |
| or compare times and lengths:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">def s = 1.s |
| assert 1000.meters == 1.km && 1.m == 100.cm</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We can also declare variables for acceleration due to |
| gravity on Earth and Mars. Gravity is a lot less on Mars:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">var gₘ = 3.7.m/s/s |
| var gâ‚‘ = 9.8.m/s/s |
| assert gₑ.toString() == '9.8 m/s²' |
| assert gₑ > gₘ</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We can also use the operator overloading in calculations |
| (here showing that the Earth has a diameter that is between |
| 1.8 and 2 times bigger than that of Mars):</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">var diameterâ‚‘ = 12_742.kilometers |
| assert diameterₘ + diameterₘ > diameterₑ |
| assert diameterₑ - diameterₘ < diameterₘ |
| assert diameterₘ * 1.8 < diameterₑ</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Even though we have more compact expressions, the same data |
| types are in play that we saw previously. They’re just a |
| little nicer to type.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_a_dynamic_dsl_for_controlling_a_mars_rover">A dynamic DSL for controlling a Mars Rover</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Let’s now look at how you could write a little |
| Domain-Specific-Language (DSL) to control a Mars rover robot.</p> |
| </div> |
| <div class="paragraph"> |
| <p><span class="image"><img src="img/mars_rover_selfie.jpg" alt="Mars rover selfie" width="500"></span></p> |
| </div> |
| <div class="paragraph"> |
| <p>First, we’ll write a <code>Direction</code> enum as part of |
| our robot domain model:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">enum Direction { |
| left, right, forward, backward |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>There are many ways to write DSLs in Groovy. We’ll use a little |
| trick where the verbs are represented as keys in a map. |
| Our DSL then looks like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">def move(Direction dir) { |
| [by: { Quantity<Length> dist -> |
| [at: { Quantity<Speed> speed -> |
| println "robot moved $dir by $dist at $speed" |
| }] |
| }] |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Here the implementation is just going to print out a message |
| indicating all of the values it is processing. The real robot |
| would send signals to the rover’s robotic subsystems.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Our script for controlling the rover now looks like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">move right by 2.m at 5.cm/s</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Which when run gives this output:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre>robot moved right by 2 m at 5 cm/s</pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>As we saw earlier, this is backed by our JSR 385 types. |
| We’ll certainly get fail-early runtime errors if there are |
| any calculations involving mismatched types.</p> |
| </div> |
| <div class="paragraph"> |
| <p>If we enable static typing, some additional errors will be |
| detected at compile but because of the very dynamic style |
| of our DSL implementation, not all runtime errors are |
| reflected by typing information. If we want, we can change |
| our DSL implementation to use richer types and that will |
| support better static typing checking. We’ll look at one |
| way to do that next.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_a_type_rich_dsl_for_the_rover">A type-rich DSL for the Rover</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>Now, instead of using our nested map style we saw previously, |
| we create several richly-typed helper classes and define our |
| <code>move</code> method in terms of those classes:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">class MoveHolder { |
| Direction dir |
| ByHolder by(Quantity<Length> dist) { |
| new ByHolder(dist: dist, dir: dir) |
| } |
| } |
| |
| class ByHolder { |
| Quantity<Length> dist |
| Direction dir |
| void at(Quantity<Speed> speed) { |
| println "robot moved $dir by $dist at $speed" |
| } |
| } |
| |
| static MoveHolder move(Direction dir) { |
| new MoveHolder(dir: dir) |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>While our DSL implementation has changed, the robot scripts |
| remain the same:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">move right by 2.m at 5.cm/s</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Indeed, if we use Groovy dynamic nature, we can still run the same |
| script and will notice no change.</p> |
| </div> |
| <div class="paragraph"> |
| <p>If however, we enable static checking and have a script with |
| an error like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">move forward by 2.kgs</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We’ll now see a compile-time error:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre><span class="maroon">[Static type checking] - Cannot call MoveHolder#by(javax.measure.Quantity<javax.measure.quantity.Length>) with arguments [javax.measure.Quantity<javax.measure.quantity.Mass>]</span></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>It is great to get this additional earlier feedback on script |
| errors, so you may wonder why we don’t write our DSL implementations |
| like this all of the time? Actually, both the dynamic and static |
| flavors of our DSL can be useful at different times. When |
| prototyping our script DSL, deciding on all the nouns and |
| verbs that we should be using to control our robot, the |
| dynamic flavored style can be much quicker to write especially |
| during early iterations which might evolve and change rapidly. |
| Once the DSL language has been locked down, we can invest in |
| adding the richer types. In the rover scenario, it might also |
| be the case that the rover itself has limited power and so may |
| not want to perform additional type checking steps. We might |
| run all scripts through a type checker back at mission control |
| before sending them through to the rover where they may be |
| enacted in dynamic mode.</p> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_adding_custom_type_checking">Adding custom type checking</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>There is one additional language feature of Groovy we haven’t |
| mentioned. Groovy’s type checking mechanism is extensible, so |
| we’ll have a look at using that feature here. The rover’s speed |
| is <a href="https://mars.nasa.gov/mars2020/spacecraft/rover/wheels/">rather limited</a>, <em>"In the case of exploring Mars, however, speed isn’t the most relevant quality. It’s about the journey and the destinations along the way. The slow pace is energy-efficient…"</em>. |
| Let’s look at limiting the speed to avoid unsafe or |
| energy wasting movement.</p> |
| </div> |
| <div class="paragraph"> |
| <p>We could put early defensive checks in our DSL implementation to |
| detect undesirable manoeuvres, but we can also use type checking |
| extensions for certain kinds of errors. Groovy in fact has its |
| own DSL for writing such extensions. That’s a topic for its own |
| blog but here’s what the code looks like:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">afterMethodCall { call -> |
| def method = getTargetMethod(call) |
| if (method.name != 'at') return |
| if (call.arguments.size() != 1) return |
| def arg = call.arguments[0] |
| if (arg !instanceof BinaryExpression) return |
| def left = arg.leftExpression |
| if (left !instanceof PropertyExpression) return |
| def obj = left.objectExpression |
| if (obj !instanceof ConstantExpression) return |
| if (obj.value > 5) { |
| addStaticTypeError("Speed of $obj.value is too fast!",call) |
| handled = true |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This is only a partial implementation, it makes numerous |
| assumptions. We could remove those assumptions by adding |
| more code, but for now we’ll keep this simplified version.</p> |
| </div> |
| <div class="paragraph"> |
| <p>So, now the following script (with the above type checking extension applied) compiles fine:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">move right by 2.m at 5.cm/s</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>But this script fails:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">move right by 2.m at 6.cm/s</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The error message is:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre><span class="maroon">[Static type checking] - Speed of 6 is too fast!</span></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_further_information">Further information</h2> |
| <div class="sectionbody"> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><a href="https://www.jcp.org/en/jsr/detail?id=385">JSR 385: Units of Measurement API 2.0</a></p> |
| </li> |
| <li> |
| <p><a href="https://unitsofmeasurement.github.io/2017/taste_of_indriya.html">A Taste of Indriya</a></p> |
| </li> |
| <li> |
| <p><a href="https://betterprogramming.pub/unit-and-measurement-in-swift-7c6be4a25586">Unit and Measurement in Swift</a></p> |
| </li> |
| <li> |
| <p><a href="https://docs.microsoft.com/en-us/archive/blogs/andrewkennedy/">Units of Measure in F#</a></p> |
| </li> |
| <li> |
| <p><a href="https://varkor.github.io/blog/2018/07/30/types-for-units-of-measure.html">Types for units of measure</a></p> |
| </li> |
| <li> |
| <p><a href="https://www.slideshare.net/keilw/how-jsr-385-could-have-saved-the-mars-climate-orbiter-jfokus-2020">How JSR 385 could have Saved the Mars Climate Orbiter - JFokus 2020</a> (slide deck)</p> |
| </li> |
| <li> |
| <p><a href="https://www.manning.com/books/groovy-in-action-second-edition">Groovy in Action</a> (in particular the DSL chapter)</p> |
| </li> |
| <li> |
| <p><a href="https://objectcomputing.com/resources/publications/sett/june-2006-units-and-measures-with-jscience">Units and Measures with JScience</a></p> |
| </li> |
| <li> |
| <p><a href="https://onlinelibrary.wiley.com/doi/10.1002/spe.2926">Unit of measurement libraries, their popularity and suitability</a></p> |
| </li> |
| <li> |
| <p><a href="https://speakerdeck.com/glaforge/groovy-domain-specific-languages-techniques">Groovy Domain-Specific Languages techniques</a> (slide deck)</p> |
| </li> |
| <li> |
| <p><a href="http://docs.groovy-lang.org/docs/latest/html/documentation/core-domain-specific-languages.html">Domain-Specific Languages</a> (Groovy documentation)</p> |
| </li> |
| <li> |
| <p><a href="https://www.baeldung.com/javax-measure">Introduction to javax.measure</a></p> |
| </li> |
| <li> |
| <p><a href="https://github.com/paulk-asert/UomGroovy" class="bare">https://github.com/paulk-asert/UomGroovy</a> (repo)</p> |
| </li> |
| </ul> |
| </div> |
| </div> |
| </div> |
| <div class="sect1"> |
| <h2 id="_conclusion">Conclusion</h2> |
| <div class="sectionbody"> |
| <div class="paragraph"> |
| <p>We have looked at using the JSR 385 <code>javax.measure</code> API using |
| Groovy and added some DSL examples to make using the API |
| a little nicer.</p> |
| </div> |
| </div> |
| </div></div></div></div></div><footer id='footer'> |
| <div class='row'> |
| <div class='colset-3-footer'> |
| <div class='col-1'> |
| <h1>Groovy</h1><ul> |
| <li><a href='http://groovy-lang.org/learn.html'>Learn</a></li><li><a href='http://groovy-lang.org/documentation.html'>Documentation</a></li><li><a href='/download.html'>Download</a></li><li><a href='http://groovy-lang.org/support.html'>Support</a></li><li><a href='/'>Contribute</a></li><li><a href='http://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li><a href='/blogs'>Blog posts</a></li><li><a href='https://groovy.apache.org/events.html'></a></li> |
| </ul> |
| </div><div class='col-2'> |
| <h1>About</h1><ul> |
| <li><a href='https://github.com/apache/groovy'>Source code</a></li><li><a href='http://groovy-lang.org/security.html'>Security</a></li><li><a href='http://groovy-lang.org/learn.html#books'>Books</a></li><li><a href='http://groovy-lang.org/thanks.html'>Thanks</a></li><li><a href='http://www.apache.org/foundation/sponsorship.html'>Sponsorship</a></li><li><a href='http://groovy-lang.org/faq.html'>FAQ</a></li><li><a href='http://groovy-lang.org/search.html'>Search</a></li> |
| </ul> |
| </div><div class='col-3'> |
| <h1>Socialize</h1><ul> |
| <li><a href='http://groovy-lang.org/mailing-lists.html'>Discuss on the mailing-list</a></li><li><a href='https://twitter.com/ApacheGroovy'>Groovy on Twitter</a></li><li><a href='http://groovy-lang.org/events.html'>Events and conferences</a></li><li><a href='https://github.com/apache/groovy'>Source code on GitHub</a></li><li><a href='http://groovy-lang.org/reporting-issues.html'>Report issues in Jira</a></li><li><a href='http://stackoverflow.com/questions/tagged/groovy'>Stack Overflow questions</a></li><li><a href='http://groovycommunity.com/'>Slack Community</a></li> |
| </ul> |
| </div><div class='col-right'> |
| <p> |
| The Groovy programming language is supported by the <a href='http://www.apache.org'>Apache Software Foundation</a> and the Groovy community. |
| </p><div text-align='right'> |
| <img src='../img/asf_logo.png' title='The Apache Software Foundation' alt='The Apache Software Foundation' style='width:60%'/> |
| </div><p>Apache® and the Apache feather logo are either registered trademarks or trademarks of The Apache Software Foundation.</p> |
| </div> |
| </div><div class='clearfix'>© 2003-2023 the Apache Groovy project — Groovy is Open Source: <a href='http://www.apache.org/licenses/LICENSE-2.0.html' alt='Apache 2 License'>license</a>, <a href='https://privacy.apache.org/policies/privacy-policy-public.html'>privacy policy</a>.</div> |
| </div> |
| </footer></div> |
| </div> |
| </div> |
| </div> |
| </div><script src='../js/vendor/jquery-1.10.2.min.js' defer></script><script src='../js/vendor/classie.js' defer></script><script src='../js/vendor/bootstrap.js' defer></script><script src='../js/vendor/sidebarEffects.js' defer></script><script src='../js/vendor/modernizr-2.6.2.min.js' defer></script><script src='../js/plugins.js' defer></script><script src='https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.js'></script><script>document.addEventListener('DOMContentLoaded',prettyPrint)</script><script> |
| (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ |
| (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), |
| m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) |
| })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); |
| |
| ga('create', 'UA-257558-10', 'auto'); |
| ga('send', 'pageview'); |
| </script> |
| </body></html> |