| <!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='groovy, chatgpt, one-liners'/><meta name='description' content='I asked ChatGPT if it could represent some short/one-liner programming-related dad jokes as short/one-liner Groovy scripts.'/><title>The Apache Groovy programming language - Blogs - ChatGPT meets Groovy one-liners</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='https://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='https://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='https://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='https://groovy-lang.org/learn.html'>Learn</a></li><li class=''><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li class=''><a href='/download.html'>Download</a></li><li class=''><a href='https://groovy-lang.org/support.html'>Support</a></li><li class=''><a href='/'>Contribute</a></li><li class=''><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li class=''><a href='/blog'>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'>ChatGPT meets Groovy one-liners</a></li></ul></div><div class='col-lg-8 col-lg-pull-0'><a name='doc'></a><h1>ChatGPT meets Groovy one-liners</h1><p><span>Author: <i>Paul King</i></span><br/><span>Published: 2023-10-19 06:00PM</span></p><hr/><div class="paragraph"> |
| <p>I asked ChatGPT if it could represent some short/one-liner programming-related dad |
| jokes as short/one-liner Groovy scripts. It’s responses were initially a little |
| underwhelming but after some coercing and some "post-production tweaking", this blog post contains the result. Be forewarned, that the quality of the puns |
| is mixed.</p> |
| </div> |
| <div class="paragraph"> |
| <p>But before starting, Groovy makes an excellent language for interacting |
| with cloud-based Generative AI systems. You can check out the |
| following <a href="https://github.com/paulk-asert/demo">GitHub repo</a> that |
| shows how to talk to ChatGPT using Groovy and <a href="https://micronaut.io/">Micronaut</a>. |
| This example is in turn based on <a href="https://www.kousenit.com/">Ken Kousen</a>'s similar example in Java. |
| Check out his excellent <a href="https://www.youtube.com/watch?v=cEl3He6Tsmk">video on the topic</a>. Also check out <a href="https://glaforge.dev/">Guillaume Laforge</a>'s recent posts |
| about using Groovy to talk to Google’s <a href="https://bard.google.com/">Bard</a> via |
| its <a href="https://glaforge.dev/posts/2023/05/30/getting-started-with-the-palm-api-in-the-java-ecosystem/">PaLM 2 API</a> and using |
| <a href="https://glaforge.dev/posts/2023/09/25/discovering-langchain4j/">LangChain4J</a>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Having said that, we just used the Web UI for this blog post. |
| Now, let’s have a look at some of those PUNch lines…​</p> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why do programmers always mix up Christmas</em> 🎄 <em>and Halloween</em> 🎃 <em>?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because 25 Dec(imal) is the same as 31 Oct(al).</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We can check that with the following Groovy script:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">def christmas = 25 // dec |
| def halloween = 031 // oct |
| assert christmas == halloween</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why is Groovy such a neat language?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because it takes a bit from Go, |
| supports data science like R though with low ceremony, |
| mixes in a dose of Python and Perl, takes a decent chunk of Java |
| with a twist of C#, and finishes with some Ruby.</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We can check that with the following Groovy script:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">assert 'Groovy' == 'Go'.take(1) + 'R'.toLowerCase() + |
| 'Python'['Perl'.size()] * 2 + |
| 'Java'['C#'.size()] + 'Ruby'[-1]</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why did the developer go broke?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because his money cache, intellisense and null stocks were all empty</em> 💰.</p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We combine the <code>Expando</code> class with the Elvis operator and Groovy truth to check this fact:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">def money = intelli = new Expando() |
| def rich = money.cache ?: intelli.cents ?: null?.stocks |
| assert !rich</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why did the programmer think using Groovy was sweet?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because creating a box of a-sorted chocolates was easy.</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We create a "sortable" <code>Chocolate</code> record making use of default parameters and declarative augmentation. Then we create a <code>Box</code> class which delegates to <code>List</code>. We then create a box of chocolates and check it is "a-sorted" using the <code>toString()</code> method:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">@Sortable @ToString(ignoreNulls = true) |
| record Chocolate(int cocoa, String filling = null) {} |
| |
| class Box { @Delegate List<Chocolate> contents } |
| |
| var box = new Box(contents: [ |
| new Chocolate(80), |
| new Chocolate(50, 'Caramel') |
| ].sort()) |
| |
| assert box.toString() == '[Chocolate(50, Caramel), Chocolate(80)]'</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>If life gives you melons, …​</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>…​you may be dyslexic.</em> 🍈🍋</p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Let’s take the word 'melons' and jumble it around a bit using Groovy’s flexible indexing to see if we get 'lemons':</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">def dyslexic = 'melons'[2..0,3..-1] == 'lemons' |
| assert dyslexic</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>6:30 is the best time on a clock, hands down.</em> 🕡</p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Let’s create a <code>LocalTime</code> of 6:30 and check that the hands are pointing down. The minute hand travels 360 degrees in 60 minutes, |
| so 6 degrees per minute. We can check at 6:30 that it is pointing straight down (180°). |
| The hour hand travels 360° in 720 minutes (1/2 a day), so we divide the total minutes by 2. The total minutes is 60 times the number of hours plus the minutes for the current hour. |
| At 6:30, we know the hour hand will have travelled halfway between the 6 and 7 hour markers (15°) but for our test we’ll |
| just make sure it is point down (we’ll say within 20° of straight down which we’ll specify as a range). Here is the result:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">def (h, m) = LocalTime.of(6, 30)[HOUR_OF_DAY, MINUTE_OF_HOUR] |
| assert 6 * m == 180L |
| assert (h * 60 + m).intdiv(2) in 180L..200L</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: What’s the difference between ignorance and apathy?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: I don’t know and I don’t care.</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>To show this with Groovy, we should first declare our |
| ignorance and apathy. Then we calculate the difference |
| and finally check our assertions:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">int ignorance, apathy |
| int know = care = ignorance - apathy |
| assert !know && !care</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Can Groovy help me answer the question: Which came first, the chicken or the egg?</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Sure, run this script:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">def (first, second) = ['egg', 'chicken'].shuffled() |
| println "Which came first, the $first or the $second? The eternal debate!"</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>It really just asks the question in two different orders, |
| but I’m sure you get the idea.</p> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why are death, taxes, and immutability similar?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because, in life and programming, they’re the only things you can’t change!</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Since death and taxes are certain, we should store a quote |
| about them in an immutable structure. |
| A record offers shallow immutability and works well for this. |
| For deep immutability, consider using the <code>@Immutable</code> AST transform, |
| so here even though our quotes are stored in a mutable ArrayList, |
| there is no way to change it. |
| Here is an example:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">record Quote(String text, String author, int year) { } |
| var q1 = new Quote('Perfection is immutable. But for things imperfect, change is the way to perfect them.', 'Owen Feltham', 1840) |
| var q2 = new Quote('Nothing is certain except death and taxes.', 'Benjamin Franklin', 1789) |
| |
| @Immutable |
| class FavoriteQuotes { List<Quote> list } |
| |
| var favorites = new FavoriteQuotes([q1, q2])</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why are diamonds and dogs a person’s best friends?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because diamonds are forever, and dogs are fur-ever!</em> 💎 🐕</p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Let’s use traits to check best friends for diamonds and dogs:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">trait HasBestFriend { |
| abstract String friend() |
| boolean isBestFriend(String candidate) { |
| friend() == candidate |
| } |
| } |
| |
| class Diamond implements HasBestFriend { |
| String friend() { 'girl' } |
| } |
| |
| class Dog implements HasBestFriend { |
| String friend() { 'man' } |
| } |
| |
| assert ['man', 'girl'].collect{ |
| [new Diamond().isBestFriend(it), |
| new Dog().isBestFriend(it)] |
| } == [[false, true], [true, false]]</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why did the Land Rover car get along so well with the Land Rover Explore smartphone?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because they both knew the importance of "exploring" new territories, |
| whether off-road or online!!</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Who knew that Cars and Smartphones had anything to do with each other, |
| but the trend seems to be companies want to get involved in both. |
| Similarly, if you have two classes that apparently have nothing to |
| do with one another, Groovy’s duck typing, or in this case |
| property handling might allow you to use them together more easily |
| than you think.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">import groovy.transform.* |
| |
| record Smartphone(String make, String model, String color, int year) { } |
| |
| record Car(String make, String model, String color, int year) { } |
| |
| def s = new Smartphone('Landrover', 'Explore', 'Black', 2018) |
| def c = new Car(s.toMap()) |
| assert c.toString() == 'Car[make=Landrover, model=Explore, color=Black, year=2018]'</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why did the Jedi use a mind trick on the stormtroopers when they couldn’t find the missing droids?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because, as Obi-Wan said, "These aren’t the droids you’re looking for. You’re actually looking for your misplaced keys, and you’ll find them in the last place you look!"</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Let’s first show how Groovy could help us find some droids that we are looking for. |
| Drones are hardest to find when there are several clone look-a-likes. We won’t mention the clone wars!</p> |
| </div> |
| <div class="paragraph"> |
| <p>We’ll create a shuffled list of droids and their clones, and then search for the ones we are after:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">@AutoClone class Droid { String name } |
| |
| def r2d2 = new Droid(name: 'R2-D2') |
| def c3po = new Droid(name: 'C-3PO') |
| def droids = [r2d2, c3po] |
| 3.times { |
| droids << r2d2.clone() |
| droids << c3po.clone() |
| } |
| droids.shuffle() |
| droids.eachWithIndex { droid, index -> |
| if (droid == r2d2) println "Droid $index is $r2d2.name" |
| if (droid == c3po) println "Droid $index is $c3po.name" |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>If we don’t want to output the index where we found the droid, we can use |
| an alternative expression to show that the droids we are after are found:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">assert droids.any{ it.is(r2d2) } |
| assert droids.any{ it.is(c3po) }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Of course, Obi-Wan uses a Jedi mind trick which we can show here using |
| some Groovy metaprogramming, in this case a category class:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">class JediMindTrick { |
| static boolean is(Droid d, Droid other) { false } |
| } |
| |
| use(JediMindTrick) { |
| assert !droids.any{ it.is(r2d2) } |
| assert !droids.any{ it.is(c3po) } |
| }</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: What happened when the pirates pet bird tried to say "Pieces of 7"?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: It got a ParrotyError!"</em> 🦜</p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>As shown here:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">try { |
| pet.say('Pieces of 7') |
| } catch(ParrotyError e) { |
| var plank = e.stackTrace |
| plank.walk() |
| }</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: Why did the functions stop calling each other?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: Because they had constant arguments.</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>By definition, pure functions always return |
| the same result for the same inputs. |
| As such, a potential optimization for pure functions |
| is to cache the result for a given set of input values. |
| Groovy provides the <code>@Memoized</code> AST transform to do this |
| (and a <code>.memoized()</code> method call for Closures).</p> |
| </div> |
| <div class="paragraph"> |
| <p>Let’s write a <code>StringUtil</code> class |
| that has an <em>almost</em> pure function. The return value of the <code>bothCases</code> method |
| is a pure function in terms of its inputs. It returns a list containing |
| the lowercase and uppercase values for the input string. |
| It also has a side effect of incrementing a counter whenever it is called; |
| this is just so we can understand what is going on.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">class StringUtil { |
| static int count = 0 |
| |
| @Memoized |
| static List<String> bothCases(String s) { |
| count++ |
| [s.toLowerCase(), s.toUpperCase()] |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>We apply the <code>@Memoized</code> annotation to <code>bothCases</code> which enables the automatic |
| caching. The annotation has a number of optional annotation attributes for |
| configuring the caching behavior, but we’ll just use the defaults.</p> |
| </div> |
| <div class="paragraph"> |
| <p>We can see that for constant arguments, 'Foo' called twice in our case, |
| that there is no need to invoke the <code>bothCases</code> method on the second call, |
| since we can use the cached value from the previous call.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">assert StringUtil.count == 0 |
| assert StringUtil.bothCases('Foo') == ['foo', 'FOO'] |
| assert StringUtil.count == 1 |
| assert StringUtil.bothCases('Foo') == ['foo', 'FOO'] |
| assert StringUtil.count == 1</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Binary: it’s easy as 1, 10, 11</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">println "Binary: It's as easy as ${(1..3).collect{ Integer.toBinaryString(it) }.join(', ')}"</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: On my first day of work at the sheep farm, I was asked to roundup 37 sheep?</em></p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: I said 40!</em> 🐑 🐏</p> |
| </div> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">assert 40 == Math.round(37/10)*10</code></pre> |
| </div> |
| </div> |
| <hr> |
| <div class="sidebarblock"> |
| <div class="content"> |
| <div class="paragraph"> |
| <p><em>Q: What code did the programmer write to celebrate Groovy’s 20th birthday?</em> 🎂</p> |
| </div> |
| <div class="paragraph"> |
| <p><em>A: A hip-hip array</em></p> |
| </div> |
| </div> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="prettyprint highlight"><code data-lang="groovy">String[] celebrate = ['hip', 'hip']</code></pre> |
| </div> |
| </div> |
| <hr></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='https://groovy-lang.org/learn.html'>Learn</a></li><li><a href='https://groovy-lang.org/documentation.html'>Documentation</a></li><li><a href='/download.html'>Download</a></li><li><a href='https://groovy-lang.org/support.html'>Support</a></li><li><a href='/'>Contribute</a></li><li><a href='https://groovy-lang.org/ecosystem.html'>Ecosystem</a></li><li><a href='/blog'>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='https://groovy-lang.org/security.html'>Security</a></li><li><a href='https://groovy-lang.org/learn.html#books'>Books</a></li><li><a href='https://groovy-lang.org/thanks.html'>Thanks</a></li><li><a href='http://www.apache.org/foundation/sponsorship.html'>Sponsorship</a></li><li><a href='https://groovy-lang.org/faq.html'>FAQ</a></li><li><a href='https://groovy-lang.org/search.html'>Search</a></li> |
| </ul> |
| </div><div class='col-3'> |
| <h1>Socialize</h1><ul> |
| <li><a href='https://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='https://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='https://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> |