PageSourceSearch

https://jsonnet.org/learning/tutorial.html

html jsonnet.org collected 2026-10-01 10:40:23 UTC 75,480 bytes, 3,007 lines download raw bytes

1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2<html xmlns="http://www.w3.org/1999/xhtml">
3
4<head>
5    <title>Jsonnet - Tutorial</title>
6    <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
7    <meta name="keywords" content="Jsonnet, JSON, YAML, language, configuration, configuration language, functional, declarative, lazy, structured, elegant, semantics, clean, mixins, inheritance, template, expansion, expand" />
8    <meta name="description" content="A powerful DSL for elegant description of JSON data." />
9    <meta name="viewport" content="width=576">
10
11    <link rel="icon" type="image/png" href="/favicon.png" />
12
13    <!-- MathJax (for semantics) -->
14    
14<script type="text/x-mathjax-config">
15    MathJax.Hub.Config({
16        extensions: ["tex2jax.js"],
17        jax: ["input/TeX","output/HTML-CSS"],
18        displayAlign: "left",
19        imageFont: null,
20    });
21    </script>
21
22    
22<script type="text/javascript" src="/third_party/MathJax-2.7.2/MathJax.js"></script>
22
23    
23<script type="text/javascript" src="/third_party/anchor.min.js"></script>
23
24
25
26    <!-- Google Analytics -->
27    
27<script type="text/javascript">
vendor: 359 bytes, lines 27-33
27
28      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
29      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
30      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
31      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
32
33      ga('create', '
33UA-53570216-1
vendor: 46 bytes, lines 33-35
33', 'auto');
34      ga('send', 'pageview');
35    
35</script>
35
36
37    <!-- Google Fonts -->
38    <link href="https://fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,700italic,400,600,700" rel="stylesheet" type="text/css">
39
40    <!-- CodeMirror -->
41    <link rel="stylesheet" href="/third_party/CodeMirror/lib/codemirror.css">
42    
42<script src="/third_party/js-yaml/dist/js-yaml.min.js"></script>
42
43    
43<script src="/third_party/CodeMirror/lib/codemirror.js"></script>
43
44    
44<script src="/third_party/CodeMirror/mode/yaml/yaml.js"></script>
44
45    
45<script src="/third_party/CodeMirror/addon/edit/matchbrackets.js"></script>
45
46    
46<script src="/js/codemirror-mode-jsonnet.js"></script>
46
47
48    <!-- Executing Jsonnet -->
49    
49<script src="/js/wasm_exec.js"></script>
49
50    
50<script>
51        if (!WebAssembly.instantiateStreaming) {
52            // This function was observed to be missing on Safari 14.
53            WebAssembly.instantiateStreaming = async (resp, importObject) => {
54                const source = await (await resp).arrayBuffer();
55                return await WebAssembly.instantiate(source, importObject);
56            };
57        }
58        const go = new Go();
59        WebAssembly.instantiateStreaming(fetch("/js/libjsonnet.wasm"), go.importObject).then((result) => {
60            go.run(result.instance);
61        });
62    </script>
62
63    
63<script src="/js/demo.js"></script>
63
64
65    <!-- Navigation menus -->
66    
66<script src="/js/menu.js"></script>
66
67
68    <!-- jsonnet.org stylesheet -->
69    <link rel="stylesheet" type="text/css" href="/css/doc.css" />
70    <link rel="stylesheet" media="print" href="/css/desktop.css">
71    <!-- The extra 16px is to account for the scrollbar, which is not always included. -->
72    <link rel="stylesheet" media="screen and (min-width: 1296px)" href="/css/desktop.css">
73    <link rel="stylesheet" media="screen and (max-width: 1295px) and (min-width: 720px)" href="/css/cellphone.css">
74    <link rel="stylesheet" media="screen and (max-width: 719px)" href="/css/cellphone-small.css">
75</head>
76
77<body>
78
79<div id=header>
80  <div class=header2>
81    <a id=header-logo class=header-element href="/"><img src="/img/isologo.svg"></a>
82    <a id=header-title class=header-element href="/">Jsonnet</a>
83
84    <div class="header-element menu">
85      <div onclick='menu_open_popup(find_enclosing_menu(this))' onmouseover='menu_open_popup(find_enclosing_menu(this))' onmouseout='menu_leave()'>
86        Learning
87      </div>
88      <div class=pop-up onmouseover='menu_open_popup(find_enclosing_menu(this))' onmouseout='menu_leave()'>
89        <a href="/learning/tutorial.html">Tutorial</a>
90        <a href="/learning/getting_started.html">Getting Started</a>
91        <a href="/learning/tools.html">Tools</a>
92        <a href="/learning/community.html">Community</a>
93      </div>
94    </div>
95
96    <div class="header-element menu">
97      <div onclick='menu_open_popup(find_enclosing_menu(this))' onmouseover='menu_open_popup(find_enclosing_menu(this))' onmouseout='menu_leave()'>
98        Reference
99      </div>
100      <div class=pop-up onmouseover='menu_open_popup(find_enclosing_menu(this))' onmouseout='menu_leave()'>
101        <a href="/ref/language.html">Language Reference</a>
102        <a href="/ref/stdlib.html">Standard Library</a>
103        <a href="/ref/spec.html">Specification</a>
104        <a href="/ref/bindings.html">Bindings</a>
105      </div>
106    </div>
107
108    <div class="header-element menu">
109      <div onclick='find_enclosing_menu(menu_open_popup(this))' onmouseover='menu_open_popup(find_enclosing_menu(this))' onmouseout='menu_leave()'>
110        Articles
111      </div>
112      <div class=pop-up onmouseover='menu_open_popup(find_enclosing_menu(this))' onmouseout='menu_leave()'>
113        <a href="/articles/fractal.1.html">Terraform & Packer</a>
114        <a href="/articles/kubernetes.html">Kubernetes</a>
115        <a href="/articles/output-formats.html">Output Formats</a>
116        <a href="/articles/design.html">Design Rationale</a>
117        <a href="/articles/comparisons.html">Comparisons</a>
118      </div>
119    </div>
120    <a id=groupsmark class=header-element href="https://groups.google.com/g/jsonnet"></a>
121    <a id=githubmark class=header-element href="https://github.com/google/jsonnet"></a>
122    <a id=gomark class=header-element href="https://github.com/google/go-jsonnet"></a>
123  </div>
124</div>
125
126<div class="hgroup">
127  <div class="hgroup-inline">
128    <div class="panel">
129      <h1 id="tutorial">Tutorial</h1>
130    </div>
131    <div style="clear: both"></div>
132  </div>
133</div>
134
135<div class="hgroup">
136  <div class="hgroup-inline">
137    <div class="panel">
138      <p>
139        Let's explore the various features in Jsonnet, and mix some cocktails.  All example codes
140        can be edited, allowing you to experience the language interactively.  If you're looking at
141        Jsonnet for the first time, this is the right place to be.  For help using the
142        implementation, see <a href="getting_started.html">getting started</a>.  For a detailed
143        systematic overview of the language, check out the <a
144        href="/ref/language.html">reference</a>.
145      </p>
146    </div>
147    <div style="clear: both"></div>
148  </div>
149</div>
150
151<div class="hgroup">
152  <div class="hgroup-inline">
153    <div class="panel">
154      <h2 id="syntax">Syntax</h2>
155    </div>
156    <div style="clear: both"></div>
157  </div>
158</div>
159
160<div class="hgroup">
161  <div class="hgroup-inline">
162    <div class="panel">
163      <p>
164        Any <a href="https://json.org">JSON</a> document is a valid Jsonnet program, so we'll focus
165        on what Jsonnet adds to JSON.  Let's start with an example that does not involve any
166        computation but uses new syntax.
167      </p>
168      <ul>
169        <li>Fields that happen to be valid identifiers have no quotes</li>
170        <li><i>Trailing</i> commas at the end of arrays / objects</li>
171        <li>Comments</li>
172        <li>
173          String literals use <code>"</code> or <code>'</code>.  The single quote is easier on the
174          eyes but either can be used to avoid escaping the other, e.g. <code>"Farmer's Gin"</code>
175          instead of <code>'Farmer\'s Gin'</code>.
176        </li>
177        <li>
178          Text blocks <code>|||</code> allow verbatim text across multiple lines.
179        </li>
180        <li>
181          Verbatim strings <code>@'foo'</code> and <code>@"foo"</code> are for single lines.
182        </li>
183        <li>
184          Large numeric literals may be rendered more readable by using underscores, e.g. <code>1_000_000</code>.
185        </li>
186      </ul>
187      <p>
188        Using the interactive demo below, try modifying the strings / quantities.  Try adding a "Dry
189        Manhattan" which uses dry red vermouth and is garnished with a lemon slice.
190      </p>
191    </div>
192    <div style="clear: both"></div>
193  </div>
194</div>
195
196<div class="inverse hgroup">
197  <div class=hgroup-inline>
198    <div class="tab-window-input" id="syntax-input">
199      <div class="tab-header">
200      </div>
201      <textarea id=syntax-jsonnet>
202/* A C-style comment. */
203# A Python-style comment.
204{
205  cocktails: {
206    // Ingredient quantities are in fl oz.
207    'Tom Collins': {
208      ingredients: [
209        { kind: "Farmer's Gin", qty: 1.5 },
210        { kind: 'Lemon', qty: 1 },
211        { kind: 'Simple Syrup', qty: 0.5 },
212        { kind: 'Soda', qty: 2 },
213        { kind: 'Angostura', qty: 'dash' },
214      ],
215      garnish: 'Maraschino Cherry',
216      served: 'Tall',
217      description: |||
218        The Tom Collins is essentially gin and
219        lemonade.  The bitters add complexity.
220      |||,
221    },
222    Manhattan: {
223      ingredients: [
224        { kind: 'Rye', qty: 2.5 },
225        { kind: 'Sweet Red Vermouth', qty: 1 },
226        { kind: 'Angostura', qty: 'dash' },
227      ],
228      garnish: 'Maraschino Cherry',
229      served: 'Straight Up',
230      description: @'A clear \ red drink.',
231    },
232  },
233}
234
235      </textarea>
236    </div>
237    <div class="bigarrow">➡</div>
238    <div class="tab-window-output" id="syntax-output">
239      <div class="tab-header">
240        <div class=selected onclick="tab_click(this, 'syntax-json-output')">output.json</div>
241      </div>
242      <textarea readonly class="selected code-json" id="syntax-json-output">
243{
244  "cocktails": {
245    "Manhattan": {
246      "description": "A clear \\ red drink.",
247      "garnish": "Maraschino Cherry",
248      "ingredients": [
249        {
250          "kind": "Rye",
251          "qty": 2.5
252        },
253        {
254          "kind": "Sweet Red Vermouth",
255          "qty": 1
256        },
257        {
258          "kind": "Angostura",
259          "qty": "dash"
260        }
261      ],
262      "served": "Straight Up"
263    },
264    "Tom Collins": {
265      "description": "The Tom Collins is essentially gin and\nlemonade.  The bitters add complexity.\n",
266      "garnish": "Maraschino Cherry",
267      "ingredients": [
268        {
269          "kind": "Farmer's Gin",
270          "qty": 1.5
271        },
272        {
273          "kind": "Lemon",
274          "qty": 1
275        },
276        {
277          "kind": "Simple Syrup",
278          "qty": 0.5
279        },
280        {
281          "kind": "Soda",
282          "qty": 2
283        },
284        {
285          "kind": "Angostura",
286          "qty": "dash"
287        }
288      ],
289      "served": "Tall"
290    }
291  }
292}
293
294      </textarea>
295    </div>
296    
296<script>
297      demo(
298        'syntax-input',
299        {
300          'syntax-jsonnet': 'syntax.jsonnet',
301        },
302        'syntax.jsonnet',
303        'syntax-output',
304        false,
305        false
306      );
307    </script>
307
308    <div style="clear: both"></div>
309  </div>
310</div>
311
312
313
314<div class="hgroup">
315  <div class="hgroup-inline">
316    <div class="panel">
317      <h2 id="variables">Variables</h2>
318    </div>
319    <div style="clear: both"></div>
320  </div>
321</div>
322
323<div class="hgroup">
324  <div class="hgroup-inline">
325    <div class="panel">
326      <p>
327        Variables are the simplest way to avoid duplication.
328      </p>
329      <ul>
330        <li>The <code>local</code> keyword defines a variable.</li>
331        <li>Variables defined next to fields end with a comma (<code>,</code>).</li>
332        <li>All other cases end with a semicolon (<code>;</code>).</li>
333      </ul>
334      <p>
335        Try factoring out <code>"Simple Syrup"</code> to the top level or to the same level as
336        the <code>pour</code> variable.
337      </p>
338    </div>
339    <div style="clear: both"></div>
340  </div>
341</div>
342
343<div class="inverse hgroup">
344  <div class=hgroup-inline>
345    <div class="tab-window-input" id="variables-input">
346      <div class="tab-header">
347      </div>
348      <textarea id=variables-jsonnet>
349// A regular definition.
350local house_rum = 'Banks Rum';
351
352{
353  // A definition next to fields.
354  local pour = 1.5,
355
356  Daiquiri: {
357    ingredients: [
358      { kind: house_rum, qty: pour },
359      { kind: 'Lime', qty: 1 },
360      { kind: 'Simple Syrup', qty: 0.5 },
361    ],
362    served: 'Straight Up',
363  },
364  Mojito: {
365    ingredients: [
366      {
367        kind: 'Mint',
368        action: 'muddle',
369        qty: 6,
370        unit: 'leaves',
371      },
372      { kind: house_rum, qty: pour },
373      { kind: 'Lime', qty: 0.5 },
374      { kind: 'Simple Syrup', qty: 0.5 },
375      { kind: 'Soda', qty: 3 },
376    ],
377    garnish: 'Lime wedge',
378    served: 'Over crushed ice',
379  },
380}
381
382      </textarea>
383    </div>
384    <div class="bigarrow">➡</div>
385    <div class="tab-window-output" id="variables-output">
386      <div class="tab-header">
387        <div class=selected onclick="tab_click(this, 'variables-json-output')">output.json</div>
388      </div>
389      <textarea readonly class="selected code-json" id="variables-json-output">
390{
391  "Daiquiri": {
392    "ingredients": [
393      {
394        "kind": "Banks Rum",
395        "qty": 1.5
396      },
397      {
398        "kind": "Lime",
399        "qty": 1
400      },
401      {
402        "kind": "Simple Syrup",
403        "qty": 0.5
404      }
405    ],
406    "served": "Straight Up"
407  },
408  "Mojito": {
409    "garnish": "Lime wedge",
410    "ingredients": [
411      {
412        "action": "muddle",
413        "kind": "Mint",
414        "qty": 6,
415        "unit": "leaves"
416      },
417      {
418        "kind": "Banks Rum",
419        "qty": 1.5
420      },
421      {
422        "kind": "Lime",
423        "qty": 0.5
424      },
425      {
426        "kind": "Simple Syrup",
427        "qty": 0.5
428      },
429      {
430        "kind": "Soda",
431        "qty": 3
432      }
433    ],
434    "served": "Over crushed ice"
435  }
436}
437
438      </textarea>
439    </div>
440    
440<script>
441      demo(
442        'variables-input',
443        {
444          'variables-jsonnet': 'variables.jsonnet',
445        },
446        'variables.jsonnet',
447        'variables-output',
448        false,
449        false
450      );
451    </script>
451
452    <div style="clear: both"></div>
453  </div>
454</div>
455
456
457
458<div class="hgroup">
459  <div class="hgroup-inline">
460    <div class="panel">
461      <h2 id="references">References</h2>
462    </div>
463    <div style="clear: both"></div>
464  </div>
465</div>
466
467<div class="hgroup">
468  <div class="hgroup-inline">
469    <div class="panel">
470      <p>
471        Another way to avoid duplication is to refer to another part of the structure.
472      </p>
473      <ul>
474        <li><code>self</code> refers to the current object.</li>
475        <li><code>$</code> refers to the outer-most object.</li>
476        <li><code>['foo']</code> looks up a field.</li>
477        <li><code>.foo</code> can be used if the field name is an identifier.</li>
478        <li><code>[10]</code> looks up an array element.</li>
479        <li>Arbitrarily long paths are allowed.</li>
480        <li>Array slices like <code>arr[10:20:2]</code> are allowed, like in Python.</li>
481        <li>Strings can be looked up / sliced too, by unicode codepoint.</li>
482      </ul>
483      <p>
484      Another name for a Tom Collins is a Gin Fizz.  Try adding an alias for that.
485      </p>
486    </div>
487    <div style="clear: both"></div>
488  </div>
489</div>
490
491<div class="inverse hgroup">
492  <div class=hgroup-inline>
493    <div class="tab-window-input" id="references-input">
494      <div class="tab-header">
495      </div>
496      <textarea id=references-jsonnet>
497{
498  'Tom Collins': {
499    ingredients: [
500      { kind: "Farmer's Gin", qty: 1.5 },
501      { kind: 'Lemon', qty: 1 },
502      { kind: 'Simple Syrup', qty: 0.5 },
503      { kind: 'Soda', qty: 2 },
504      { kind: 'Angostura', qty: 'dash' },
505    ],
506    garnish: 'Maraschino Cherry',
507    served: 'Tall',
508  },
509  Martini: {
510    ingredients: [
511      {
512        // Use the same gin as the Tom Collins.
513        kind:
514          $['Tom Collins'].ingredients[0].kind,
515        qty: 2,
516      },
517      { kind: 'Dry White Vermouth', qty: 1 },
518    ],
519    garnish: 'Olive',
520    served: 'Straight Up',
521  },
522  // Create an alias.
523  'Gin Martini': self.Martini,
524}
525
526      </textarea>
527    </div>
528    <div class="bigarrow">➡</div>
529    <div class="tab-window-output" id="references-output">
530      <div class="tab-header">
531        <div class=selected onclick="tab_click(this, 'references-json-output')">output.json</div>
532      </div>
533      <textarea readonly class="selected code-json" id="references-json-output">
534{
535  "Gin Martini": {
536    "garnish": "Olive",
537    "ingredients": [
538      {
539        "kind": "Farmer's Gin",
540        "qty": 2
541      },
542      {
543        "kind": "Dry White Vermouth",
544        "qty": 1
545      }
546    ],
547    "served": "Straight Up"
548  },
549  "Martini": {
550    "garnish": "Olive",
551    "ingredients": [
552      {
553        "kind": "Farmer's Gin",
554        "qty": 2
555      },
556      {
557        "kind": "Dry White Vermouth",
558        "qty": 1
559      }
560    ],
561    "served": "Straight Up"
562  },
563  "Tom Collins": {
564    "garnish": "Maraschino Cherry",
565    "ingredients": [
566      {
567        "kind": "Farmer's Gin",
568        "qty": 1.5
569      },
570      {
571        "kind": "Lemon",
572        "qty": 1
573      },
574      {
575        "kind": "Simple Syrup",
576        "qty": 0.5
577      },
578      {
579        "kind": "Soda",
580        "qty": 2
581      },
582      {
583        "kind": "Angostura",
584        "qty": "dash"
585      }
586    ],
587    "served": "Tall"
588  }
589}
590
591      </textarea>
592    </div>
593    
593<script>
594      demo(
595        'references-input',
596        {
597          'references-jsonnet': 'references.jsonnet',
598        },
599        'references.jsonnet',
600        'references-output',
601        false,
602        false
603      );
604    </script>
604
605    <div style="clear: both"></div>
606  </div>
607</div>
608
609
610
611<div class="hgroup">
612  <div class="hgroup-inline">
613    <div class="panel">
614      <p>
615        In order to refer to objects between the current and outer-most object, we use a variable to
616        create a name for that level:
617      </p>
618    </div>
619    <div style="clear: both"></div>
620  </div>
621</div>
622
623<div class="inverse hgroup">
624  <div class=hgroup-inline>
625    <div class="tab-window-input" id="inner-reference-input">
626      <div class="tab-header">
627      </div>
628      <textarea id=inner-reference-jsonnet>
629{
630  Martini: {
631    local drink = self,
632    ingredients: [
633      { kind: "Farmer's Gin", qty: 1 },
634      {
635        kind: 'Dry White Vermouth',
636        qty: drink.ingredients[0].qty,
637      },
638    ],
639    garnish: 'Olive',
640    served: 'Straight Up',
641  },
642}
643
644      </textarea>
645    </div>
646    <div class="bigarrow">➡</div>
647    <div class="tab-window-output" id="inner-reference-output">
648      <div class="tab-header">
649        <div class=selected onclick="tab_click(this, 'inner-reference-json-output')">output.json</div>
650      </div>
651      <textarea readonly class="selected code-json" id="inner-reference-json-output">
652{
653  "Martini": {
654    "garnish": "Olive",
655    "ingredients": [
656      {
657        "kind": "Farmer's Gin",
658        "qty": 1
659      },
660      {
661        "kind": "Dry White Vermouth",
662        "qty": 1
663      }
664    ],
665    "served": "Straight Up"
666  }
667}
668
669      </textarea>
670    </div>
671    
671<script>
672      demo(
673        'inner-reference-input',
674        {
675          'inner-reference-jsonnet': 'inner-reference.jsonnet',
676        },
677        'inner-reference.jsonnet',
678        'inner-reference-output',
679        false,
680        false
681      );
682    </script>
682
683    <div style="clear: both"></div>
684  </div>
685</div>
686
687
688
689<div class="hgroup">
690  <div class="hgroup-inline">
691    <div class="panel">
692      <h2 id="arithmetic">Arithmetic</h2>
693    </div>
694    <div style="clear: both"></div>
695  </div>
696</div>
697
698<div class="hgroup">
699  <div class="hgroup-inline">
700    <div class="panel">
701      <p>
702        Arithmetic includes numeric operations like multiplication but also various operations on
703        other types.
704      </p>
705      <ul>
706        <li>Use floating point arithmetic, bitwise ops, boolean logic.</li>
707        <li>
708          Strings may be concatenated with <code>+</code>, which implicitly converts
709          one operand to string if needed.
710        </li>
711        <li>Two strings can be compared with <code>&lt;</code> (unicode codepoint order).</li>
712        <li>
713          Objects may be combined with <code>+</code> where the right-hand side wins field
714          conflicts.
715        </li>
716        <li>Test if a field is in an object with <code>in</code>.</li>
717        <li><code>==</code> is deep value equality.</li>
718        <li>Python-compatible string formatting is available via <code>%</code>.  When combined with
719        <code>|||</code> this can be used for templating text files.</li>
720      </ul>
721      <p>Try dividing by zero to see what happens.</p>
722    </div>
723    <div style="clear: both"></div>
724  </div>
725</div>
726
727<div class="inverse hgroup">
728  <div class=hgroup-inline>
729    <div class="tab-window-input" id="arith-input">
730      <div class="tab-header">
731      </div>
732      <textarea id=arith-jsonnet>
733{
734  concat_array: [1, 2, 3] + [4],
735  concat_string: '123' + 4,
736  equality1: 1 == '1',
737  equality2: [{}, { x: 3 - 1 }]
738             == [{}, { x: 2 }],
739  ex1: 1 + 2 * 3 / (4 + 5),
740  // Bitwise operations first cast to int.
741  ex2: self.ex1 | 3,
742  // Modulo operator.
743  ex3: self.ex1 % 2,
744  // Boolean logic
745  ex4: (4 > 3) && (1 <= 3) || false,
746  // Mixing objects together
747  obj: { a: 1, b: 2 } + { b: 3, c: 4 },
748  // Test if a field is in an object
749  obj_member: 'foo' in { foo: 1 },
750  // String formatting
751  str1: 'The value of self.ex2 is '
752        + self.ex2 + '.',
753  str2: 'The value of self.ex2 is %g.'
754        % self.ex2,
755  str3: 'ex1=%0.2f, ex2=%0.2f'
756        % [self.ex1, self.ex2],
757  // By passing self, we allow ex1 and ex2 to
758  // be extracted internally.
759  str4: 'ex1=%(ex1)0.2f, ex2=%(ex2)0.2f'
760        % self,
761  // Do textual templating of entire files:
762  str5: |||
763    ex1=%(ex1)0.2f
764    ex2=%(ex2)0.2f
765  ||| % self,
766}
767
768      </textarea>
769    </div>
770    <div class="bigarrow">➡</div>
771    <div class="tab-window-output" id="arith-output">
772      <div class="tab-header">
773        <div class=selected onclick="tab_click(this, 'arith-json-output')">output.json</div>
774      </div>
775      <textarea readonly class="selected code-json" id="arith-json-output">
776{
777  "concat_array": [
778    1,
779    2,
780    3,
781    4
782  ],
783  "concat_string": "1234",
784  "equality1": false,
785  "equality2": true,
786  "ex1": 1.6666666666666665,
787  "ex2": 3,
788  "ex3": 1.6666666666666665,
789  "ex4": true,
790  "obj": {
791    "a": 1,
792    "b": 3,
793    "c": 4
794  },
795  "obj_member": true,
796  "str1": "The value of self.ex2 is 3.",
797  "str2": "The value of self.ex2 is 3.",
798  "str3": "ex1=1.67, ex2=3.00",
799  "str4": "ex1=1.67, ex2=3.00",
800  "str5": "ex1=1.67\nex2=3.00\n"
801}
802
803      </textarea>
804    </div>
805    
805<script>
806      demo(
807        'arith-input',
808        {
809          'arith-jsonnet': 'arith.jsonnet',
810        },
811        'arith.jsonnet',
812        'arith-output',
813        false,
814        false
815      );
816    </script>
816
817    <div style="clear: both"></div>
818  </div>
819</div>
820
821
822
823<div class="hgroup">
824  <div class="hgroup-inline">
825    <div class="panel">
826      <h2 id="functions">Functions</h2>
827    </div>
828    <div style="clear: both"></div>
829  </div>
830</div>
831
832<div class="hgroup">
833  <div class="hgroup-inline">
834    <div class="panel">
835      <p>
836        Like Python, functions have positional parameters, named parameters, and default arguments.
837        Closures are also supported.  The examples below should demonstrate the syntax.  Many
838        functions are already defined in the <a href=/ref/stdlib.html>standard library</a>.
839      </p>
840      <p>
841        Try writing a function <code>is_even</code> that uses the modulo operator <code>%</code> and
842        returns either true or false.
843      </p>
844    </div>
845    <div style="clear: both"></div>
846  </div>
847</div>
848
849<div class="inverse hgroup">
850  <div class=hgroup-inline>
851    <div class="tab-window-input" id="functions-input">
852      <div class="tab-header">
853      </div>
854      <textarea id=functions-jsonnet>
855// Define a local function.
856// Default arguments are like Python:
857local my_function(x, y=10) = x + y;
858
859// Define a local multiline function.
860local multiline_function(x) =
861  // One can nest locals.
862  local temp = x * 2;
863  // Every local ends with a semi-colon.
864  [temp, temp + 1];
865
866local object = {
867  // A method
868  my_method(x): x * x,
869};
870
871{
872  // Functions are first class citizens.
873  call_inline_function:
874    (function(x) x * x)(5),
875
876  call_multiline_function: multiline_function(4),
877
878  // Using the variable fetches the function,
879  // the parens call the function.
880  call: my_function(2),
881
882  // Like python, parameters can be named at
883  // call time.
884  named_params: my_function(x=2),
885  // This allows changing their order
886  named_params2: my_function(y=3, x=2),
887
888  // object.my_method returns the function,
889  // which is then called like any other.
890  call_method1: object.my_method(3),
891
892  standard_lib:
893    std.join(' ', std.split('foo/bar', '/')),
894  len: [
895    std.length('hello'),
896    std.length([1, 2, 3]),
897  ],
898}
899
900      </textarea>
901    </div>
902    <div class="bigarrow">➡</div>
903    <div class="tab-window-output" id="functions-output">
904      <div class="tab-header">
905        <div class=selected onclick="tab_click(this, 'functions-json-output')">output.json</div>
906      </div>
907      <textarea readonly class="selected code-json" id="functions-json-output">
908{
909  "call": 12,
910  "call_inline_function": 25,
911  "call_method1": 9,
912  "call_multiline_function": [8, 9],
913  "len": [
914    5,
915    3
916  ],
917  "named_params": 12,
918  "named_params2": 5,
919  "standard_lib": "foo bar"
920}
921
922      </textarea>
923    </div>
924    
924<script>
925      demo(
926        'functions-input',
927        {
928          'functions-jsonnet': 'functions.jsonnet',
929        },
930        'functions.jsonnet',
931        'functions-output',
932        false,
933        false
934      );
935    </script>
935
936    <div style="clear: both"></div>
937  </div>
938</div>
939
940
941
942<div class="hgroup">
943  <div class="hgroup-inline">
944    <div class="panel">
945      <p>
946        And here's an example with cocktails.
947      </p>
948    </div>
949    <div style="clear: both"></div>
950  </div>
951</div>
952
953<div class="inverse hgroup">
954  <div class=hgroup-inline>
955    <div class="tab-window-input" id="sours-input">
956      <div class="tab-header">
957      </div>
958      <textarea id=sours-jsonnet>
959// This function returns an object. Although
960// the braces look like Java or C++ they do
961// not mean a statement block, they are instead
962// the value being returned.
963local Sour(spirit, garnish='Lemon twist') = {
964  ingredients: [
965    { kind: spirit, qty: 2 },
966    { kind: 'Egg white', qty: 1 },
967    { kind: 'Lemon Juice', qty: 1 },
968    { kind: 'Simple Syrup', qty: 1 },
969  ],
970  garnish: garnish,
971  served: 'Straight Up',
972};
973
974{
975  'Whiskey Sour': Sour('Bulleit Bourbon',
976                       'Orange bitters'),
977  'Pisco Sour': Sour('Machu Pisco',
978                     'Angostura bitters'),
979}
980
981      </textarea>
982    </div>
983    <div class="bigarrow">➡</div>
984    <div class="tab-window-output" id="sours-output">
985      <div class="tab-header">
986        <div class=selected onclick="tab_click(this, 'sours-json-output')">output.json</div>
987      </div>
988      <textarea readonly class="selected code-json" id="sours-json-output">
989{
990  "Pisco Sour": {
991    "garnish": "Angostura bitters",
992    "ingredients": [
993      {
994        "kind": "Machu Pisco",
995        "qty": 2
996      },
997      {
998        "kind": "Egg white",
999        "qty": 1
1000      },
1001      {
1002        "kind": "Lemon Juice",
1003        "qty": 1
1004      },
1005      {
1006        "kind": "Simple Syrup",
1007        "qty": 1
1008      }
1009    ],
1010    "served": "Straight Up"
1011  },
1012  "Whiskey Sour": {
1013    "garnish": "Orange bitters",
1014    "ingredients": [
1015      {
1016        "kind": "Bulleit Bourbon",
1017        "qty": 2
1018      },
1019      {
1020        "kind": "Egg white",
1021        "qty": 1
1022      },
1023      {
1024        "kind": "Lemon Juice",
1025        "qty": 1
1026      },
1027      {
1028        "kind": "Simple Syrup",
1029        "qty": 1
1030      }
1031    ],
1032    "served": "Straight Up"
1033  }
1034}
1035
1036      </textarea>
1037    </div>
1038    
1038<script>
1039      demo(
1040        'sours-input',
1041        {
1042          'sours-jsonnet': 'sours.jsonnet',
1043        },
1044        'sours.jsonnet',
1045        'sours-output',
1046        false,
1047        false
1048      );
1049    </script>
1049
1050    <div style="clear: both"></div>
1051  </div>
1052</div>
1053
1054
1055
1056<div class="hgroup">
1057  <div class="hgroup-inline">
1058    <div class="panel">
1059      <h2 id="conditionals">Conditionals</h2>
1060    </div>
1061    <div style="clear: both"></div>
1062  </div>
1063</div>
1064
1065<div class="hgroup">
1066  <div class="hgroup-inline">
1067    <div class="panel">
1068      <p>
1069        Conditional expressions look like <code>if b then e else e</code>. The
1070        <code>else</code> branch is optional and defaults to <code>null</code>.
1071      </p>
1072      <p>
1073        Try adding the missing Large Virgin Mojito:
1074      </p>
1075    </div>
1076    <div style="clear: both"></div>
1077  </div>
1078</div>
1079
1080<div class="inverse hgroup">
1081  <div class=hgroup-inline>
1082    <div class="tab-window-input" id="conditionals-input">
1083      <div class="tab-header">
1084      </div>
1085      <textarea id=conditionals-jsonnet>
1086local Mojito(virgin=false, large=false) = {
1087  // A local next to fields ends with ','.
1088  local factor = if large then 2 else 1,
1089  // The ingredients are split into 3 arrays,
1090  // the middle one is either length 1 or 0.
1091  ingredients: [
1092    {
1093      kind: 'Mint',
1094      action: 'muddle',
1095      qty: 6 * factor,
1096      unit: 'leaves',
1097    },
1098  ] + (
1099    if virgin then [] else [
1100      { kind: 'Banks', qty: 1.5 * factor },
1101    ]
1102  ) + [
1103    { kind: 'Lime', qty: 0.5 * factor },
1104    { kind: 'Simple Syrup', qty: 0.5 * factor },
1105    { kind: 'Soda', qty: 3 * factor },
1106  ],
1107  // Returns null if not large.
1108  garnish: if large then 'Lime wedge',
1109  served: 'Over crushed ice',
1110};
1111
1112{
1113  Mojito: Mojito(),
1114  'Virgin Mojito': Mojito(virgin=true),
1115  'Large Mojito': Mojito(large=true),
1116}
1117
1118      </textarea>
1119    </div>
1120    <div class="bigarrow">➡</div>
1121    <div class="tab-window-output" id="conditionals-output">
1122      <div class="tab-header">
1123        <div class=selected onclick="tab_click(this, 'conditionals-json-output')">output.json</div>
1124      </div>
1125      <textarea readonly class="selected code-json" id="conditionals-json-output">
1126{
1127  "Large Mojito": {
1128    "garnish": "Lime wedge",
1129    "ingredients": [
1130      {
1131        "action": "muddle",
1132        "kind": "Mint",
1133        "qty": 12,
1134        "unit": "leaves"
1135      },
1136      {
1137        "kind": "Banks",
1138        "qty": 3
1139      },
1140      {
1141        "kind": "Lime",
1142        "qty": 1
1143      },
1144      {
1145        "kind": "Simple Syrup",
1146        "qty": 1
1147      },
1148      {
1149        "kind": "Soda",
1150        "qty": 6
1151      }
1152    ],
1153    "served": "Over crushed ice"
1154  },
1155  "Mojito": {
1156    "garnish": null,
1157    "ingredients": [
1158      {
1159        "action": "muddle",
1160        "kind": "Mint",
1161        "qty": 6,
1162        "unit": "leaves"
1163      },
1164      {
1165        "kind": "Banks",
1166        "qty": 1.5
1167      },
1168      {
1169        "kind": "Lime",
1170        "qty": 0.5
1171      },
1172      {
1173        "kind": "Simple Syrup",
1174        "qty": 0.5
1175      },
1176      {
1177        "kind": "Soda",
1178        "qty": 3
1179      }
1180    ],
1181    "served": "Over crushed ice"
1182  },
1183  "Virgin Mojito": {
1184    "garnish": null,
1185    "ingredients": [
1186      {
1187        "action": "muddle",
1188        "kind": "Mint",
1189        "qty": 6,
1190        "unit": "leaves"
1191      },
1192      {
1193         "kind": "Lime",
1194         "qty": 0.5
1195      },
1196      {
1197         "kind": "Simple Syrup",
1198         "qty": 0.5
1199      },
1200      {
1201         "kind": "Soda",
1202         "qty": 3
1203      }
1204    ],
1205    "served": "Over crushed ice"
1206  }
1207}
1208
1209      </textarea>
1210    </div>
1211    
1211<script>
1212      demo(
1213        'conditionals-input',
1214        {
1215          'conditionals-jsonnet': 'conditionals.jsonnet',
1216        },
1217        'conditionals.jsonnet',
1218        'conditionals-output',
1219        false,
1220        false
1221      );
1222    </script>
1222
1223    <div style="clear: both"></div>
1224  </div>
1225</div>
1226
1227
1228
1229<div class="hgroup">
1230  <div class="hgroup-inline">
1231    <div class="panel">
1232      <h2 id="computed_field_names">Computed Field Names</h2>
1233    </div>
1234    <div style="clear: both"></div>
1235  </div>
1236</div>
1237
1238<div class="hgroup">
1239  <div class="hgroup-inline">
1240    <div class="panel">
1241      <p>
1242        Jsonnet objects can be used like a <code>std::map</code> or similar datastructures from
1243        regular languages.
1244      </p>
1245      <ul>
1246        <li>Recall that a field lookup can be computed with <code>obj[e]</code></li>
1247        <li>The definition equivalent is <code>{[e]: </code>... <code>}</code></li>
1248        <li>
1249          <code>self</code> or object locals cannot be accessed when field names are being computed,
1250          since the object is not yet constructed.
1251        </li>
1252        <li>
1253          If a field name evaluates to <code>null</code> during object construction, the field is
1254          omitted.  This works nicely with the default false branch of a conditional (see below).
1255        </li>
1256      </ul>
1257      <p>
1258        Try adding <code>[self.f]: 'f'</code> to see what happens, while pondering what that
1259        could even mean!
1260      </p>
1261    </div>
1262    <div style="clear: both"></div>
1263  </div>
1264</div>
1265
1266<div class="inverse hgroup">
1267  <div class=hgroup-inline>
1268    <div class="tab-window-input" id="computed-fields-input">
1269      <div class="tab-header">
1270      </div>
1271      <textarea id=computed-fields-jsonnet>
1272local Margarita(salted) = {
1273  ingredients: [
1274    { kind: 'Tequila Blanco', qty: 2 },
1275    { kind: 'Lime', qty: 1 },
1276    { kind: 'Cointreau', qty: 1 },
1277  ],
1278  [if salted then 'garnish']: 'Salt',
1279};
1280{
1281  Margarita: Margarita(true),
1282  'Margarita Unsalted': Margarita(false),
1283}
1284
1285      </textarea>
1286    </div>
1287    <div class="bigarrow">➡</div>
1288    <div class="tab-window-output" id="computed-fields-output">
1289      <div class="tab-header">
1290        <div class=selected onclick="tab_click(this, 'computed-fields-json-output')">output.json</div>
1291      </div>
1292      <textarea readonly class="selected code-json" id="computed-fields-json-output">
1293{
1294  "Margarita": {
1295    "garnish": "Salt",
1296    "ingredients": [
1297      {
1298        "kind": "Tequila Blanco",
1299        "qty": 2
1300      },
1301      {
1302        "kind": "Lime",
1303        "qty": 1
1304      },
1305      {
1306        "kind": "Cointreau",
1307        "qty": 1
1308      }
1309    ]
1310  },
1311  "Margarita Unsalted": {
1312    "ingredients": [
1313      {
1314        "kind": "Tequila Blanco",
1315        "qty": 2
1316      },
1317      {
1318        "kind": "Lime",
1319        "qty": 1
1320      },
1321      {
1322        "kind": "Cointreau",
1323        "qty": 1
1324      }
1325    ]
1326  }
1327}
1328
1329      </textarea>
1330    </div>
1331    
1331<script>
1332      demo(
1333        'computed-fields-input',
1334        {
1335          'computed-fields-jsonnet': 'computed-fields.jsonnet',
1336        },
1337        'computed-fields.jsonnet',
1338        'computed-fields-output',
1339        false,
1340        false
1341      );
1342    </script>
1342
1343    <div style="clear: both"></div>
1344  </div>
1345</div>
1346
1347
1348
1349<div class="hgroup">
1350  <div class="hgroup-inline">
1351    <div class="panel">
1352      <h2 id="comprehension">Array and Object Comprehension</h2>
1353    </div>
1354    <div style="clear: both"></div>
1355  </div>
1356</div>
1357
1358<div class="hgroup">
1359  <div class="hgroup-inline">
1360    <div class="panel">
1361      <p>
1362        What if you want to make an array or object and you don't know how many elements / fields
1363        they will have at run-time?  Jsonnet has Python-style array and object comprehension
1364        constructs to allow this.
1365      </p>
1366      <ul>
1367        <li>Any nesting of <code>for</code> and <code>if</code> can be used.</li>
1368        <li>The nest behaves like a loop nest, although the body is written first.</li>
1369      </ul>
1370    </div>
1371    <div style="clear: both"></div>
1372  </div>
1373</div>
1374
1375<div class="inverse hgroup">
1376  <div class=hgroup-inline>
1377    <div class="tab-window-input" id="comprehensions-input">
1378      <div class="tab-header">
1379      </div>
1380      <textarea id=comprehensions-jsonnet>
1381local arr = std.range(5, 8);
1382{
1383  array_comprehensions: {
1384    higher: [x + 3 for x in arr],
1385    lower: [x - 3 for x in arr],
1386    evens: [x for x in arr if x % 2 == 0],
1387    odds: [x for x in arr if x % 2 == 1],
1388    evens_and_odds: [
1389      '%d-%d' % [x, y]
1390      for x in arr
1391      if x % 2 == 0
1392      for y in arr
1393      if y % 2 == 1
1394    ],
1395  },
1396  object_comprehensions: {
1397    evens: {
1398      ['f' + x]: true
1399      for x in arr
1400      if x % 2 == 0
1401    },
1402    // Use object composition (+) to add in
1403    // static fields:
1404    mixture: {
1405      f: 1,
1406      g: 2,
1407    } + {
1408      [x]: 0
1409      for x in ['a', 'b', 'c']
1410    },
1411  },
1412}
1413
1414      </textarea>
1415    </div>
1416    <div class="bigarrow">➡</div>
1417    <div class="tab-window-output" id="comprehensions-output">
1418      <div class="tab-header">
1419        <div class=selected onclick="tab_click(this, 'comprehensions-json-output')">output.json</div>
1420      </div>
1421      <textarea readonly class="selected code-json" id="comprehensions-json-output">
1422{
1423  "array_comprehensions": {
1424    "evens": [
1425      6,
1426      8
1427    ],
1428    "evens_and_odds": [
1429      "6-5",
1430      "6-7",
1431      "8-5",
1432      "8-7"
1433    ],
1434    "higher": [
1435      8,
1436      9,
1437      10,
1438      11
1439    ],
1440    "lower": [
1441      2,
1442      3,
1443      4,
1444      5
1445    ],
1446    "odds": [
1447      5,
1448      7
1449    ]
1450  },
1451  "object_comprehensions": {
1452    "evens": {
1453      "f6": true,
1454      "f8": true
1455    },
1456    "mixture": {
1457      "a": 0,
1458      "b": 0,
1459      "c": 0,
1460      "f": 1,
1461      "g": 2
1462    }
1463  }
1464}
1465
1466      </textarea>
1467    </div>
1468    
1468<script>
1469      demo(
1470        'comprehensions-input',
1471        {
1472          'comprehensions-jsonnet': 'comprehensions.jsonnet',
1473        },
1474        'comprehensions.jsonnet',
1475        'comprehensions-output',
1476        false,
1477        false
1478      );
1479    </script>
1479
1480    <div style="clear: both"></div>
1481  </div>
1482</div>
1483
1484
1485
1486<div class="hgroup">
1487  <div class="hgroup-inline">
1488    <div class="panel">
1489      <p>
1490        Below, a less contrived example.  The first cocktail has equal parts of three ingredients.
1491        The second iterates over an array of records - name prefix and fruit juice.
1492      </p>
1493      <p>
1494        Try adding a "Red Screwdriver" with cranberry juice.
1495      </p>
1496    </div>
1497    <div style="clear: both"></div>
1498  </div>
1499</div>
1500
1501<div class="inverse hgroup">
1502  <div class=hgroup-inline>
1503    <div class="tab-window-input" id="cocktail-comprehensions-input">
1504      <div class="tab-header">
1505      </div>
1506      <textarea id=cocktail-comprehensions-jsonnet>
1507{
1508  cocktails: {
1509    "Bee's Knees": {
1510      // Construct the ingredients by using
1511      // 4/3 oz of each element in the given
1512      // list.
1513      ingredients: [  // Array comprehension.
1514        { kind: kind, qty: 4 / 3 }
1515        for kind in [
1516          'Honey Syrup',
1517          'Lemon Juice',
1518          'Farmers Gin',
1519        ]
1520      ],
1521      garnish: 'Lemon Twist',
1522      served: 'Straight Up',
1523    },
1524  } + {  // Object comprehension.
1525    [sd.name + 'Screwdriver']: {
1526      ingredients: [
1527        { kind: 'Vodka', qty: 1.5 },
1528        { kind: sd.fruit, qty: 3 },
1529      ],
1530      served: 'On The Rocks',
1531    }
1532    for sd in [
1533      { name: 'Yellow ', fruit: 'Lemonade' },
1534      { name: '', fruit: 'Orange Juice' },
1535    ]
1536  },
1537}
1538
1539      </textarea>
1540    </div>
1541    <div class="bigarrow">➡</div>
1542    <div class="tab-window-output" id="cocktail-comprehensions-output">
1543      <div class="tab-header">
1544        <div class=selected onclick="tab_click(this, 'cocktail-comprehensions-json-output')">output.json</div>
1545      </div>
1546      <textarea readonly class="selected code-json" id="cocktail-comprehensions-json-output">
1547{
1548  "cocktails": {
1549    "Bee's Knees": {
1550      "garnish": "Lemon Twist",
1551      "ingredients": [
1552        {
1553          "kind": "Honey Syrup",
1554          "qty": 1.3333333333333333
1555        },
1556        {
1557          "kind": "Lemon Juice",
1558          "qty": 1.3333333333333333
1559        },
1560        {
1561          "kind": "Farmers Gin",
1562          "qty": 1.3333333333333333
1563        }
1564      ],
1565      "served": "Straight Up"
1566    },
1567    "Screwdriver": {
1568      "ingredients": [
1569        {
1570          "kind": "Vodka",
1571          "qty": 1.5
1572        },
1573        {
1574          "kind": "Orange Juice",
1575          "qty": 3
1576        }
1577      ],
1578      "served": "On The Rocks"
1579    },
1580    "Yellow Screwdriver": {
1581      "ingredients": [
1582        {
1583          "kind": "Vodka",
1584          "qty": 1.5
1585        },
1586        {
1587          "kind": "Lemonade",
1588          "qty": 3
1589        }
1590      ],
1591      "served": "On The Rocks"
1592    }
1593  }
1594}
1595
1596      </textarea>
1597    </div>
1598    
1598<script>
1599      demo(
1600        'cocktail-comprehensions-input',
1601        {
1602          'cocktail-comprehensions-jsonnet': 'cocktail-comprehensions.jsonnet',
1603        },
1604        'cocktail-comprehensions.jsonnet',
1605        'cocktail-comprehensions-output',
1606        false,
1607        false
1608      );
1609    </script>
1609
1610    <div style="clear: both"></div>
1611  </div>
1612</div>
1613
1614
1615
1616<div class="hgroup">
1617  <div class="hgroup-inline">
1618    <div class="panel">
1619      <h2 id="imports">Imports</h2>
1620    </div>
1621    <div style="clear: both"></div>
1622  </div>
1623</div>
1624
1625<div class="hgroup">
1626  <div class="hgroup-inline">
1627    <div class="panel">
1628      <p>
1629        It's possible to import both code and raw data from other files.
1630      </p>
1631      <ul>
1632        <li>The <code>import</code> construct is like copy/pasting Jsonnet code.</li>
1633        <li>Files designed for import by convention end with <tt>.libsonnet</tt></li>
1634        <li>Raw JSON can be imported this way too.</li>
1635        <li>The <code>importstr</code> construct is for verbatim UTF-8 text.</li>
1636        <li>The <code>importbin</code> construct is for verbatim binary data.</li>
1637      </ul>
1638      <p>
1639        Usually, imported Jsonnet content is stashed in a top-level local variable.  This
1640        resembles the way other programming languages handle modules.  Jsonnet libraries typically
1641        return an object, so that they can easily be extended.  Neither of these conventions are
1642        enforced.
1643      </p>
1644      <p>
1645        Try including the Cosmopolitan from the library.
1646      </p>
1647    </div>
1648    <div style="clear: both"></div>
1649  </div>
1650</div>
1651
1652<div class="inverse hgroup">
1653  <div class=hgroup-inline>
1654    <div class="tab-window-input" id="imports-input">
1655      <div class="tab-header">
1656      </div>
1657      <textarea id=imports-jsonnet>
1658local martinis = import 'martinis.libsonnet';
1659
1660{
1661  'Vodka Martini': martinis['Vodka Martini'],
1662  Manhattan: {
1663    ingredients: [
1664      { kind: 'Rye', qty: 2.5 },
1665      { kind: 'Sweet Red Vermouth', qty: 1 },
1666      { kind: 'Angostura', qty: 'dash' },
1667    ],
1668    garnish: importstr 'garnish.txt',
1669    served: 'Straight Up',
1670  },
1671}
1672
1673      </textarea>
1674      <textarea id=martinis-libsonnet>
1675{
1676  'Vodka Martini': {
1677    ingredients: [
1678      { kind: 'Vodka', qty: 2 },
1679      { kind: 'Dry White Vermouth', qty: 1 },
1680    ],
1681    garnish: 'Olive',
1682    served: 'Straight Up',
1683  },
1684  Cosmopolitan: {
1685    ingredients: [
1686      { kind: 'Vodka', qty: 2 },
1687      { kind: 'Triple Sec', qty: 0.5 },
1688      { kind: 'Cranberry Juice', qty: 0.75 },
1689      { kind: 'Lime Juice', qty: 0.5 },
1690    ],
1691    garnish: 'Orange Peel',
1692    served: 'Straight Up',
1693  },
1694}
1695
1696      </textarea>
1697      <textarea id=garnish-txt>
1698Maraschino Cherry
1699      </textarea>
1700    </div>
1701    <div class="bigarrow">➡</div>
1702    <div class="tab-window-output" id="imports-output">
1703      <div class="tab-header">
1704        <div class=selected onclick="tab_output_click(this, 'imports-json-output')">output.json</div>
1705      </div>
1706      <textarea readonly class="selected code-json" id="imports-json-output">
1707{
1708  "Manhattan": {
1709    "garnish": "Maraschino Cherry",
1710    "ingredients": [
1711      {
1712        "kind": "Rye",
1713        "qty": 2.5
1714      },
1715      {
1716        "kind": "Sweet Red Vermouth",
1717        "qty": 1
1718      },
1719      {
1720        "kind": "Angostura",
1721        "qty": "dash"
1722      }
1723    ],
1724    "served": "Straight Up"
1725  },
1726  "Vodka Martini": {
1727    "garnish": "Olive",
1728    "ingredients": [
1729      {
1730        "kind": "Vodka",
1731        "qty": 2
1732      },
1733      {
1734        "kind": "Dry White Vermouth",
1735        "qty": 1
1736      }
1737    ],
1738    "served": "Straight Up"
1739  }
1740}
1741
1742      </textarea>
1743    </div>
1744    
1744<script>
1745      demo(
1746        'imports-input',
1747        {
1748          'imports-jsonnet': 'imports.jsonnet',
1749          'martinis-libsonnet': 'martinis.libsonnet',
1750          'garnish-txt': 'garnish.txt',
1751        },
1752        'imports.jsonnet',
1753        'imports-output',
1754        false,
1755        false
1756      );
1757    </script>
1757
1758    <div style="clear: both"></div>
1759  </div>
1760</div>
1761
1762<div class="hgroup">
1763  <div class="hgroup-inline">
1764    <div class="panel">
1765      <p>
1766        The following example shows how to write libraries of functions.  It also shows how to
1767        define a local variable in the scope of a function.  Try adding the Bee's Knees from above
1768        using the utility library.
1769      </p>
1770    </div>
1771    <div style="clear: both"></div>
1772  </div>
1773</div>
1774
1775<div class="inverse hgroup">
1776  <div class=hgroup-inline>
1777    <div class="tab-window-input" id="negroni-input">
1778      <div class="tab-header">
1779      </div>
1780      <textarea id=negroni-jsonnet>
1781local utils = import 'utils.libsonnet';
1782{
1783  Negroni: {
1784    // Divide 3oz among the 3 ingredients.
1785    ingredients: utils.equal_parts(3, [
1786      'Farmers Gin',
1787      'Sweet Red Vermouth',
1788      'Campari',
1789    ]),
1790    garnish: 'Orange Peel',
1791    served: 'On The Rocks',
1792  },
1793}
1794
1795      </textarea>
1796      <textarea id=utils-libsonnet>
1797{
1798  equal_parts(size, ingredients)::
1799    // Define a function-scoped variable.
1800    local qty = size / std.length(ingredients);
1801    // Return an array.
1802    [
1803      { kind: i, qty: qty }
1804      for i in ingredients
1805    ],
1806}
1807
1808      </textarea>
1809    </div>
1810    <div class="bigarrow">➡</div>
1811    <div class="tab-window-output" id="negroni-output">
1812      <div class="tab-header">
1813        <div class=selected onclick="tab_output_click(this, 'negroni-json-output')">output.json</div>
1814      </div>
1815      <textarea readonly class="selected code-json" id="negroni-json-output">
1816{
1817  "Negroni": {
1818    "garnish": "Orange Peel",
1819    "ingredients": [
1820      {
1821        "kind": "Farmers Gin",
1822        "qty": 1
1823      },
1824      {
1825        "kind": "Sweet Red Vermouth",
1826        "qty": 1
1827      },
1828      {
1829        "kind": "Campari",
1830        "qty": 1
1831      }
1832    ],
1833    "served": "On The Rocks"
1834  }
1835}
1836
1837      </textarea>
1838    </div>
1839    
1839<script>
1840      demo(
1841        'negroni-input',
1842        {
1843          'negroni-jsonnet': 'negroni.jsonnet',
1844          'utils-libsonnet': 'utils.libsonnet',
1845        },
1846        'negroni.jsonnet',
1847        'negroni-output',
1848        false,
1849        false
1850      );
1851    </script>
1851
1852    <div style="clear: both"></div>
1853  </div>
1854</div>
1855
1856<div class="hgroup">
1857  <div class="hgroup-inline">
1858    <div class="panel">
1859      <h2 id="errors">Errors</h2>
1860    </div>
1861    <div style="clear: both"></div>
1862  </div>
1863</div>
1864
1865<div class="hgroup">
1866  <div class="hgroup-inline">
1867    <div class="panel">
1868      <p>
1869        Errors can arise from the language itself (e.g. an array overrun) or thrown from Jsonnet
1870        code.  Stack traces provide context for the error.
1871      </p>
1872      <ul>
1873        <li>To raise an error: <code>error "foo"</code></li>
1874        <li>To assert a condition before an expression: <code>assert "foo";</code></li>
1875        <li>A custom failure message: <code>assert "foo" : "message";</code></li>
1876        <li>Assert fields have a property: <code>assert self.f == 10,</code></li>
1877        <li>With custom failure message: <code>assert self.f == 10 : "message",</code></li>
1878      </ul>
1879      <p>
1880        Try modifying the code below to trigger the assertion failures, and observe the error
1881        messages and stack traces that result.
1882      </p>
1883      <p>
1884        You might wonder why the divide-by-zero is not triggered in the equal_parts function, since
1885        that code occurs before the <code>std.length()</code> check.  Jsonnet is a lazy
1886        language, so variable initializers are not evaluated until the variable is used.  Evaluation
1887        order is very hard to notice.  It only becomes relevant when code throws errors, or takes a
1888        long time to execute.  For more discussion of laziness, see <a
1889        href=/articles/design.html>design rationale</a>.
1890      </p>
1891    </div>
1892    <div style="clear: both"></div>
1893  </div>
1894</div>
1895
1896<div class="inverse hgroup">
1897  <div class=hgroup-inline>
1898    <div class="tab-window-input" id="error-examples-input">
1899      <div class="tab-header">
1900      </div>
1901      <textarea id=error-examples-jsonnet>
1902// Extend above example to sanity check input.
1903local equal_parts(size, ingredients) =
1904  local qty = size / std.length(ingredients);
1905  // Check a pre-condition
1906  if std.length(ingredients) == 0 then
1907    error 'Empty ingredients.'
1908  else [
1909    { kind: i, qty: qty }
1910    for i in ingredients
1911  ];
1912
1913local subtract(a, b) =
1914  assert a > b : 'a must be bigger than b';
1915  a - b;
1916
1917assert std.isFunction(subtract);
1918
1919{
1920  test1: equal_parts(1, ['Whiskey']),
1921  test2: subtract(10, 3),
1922  object: {
1923    assert self.f < self.g : 'wat',
1924    f: 1,
1925    g: 2,
1926  },
1927  assert std.isObject(self.object),
1928}
1929
1930      </textarea>
1931    </div>
1932    <div class="bigarrow">➡</div>
1933    <div class="tab-window-output" id="error-examples-output">
1934      <div class="tab-header">
1935        <div class=selected onclick="tab_click(this, 'error-examples-json-output')">output.json</div>
1936      </div>
1937      <textarea readonly class="selected code-json" id="error-examples-json-output">
1938{
1939  "object": {
1940    "f": 1,
1941    "g": 2
1942  },
1943  "test1": [
1944    {
1945      "kind": "Whiskey",
1946      "qty": 1
1947    }
1948  ],
1949  "test2": 7
1950}
1951
1952      </textarea>
1953    </div>
1954    
1954<script>
1955      demo(
1956        'error-examples-input',
1957        {
1958          'error-examples-jsonnet': 'error-examples.jsonnet',
1959        },
1960        'error-examples.jsonnet',
1961        'error-examples-output',
1962        false,
1963        false
1964      );
1965    </script>
1965
1966    <div style="clear: both"></div>
1967  </div>
1968</div>
1969
1970
1971
1972<div class="hgroup">
1973  <div class="hgroup-inline">
1974    <div class="panel">
1975      <h2 id="parameterize-entire-config">Parameterize Entire Config</h2>
1976    </div>
1977    <div style="clear: both"></div>
1978  </div>
1979</div>
1980
1981<div class="hgroup">
1982  <div class="hgroup-inline">
1983    <div class="panel">
1984      <p>
1985        Jsonnet is hermetic: It always generates the same data no matter the execution environment.
1986        This is an important property but there are times when you want a few carefully chosen
1987        parameters at the top level.  There are two ways to do this, with different properties.
1988      </p>
1989      <ul>
1990        <li>
1991          <b>External variables</b>, which are accessible anywhere in the config, or any file,
1992          using <code>std.extVar("foo")</code>.
1993        </li>
1994        <li>
1995          <b>Top-level arguments</b>, where the whole config is expressed as a function.
1996        </li>
1997      </ul>
1998      <p>
1999        To illustrate the differences, We'll show how the same example is expressed in each case.
2000      </p>
2001    </div>
2002    <div style="clear: both"></div>
2003  </div>
2004</div>
2005
2006<div class="hgroup">
2007  <div class="hgroup-inline">
2008    <div class="panel">
2009      <h3 id="external-variables">External variables</h3>
2010    </div>
2011    <div style="clear: both"></div>
2012  </div>
2013</div>
2014
2015<div class="hgroup">
2016  <div class="hgroup-inline">
2017    <div class="panel">
2018      <p>
2019        The following example binds two external variables, listed below.  Any Jsonnet value can be
2020        bound to an external variable, even functions.
2021      </p>
2022      <ul>
2023        <li><code>prefix</code> is bound to the string <code>"Happy Hour "</code></li>
2024        <li><code>brunch</code> is bound to <code>true</code></li>
2025      </ul>
2026      <p>
2027        The values are configured when the Jsonnet virtual machine is initialized, by passing either
2028        1) Jsonnet code (which evaluates to the value), 2) or a raw string.  The latter is just a
2029        convenience, because escaping a string to pass it as Jsonnet code can be tedious.  To make
2030        this concrete, the above variables can be configured with the following commandline:
2031      </p>
2032      <pre>jsonnet --ext-str prefix="Happy Hour " \
2033        --ext-code brunch=true ...
2034      </pre>
2035    </div>
2036    <div style="clear: both"></div>
2037  </div>
2038</div>
2039
2040<div class="inverse hgroup">
2041  <div class=hgroup-inline>
2042    <div class="tab-window-input" id="top-level-ext-input">
2043      <div class="tab-header">
2044      </div>
2045      <textarea id=top-level-ext-jsonnet>
2046local lib = import 'library-ext.libsonnet';
2047{
2048  [std.extVar('prefix') + 'Pina Colada']: {
2049    ingredients: [
2050      { kind: 'Rum', qty: 3 },
2051      { kind: 'Pineapple Juice', qty: 6 },
2052      { kind: 'Coconut Cream', qty: 2 },
2053      { kind: 'Ice', qty: 12 },
2054    ],
2055    garnish: 'Pineapple slice',
2056    served: 'Frozen',
2057  },
2058
2059  [if std.extVar('brunch') then
2060    std.extVar('prefix') + 'Bloody Mary'
2061  ]: {
2062    ingredients: [
2063      { kind: 'Vodka', qty: 1.5 },
2064      { kind: 'Tomato Juice', qty: 3 },
2065      { kind: 'Lemon Juice', qty: 1.5 },
2066      { kind: 'Worcestershire', qty: 0.25 },
2067      { kind: 'Tobasco Sauce', qty: 0.15 },
2068    ],
2069    garnish: 'Celery salt & pepper',
2070    served: 'Tall',
2071  },
2072
2073  [std.extVar('prefix') + 'Mimosa']:
2074    lib.Mimosa,
2075}
2076
2077      </textarea>
2078      <textarea id=library-ext-libsonnet>
2079local fizz = if std.extVar('brunch') then
2080  'Cheap Sparkling Wine'
2081else
2082  'Champagne';
2083{
2084  Mimosa: {
2085    ingredients: [
2086      { kind: fizz, qty: 3 },
2087      { kind: 'Orange Juice', qty: 3 },
2088    ],
2089    garnish: 'Orange Slice',
2090    served: 'Champagne Flute',
2091  },
2092}
2093
2094      </textarea>
2095    </div>
2096    <div class="bigarrow">➡</div>
2097    <div class="tab-window-output" id="top-level-ext-output">
2098      <div class="tab-header">
2099        <div class=selected onclick="tab_click(this, 'top-level-ext-json-output')">output.json</div>
2100      </div>
2101      <textarea readonly class="selected code-json" id="top-level-ext-json-output">
2102{
2103  "Happy Hour Bloody Mary": {
2104    "garnish": "Celery salt & pepper",
2105    "ingredients": [
2106      {
2107        "kind": "Vodka",
2108        "qty": 1.5
2109      },
2110      {
2111        "kind": "Tomato Juice",
2112        "qty": 3
2113      },
2114      {
2115        "kind": "Lemon Juice",
2116        "qty": 1.5
2117      },
2118      {
2119        "kind": "Worcestershire",
2120        "qty": 0.25
2121      },
2122      {
2123        "kind": "Tobasco Sauce",
2124        "qty": 0.15
2125      }
2126    ],
2127    "served": "Tall"
2128  },
2129  "Happy Hour Mimosa": {
2130    "garnish": "Orange Slice",
2131    "ingredients": [
2132      {
2133        "kind": "Cheap Sparkling Wine",
2134        "qty": 3
2135      },
2136      {
2137        "kind": "Orange Juice",
2138        "qty": 3
2139      }
2140    ],
2141    "served": "Champagne Flute"
2142  },
2143  "Happy Hour Pina Colada": {
2144    "garnish": "Pineapple slice",
2145    "ingredients": [
2146      {
2147        "kind": "Rum",
2148        "qty": 3
2149      },
2150      {
2151        "kind": "Pineapple Juice",
2152        "qty": 6
2153      },
2154      {
2155        "kind": "Coconut Cream",
2156        "qty": 2
2157      },
2158      {
2159        "kind": "Ice",
2160        "qty": 12
2161      }
2162    ],
2163    "served": "Frozen"
2164  }
2165}
2166
2167      </textarea>
2168    </div>
2169    
2169<script>
2170      demo(
2171        'top-level-ext-input',
2172        {
2173          'top-level-ext-jsonnet': 'top-level-ext.jsonnet',
2174          'library-ext-libsonnet': 'library-ext.libsonnet',
2175        },
2176        'top-level-ext.jsonnet',
2177        'top-level-ext-output',
2178        false,
2179        false,
2180        {
2181          ext_str: {
2182            prefix: 'Happy Hour ',
2183          },
2184          ext_code: {
2185            brunch: 'true',
2186          },
2187        }
2188      );
2189    </script>
2189
2190    <div style="clear: both"></div>
2191  </div>
2192</div>
2193
2194<div class="hgroup">
2195  <div class="hgroup-inline">
2196    <div class="panel">
2197      <h3 id="top-level-arguments">Top-level arguments</h3>
2198    </div>
2199    <div style="clear: both"></div>
2200  </div>
2201</div>
2202
2203<div class="hgroup">
2204  <div class="hgroup-inline">
2205    <div class="panel">
2206      <p>
2207        Alternatively the same code can be written using top-level arguments, where the whole config
2208        is written as a function.  How does this differ to using external variables?
2209      </p>
2210      <ul>
2211        <li>Values must be explicitly threaded through files</li>
2212        <li>Default values can be provided</li>
2213        <li>A config with top-level arguments can also be imported as a library and called as a
2214            function with parameters passed in</li>
2215      </ul>
2216      <p>
2217        Generally, top-level arguments are the safer and easier way to parameterize an entire
2218        config, because the variables are not global and it's clear what parts of the config
2219        are dependent on their environment.  However, they do require more explicit threading of
2220        the values into other imported code.  Here's the equivalent invocation of the Jsonnet
2221        command-line tool:
2222      </p>
2223      <pre>jsonnet --tla-str prefix="Happy Hour " \
2224        --tla-code brunch=true ...
2225      </pre>
2226    </div>
2227    <div style="clear: both"></div>
2228  </div>
2229</div>
2230
2231<div class="inverse hgroup">
2232  <div class=hgroup-inline>
2233    <div class="tab-window-input" id="top-level-tla-input">
2234      <div class="tab-header">
2235      </div>
2236      <textarea id=top-level-tla-jsonnet>
2237local lib = import 'library-tla.libsonnet';
2238
2239// Here is the top-level function, note brunch
2240// now has a default value.
2241function(prefix, brunch=false) {
2242
2243  [prefix + 'Pina Colada']: {
2244    ingredients: [
2245      { kind: 'Rum', qty: 3 },
2246      { kind: 'Pineapple Juice', qty: 6 },
2247      { kind: 'Coconut Cream', qty: 2 },
2248      { kind: 'Ice', qty: 12 },
2249    ],
2250    garnish: 'Pineapple slice',
2251    served: 'Frozen',
2252  },
2253
2254  [if brunch then prefix + 'Bloody Mary']: {
2255    ingredients: [
2256      { kind: 'Vodka', qty: 1.5 },
2257      { kind: 'Tomato Juice', qty: 3 },
2258      { kind: 'Lemon Juice', qty: 1.5 },
2259      { kind: 'Worcestershire', qty: 0.25 },
2260      { kind: 'Tobasco Sauce', qty: 0.15 },
2261    ],
2262    garnish: 'Celery salt & pepper',
2263    served: 'Tall',
2264  },
2265
2266  [prefix + 'Mimosa']: lib.Mimosa(brunch),
2267}
2268
2269      </textarea>
2270      <textarea id=library-tla-libsonnet>
2271{
2272  // Note that the Mimosa is now
2273  // parameterized.
2274  Mimosa(brunch): {
2275    local fizz = if brunch then
2276      'Cheap Sparkling Wine'
2277    else
2278      'Champagne',
2279    ingredients: [
2280      { kind: fizz, qty: 3 },
2281      { kind: 'Orange Juice', qty: 3 },
2282    ],
2283    garnish: 'Orange Slice',
2284    served: 'Champagne Flute',
2285  },
2286}
2287
2288
2289      </textarea>
2290    </div>
2291    <div class="bigarrow">➡</div>
2292    <div class="tab-window-output" id="top-level-tla-output">
2293      <div class="tab-header">
2294        <div class=selected onclick="tab_click(this, 'top-level-tla-json-output')">output.json</div>
2295      </div>
2296      <textarea readonly class="selected code-json" id="top-level-tla-json-output">
2297{
2298  "Happy Hour Bloody Mary": {
2299    "garnish": "Celery salt & pepper",
2300    "ingredients": [
2301      {
2302        "kind": "Vodka",
2303        "qty": 1.5
2304      },
2305      {
2306        "kind": "Tomato Juice",
2307        "qty": 3
2308      },
2309      {
2310        "kind": "Lemon Juice",
2311        "qty": 1.5
2312      },
2313      {
2314        "kind": "Worcestershire",
2315        "qty": 0.25
2316      },
2317      {
2318        "kind": "Tobasco Sauce",
2319        "qty": 0.15
2320      }
2321    ],
2322    "served": "Tall"
2323  },
2324  "Happy Hour Mimosa": {
2325    "garnish": "Orange Slice",
2326    "ingredients": [
2327      {
2328        "kind": "Cheap Sparkling Wine",
2329        "qty": 3
2330      },
2331      {
2332        "kind": "Orange Juice",
2333        "qty": 3
2334      }
2335    ],
2336    "served": "Champagne Flute"
2337  },
2338  "Happy Hour Pina Colada": {
2339    "garnish": "Pineapple slice",
2340    "ingredients": [
2341      {
2342        "kind": "Rum",
2343        "qty": 3
2344      },
2345      {
2346        "kind": "Pineapple Juice",
2347        "qty": 6
2348      },
2349      {
2350        "kind": "Coconut Cream",
2351        "qty": 2
2352      },
2353      {
2354        "kind": "Ice",
2355        "qty": 12
2356      }
2357    ],
2358    "served": "Frozen"
2359  }
2360}
2361
2362      </textarea>
2363    </div>
2364    
2364<script>
2365      demo(
2366        'top-level-tla-input',
2367        {
2368          'top-level-tla-jsonnet': 'top-level-tla.jsonnet',
2369          'library-tla-libsonnet': 'library-tla.libsonnet',
2370        },
2371        'top-level-tla.jsonnet',
2372        'top-level-tla-output',
2373        false,
2374        false,
2375        {
2376          tla_str: {
2377            prefix: 'Happy Hour ',
2378          },
2379          tla_code: {
2380            brunch: 'true',
2381          },
2382        }
2383      );
2384    </script>
2384
2385    <div style="clear: both"></div>
2386  </div>
2387</div>
2388
2389<div class="hgroup">
2390  <div class="hgroup-inline">
2391    <div class="panel">
2392      <h2 id="object-orientation">Object-Orientation</h2>
2393    </div>
2394    <div style="clear: both"></div>
2395  </div>
2396</div>
2397
2398<div class="hgroup">
2399  <div class="hgroup-inline">
2400    <div class="panel">
2401      <p>
2402        In general, object-orientation makes it easy to define many variants from a single "base".
2403        Unlike Java, C++ and Python, where classes extend other classes, in Jsonnet, objects extend
2404        other objects.  We have already discussed some of the raw ingredients for this, albeit in
2405        isolation:
2406      </p>
2407      <ul>
2408        <li>Objects (which we inherit from JSON)</li>
2409        <li>The <dfn>object composition</dfn> operator <code>+</code>, which merges two objects,
2410        choosing the right hand side when fields collide</li>
2411        <li>The <code>self</code> keyword, a reference to the current object</li>
2412      </ul>
2413      <p>
2414        When these features are combined together and with the following <i>new</i> features, things
2415        get a lot more interesting:
2416      </p>
2417      <ul>
2418        <li>Hidden fields, defined with <code>::</code>, which do not appear in generated JSON</li>
2419        <li>The <code>super</code> keyword is used to access fields on a base object.</li>
2420        <li>The <code>+:</code> field syntax for overriding deeply nested fields</li>
2421      </ul>
2422    </div>
2423    <div style="clear: both"></div>
2424  </div>
2425</div>
2426
2427<div class="hgroup">
2428  <div class="hgroup-inline">
2429    <div class="panel">
2430      <p>
2431        The following example is contrived but demonstrates a few of the features using the
2432        classical language of object-orientation, "derived" and "base".
2433      </p>
2434    </div>
2435    <div style="clear: both"></div>
2436  </div>
2437</div>
2438
2439<div class="inverse hgroup">
2440  <div class=hgroup-inline>
2441    <div class="tab-window-input" id="oo-contrived-input">
2442      <div class="tab-header">
2443      </div>
2444      <textarea id=oo-contrived-jsonnet>
2445local Base = {
2446  f: 2,
2447  g: self.f + 100,
2448};
2449
2450local WrapperBase = {
2451  Base: Base,
2452};
2453
2454{
2455  Derived: Base + {
2456    f: 5,
2457    old_f: super.f,
2458    old_g: super.g,
2459  },
2460  WrapperDerived: WrapperBase + {
2461    Base+: { f: 5 },
2462  },
2463}
2464
2465      </textarea>
2466    </div>
2467    <div class="bigarrow">➡</div>
2468    <div class="tab-window-output" id="oo-contrived-output">
2469      <div class="tab-header">
2470        <div class=selected onclick="tab_click(this, 'oo-contrived-json-output')">output.json</div>
2471      </div>
2472      <textarea readonly class="selected code-json" id="oo-contrived-json-output">
2473{
2474  "Derived": {
2475    "f": 5,
2476    "g": 105,
2477    "old_f": 2,
2478    "old_g": 105
2479  },
2480  "WrapperDerived": {
2481    "Base": {
2482      "f": 5,
2483      "g": 105
2484    }
2485  }
2486}
2487
2488      </textarea>
2489    </div>
2490    
2490<script>
2491      demo(
2492        'oo-contrived-input',
2493        {
2494          'oo-contrived-jsonnet': 'oo-contrived.jsonnet',
2495        },
2496        'oo-contrived.jsonnet',
2497        'oo-contrived-output',
2498        false,
2499        false
2500      );
2501    </script>
2501
2502    <div style="clear: both"></div>
2503  </div>
2504</div>
2505
2506
2507
2508<div class="hgroup">
2509  <div class="hgroup-inline">
2510    <div class="panel">
2511      <p>
2512        Let's make it more concrete by mixing some cocktails that are quite similar from a
2513        template that draws out their similarities.  In these examples we use implicit object
2514        composition: In the common case where you write <code>foo + {</code> ... <code>}</code>,
2515        that is, you have a <code>+</code> operation with a variable on the left and an object
2516        definition (something starting with <code>{</code>) on the right, then the <code>+</code>
2517        can be elided. This also works if the left hand side is a field access (e.g.,
2518        <code>a.b</code>, or <code>a[b]</code>), function call (e.g., <code>f(a)</code>),
2519        or parenthesised expression.
2520      </p>
2521      <p>
2522        Try explicitly adding the + in the 4 cases, below.
2523      </p>
2524    </div>
2525    <div style="clear: both"></div>
2526  </div>
2527</div>
2528
2529<div class="inverse hgroup">
2530  <div class=hgroup-inline>
2531    <div class="tab-window-input" id="sours-oo-input">
2532      <div class="tab-header">
2533      </div>
2534      <textarea id=sours-oo-jsonnet>
2535local templates = import 'templates.libsonnet';
2536
2537{
2538  // The template requires us to override
2539  // the 'spirit'.
2540  'Whiskey Sour': templates.Sour {
2541    spirit: 'Whiskey',
2542  },
2543
2544  // Specialize it further.
2545  'Deluxe Sour': self['Whiskey Sour'] {
2546    // Don't replace the whole sweetner,
2547    // just change 'kind' within it.
2548    sweetener+: { kind: 'Gomme Syrup' },
2549  },
2550
2551  Daiquiri: templates.Sour {
2552    spirit: 'Banks 7 Rum',
2553    citrus+: { kind: 'Lime' },
2554    // Any field can be overridden.
2555    garnish: 'Lime wedge',
2556  },
2557
2558  "Nor'Easter": templates.Sour {
2559    spirit: 'Whiskey',
2560    citrus: { kind: 'Lime', qty: 0.5 },
2561    sweetener+: { kind: 'Maple Syrup' },
2562    // +: Can also add to a list.
2563    ingredients+: [
2564      { kind: 'Ginger Beer', qty: 1 },
2565    ],
2566  },
2567}
2568
2569      </textarea>
2570      <textarea id=templates-libsonnet>
2571{
2572  // Abstract template of a "sour" cocktail.
2573  Sour: {
2574    local drink = self,
2575
2576    // Hidden fields can be referred to
2577    // and overridden, but do not appear
2578    // in the JSON output.
2579    citrus:: {
2580      kind: 'Lemon Juice',
2581      qty: 1,
2582    },
2583    sweetener:: {
2584      kind: 'Simple Syrup',
2585      qty: 0.5,
2586    },
2587
2588    // A field that must be overridden.
2589    spirit:: error 'Must override "spirit"',
2590
2591    ingredients: [
2592      { kind: drink.spirit, qty: 2 },
2593      drink.citrus,
2594      drink.sweetener,
2595    ],
2596    garnish: self.citrus.kind + ' twist',
2597    served: 'Straight Up',
2598  },
2599}
2600
2601      </textarea>
2602    </div>
2603    <div class="bigarrow">➡</div>
2604    <div class="tab-window-output" id="sours-oo-output">
2605      <div class="tab-header">
2606        <div class=selected onclick="tab_output_click(this, 'sours-oo-json-output')">output.json</div>
2607      </div>
2608      <textarea readonly class="selected code-json" id="sours-oo-json-output">
2609{
2610  "Daiquiri": {
2611    "garnish": "Lime wedge",
2612    "ingredients": [
2613      {
2614        "kind": "Banks 7 Rum",
2615        "qty": 2
2616      },
2617      {
2618        "kind": "Lime",
2619        "qty": 1
2620      },
2621      {
2622        "kind": "Simple Syrup",
2623        "qty": 0.5
2624      }
2625    ],
2626    "served": "Straight Up"
2627  },
2628  "Deluxe Sour": {
2629    "garnish": "Lemon Juice twist",
2630    "ingredients": [
2631      {
2632        "kind": "Whiskey",
2633        "qty": 2
2634      },
2635      {
2636        "kind": "Lemon Juice",
2637        "qty": 1
2638      },
2639      {
2640        "kind": "Gomme Syrup",
2641        "qty": 0.5
2642      }
2643    ],
2644    "served": "Straight Up"
2645  },
2646  "Nor'Easter": {
2647    "garnish": "Lime twist",
2648    "ingredients": [
2649      {
2650        "kind": "Whiskey",
2651        "qty": 2
2652      },
2653      {
2654        "kind": "Lime",
2655        "qty": 0.5
2656      },
2657      {
2658        "kind": "Maple Syrup",
2659        "qty": 0.5
2660      },
2661      {
2662        "kind": "Ginger Beer",
2663        "qty": 1
2664      }
2665    ],
2666    "served": "Straight Up"
2667  },
2668  "Whiskey Sour": {
2669    "garnish": "Lemon Juice twist",
2670    "ingredients": [
2671      {
2672        "kind": "Whiskey",
2673        "qty": 2
2674      },
2675      {
2676        "kind": "Lemon Juice",
2677        "qty": 1
2678      },
2679      {
2680        "kind": "Simple Syrup",
2681        "qty": 0.5
2682      }
2683    ],
2684    "served": "Straight Up"
2685  }
2686}
2687
2688      </textarea>
2689    </div>
2690    
2690<script>
2691      demo(
2692        'sours-oo-input',
2693        {
2694          'sours-oo-jsonnet': 'sours-oo.jsonnet',
2695          'templates-libsonnet': 'templates.libsonnet',
2696        },
2697        'sours-oo.jsonnet',
2698        'sours-oo-output',
2699        false,
2700        false
2701      );
2702    </script>
2702
2703    <div style="clear: both"></div>
2704  </div>
2705</div>
2706
2707<div class="hgroup">
2708  <div class="hgroup-inline">
2709    <div class="panel">
2710      <p>
2711        The key to making Jsonnet object-oriented is that the <code>self</code> keyword is "late
2712        bound".  In other words, <code>self.foo</code> can have its meaning altered by overriding it
2713        on the right hand side of a <code>+</code>.  This would not be the case in a simple "object
2714        merge" semantics of <code>+</code>, where <code>self.foo</code> would be fully evaluated to
2715        a concrete value before we did the <code>+</code>.  In the above example, the left hand side
2716        of the <code>+</code> is <code>templates.Sour</code> (the <code>+</code> is implicit) and
2717        the right hand side is an object that overrides <code>spirit</code>.  So in the
2718        <code>templates.Sour</code> definition (in <tt>templates.libsonnet</tt>) that causes the
2719        reference to <code>drink.spirit</code> in the ingredients list to return
2720        <code>"Whiskey"</code> rather than raise the error <code>'Must override "spirit"'</code>.
2721      </p>
2722      <p>
2723        Most of the abstraction in the above example could have been achieved with functions instead
2724        of object-orientation.  Indeed, last time we spoke about sours, we used functions for that
2725        very purpose.  In general, the choice of whether to use functions or object-orientation is
2726        quite a complex one, and can be driven as much by instinct as by anything else.  The most
2727        obvious difference is that with object-orientation, you can override any field from the
2728        resulting object, whereas with functions you can only pass arguments for the specific
2729        parameters of the function.  So, e.g., if the <code>templates.Sour</code> had been
2730        implemented as a function that did not parameterize the ingredients list in general way,
2731        then it would not have been possible to add that extra ingredient to form the Nor'Easter.
2732      </p>
2733      <p>
2734        So far, every example has used a literal object on the right hand side of the
2735        <code>+</code>.  This is consistent with the majority of object-oriented languages. But
2736        Jsonnet is more general in that is has <a
2737        href="https://www.bracha.org/oopsla90.pdf">mixins</a>.  A Mixin allows you to take some
2738        "overrides" and instead of just applying them to an existing object to get a new object, you
2739        can pass them around as a first class value and apply them to any object you want.  The
2740        following example shows mixins being used to modify cocktails in a general way:
2741      </p>
2742    </div>
2743    <div style="clear: both"></div>
2744  </div>
2745</div>
2746
2747<div class="inverse hgroup">
2748  <div class=hgroup-inline>
2749    <div class="tab-window-input" id="mixins-input">
2750      <div class="tab-header">
2751      </div>
2752      <textarea id=mixins-jsonnet>
2753local sours = import 'sours-oo.jsonnet';
2754
2755local RemoveGarnish = {
2756  // Not technically removed, but made hidden.
2757  garnish:: super.garnish,
2758};
2759
2760// Make virgin cocktails
2761local NoAlcohol = {
2762  local Substitute(ingredient) =
2763    local k = ingredient.kind;
2764    local bitters = 'Angustura Bitters';
2765    if k == 'Whiskey' then [
2766      { kind: 'Water', qty: ingredient.qty },
2767      { kind: bitters, qty: 'tsp' },
2768    ] else if k == 'Banks 7 Rum' then [
2769      { kind: 'Water', qty: ingredient.qty },
2770      { kind: 'Vanilla Essence', qty: 'dash' },
2771      { kind: bitters, qty: 'dash' },
2772    ] else [
2773      ingredient,
2774    ],
2775  ingredients: std.flattenArrays([
2776    Substitute(i)
2777    for i in super.ingredients
2778  ]),
2779};
2780
2781local PartyMode = {
2782  served: 'In a plastic cup',
2783};
2784
2785{
2786  'Whiskey Sour':
2787    sours['Whiskey Sour']
2788    + RemoveGarnish + PartyMode,
2789
2790  'Virgin Whiskey Sour':
2791    sours['Whiskey Sour'] + NoAlcohol,
2792
2793  'Virgin Daiquiri':
2794    sours.Daiquiri + NoAlcohol,
2795
2796}
2797
2798      </textarea>
2799      <textarea id=sours-oo-jsonnet2>
2800local templates = import 'templates.libsonnet';
2801
2802{
2803  // The template requires us to override
2804  // the 'spirit'.
2805  'Whiskey Sour': templates.Sour {
2806    spirit: 'Whiskey',
2807  },
2808
2809  // Specialize it further.
2810  'Deluxe Sour': self['Whiskey Sour'] {
2811    // Don't replace the whole sweetner,
2812    // just change 'kind' within it.
2813    sweetener+: { kind: 'Gomme Syrup' },
2814  },
2815
2816  Daiquiri: templates.Sour {
2817    spirit: 'Banks 7 Rum',
2818    citrus+: { kind: 'Lime' },
2819    // Any field can be overridden.
2820    garnish: 'Lime wedge',
2821  },
2822
2823  "Nor'Easter": templates.Sour {
2824    spirit: 'Whiskey',
2825    citrus: { kind: 'Lime', qty: 0.5 },
2826    sweetener+: { kind: 'Maple Syrup' },
2827    // +: Can also add to a list.
2828    ingredients+: [
2829      { kind: 'Ginger Beer', qty: 1 },
2830    ],
2831  },
2832}
2833
2834      </textarea>
2835      <textarea id=templates-libsonnet2>
2836{
2837  // Abstract template of a "sour" cocktail.
2838  Sour: {
2839    local drink = self,
2840
2841    // Hidden fields can be referred to
2842    // and overridden, but do not appear
2843    // in the JSON output.
2844    citrus:: {
2845      kind: 'Lemon Juice',
2846      qty: 1,
2847    },
2848    sweetener:: {
2849      kind: 'Simple Syrup',
2850      qty: 0.5,
2851    },
2852
2853    // A field that must be overridden.
2854    spirit:: error 'Must override "spirit"',
2855
2856    ingredients: [
2857      { kind: drink.spirit, qty: 2 },
2858      drink.citrus,
2859      drink.sweetener,
2860    ],
2861    garnish: self.citrus.kind + ' twist',
2862    served: 'Straight Up',
2863  },
2864}
2865
2866      </textarea>
2867    </div>
2868    <div class="bigarrow">➡</div>
2869    <div class="tab-window-output" id="mixins-output">
2870      <div class="tab-header">
2871        <div class=selected onclick="tab_output_click(this, 'mixins-json-output')">output.json</div>
2872      </div>
2873      <textarea readonly class="selected code-json" id="mixins-json-output">
2874{
2875  "Virgin Daiquiri": {
2876    "garnish": "Lime wedge",
2877    "ingredients": [
2878      {
2879        "kind": "Water",
2880        "qty": 2
2881      },
2882      {
2883        "kind": "Vanilla Essence",
2884        "qty": "dash"
2885      },
2886      {
2887        "kind": "Angustura Bitters",
2888        "qty": "dash"
2889      },
2890      {
2891        "kind": "Lime",
2892        "qty": 1
2893      },
2894      {
2895        "kind": "Simple Syrup",
2896        "qty": 0.5
2897      }
2898    ],
2899    "served": "Straight Up"
2900  },
2901  "Virgin Whiskey Sour": {
2902    "garnish": "Lemon Juice twist",
2903    "ingredients": [
2904      {
2905        "kind": "Water",
2906        "qty": 2
2907      },
2908      {
2909        "kind": "Angustura Bitters",
2910        "qty": "tsp"
2911      },
2912      {
2913        "kind": "Lemon Juice",
2914        "qty": 1
2915      },
2916      {
2917        "kind": "Simple Syrup",
2918        "qty": 0.5
2919      }
2920    ],
2921    "served": "Straight Up"
2922  },
2923  "Whiskey Sour": {
2924    "ingredients": [
2925      {
2926        "kind": "Whiskey",
2927        "qty": 2
2928      },
2929      {
2930        "kind": "Lemon Juice",
2931        "qty": 1
2932      },
2933      {
2934        "kind": "Simple Syrup",
2935        "qty": 0.5
2936      }
2937    ],
2938    "served": "In a plastic cup"
2939  }
2940}
2941
2942      </textarea>
2943    </div>
2944    
2944<script>
2945      demo(
2946        'mixins-input',
2947        {
2948          'mixins-jsonnet': 'mixins.jsonnet',
2949          'sours-oo-jsonnet2': 'sours-oo.jsonnet',
2950          'templates-libsonnet2': 'templates.libsonnet',
2951        },
2952        'mixins.jsonnet',
2953        'mixins-output',
2954        false,
2955        false
2956      );
2957    </script>
2957
2958    <div style="clear: both"></div>
2959  </div>
2960</div>
2961
2962<div class="hgroup">
2963  <div class="hgroup-inline">
2964    <div class="panel">
2965      <p>
2966        Thank you for completing the Jsonnet tutorial! We hope you enjoyed the accompanying
2967        beverages.  At this point, you should be able to build concoctions of your own.  If you want
2968        to read about the Jsonnet language in greater and more systematic detail, we recommend the
2969        language <a href="/ref/language.html">reference</a>.
2970      </p>
2971    </div>
2972    <div style="clear: both"></div>
2973  </div>
2974</div>
2975
2976<div class="hgroup">
2977  <div class="hgroup-inline">
2978    <div class="panel">
2979    </div>
2980    <div style="clear: both"></div>
2981  </div>
2982</div>
2983
2984
2985
2986<div class="inverse hgroup">
2987  <div class="hgroup-inline">
2988    <div class="panel">
2989      <p class=footer>
2990        Except as noted, this content is licensed under Creative Commons Attribution 2.5.
2991      </p>
2992    </div>
2993  </div>
2994</div>
2995
2996<script>
2997  anchors.options = {
2998    placement: 'right',
2999    visible: 'always',
3000    icon: '#'
3001  };
3002  anchors.add('h2,h3');
3003</script>
3003
3004</body>
3005
3006</html>
3007

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.