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><</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.