CINXE.COM
IPLD ♦ Schemas: Authoring Guide
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="/css/layout.css?1718759055581"> <link rel="stylesheet" href="/css/nav.css?1718759055581"> <link rel="stylesheet" href="/css/style.css?1718759055581"> <link rel="stylesheet" href="/css/prismjs@1.24-themes-prism.css"> <title>IPLD ♦ Schemas: Authoring Guide</title> </head> <body> <header> <div class="sidebar-button" onclick="document.getElementById('sidebar').classList.toggle('sidebar-open')"> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"> <path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path> </svg> </div> <a href="/" class="logo">IPLD</a> <aside id=breadcrumbs> <ul> <li><a href="/docs">docs</a></li> <li><a href="/docs/schemas">schemas</a></li> <li><a href="/docs/schemas/using">using</a></li> <li><a href="/docs/schemas/using/authoring-guide/">authoring-guide</a></li> </ul> </aside> </header> <aside id=sidebar> <nav> <ul> <li> <a href="/docs/">Docs</a><ul> <li> <a href="/docs/intro/">Intro</a><ul> <li> <a href="/docs/intro/hello-world/">Hello, World</a></li> <li> <a href="/docs/intro/primer/">The Brief Primer</a></li> <li> <a href="/docs/intro/ecosystem/">InterPlanetary Ecosystem Overview</a></li> <li> <a href="/docs/intro/community/">Finding Community</a></li></ul></li> <li> <a href="/docs/motivation/">Motivation</a><ul> <li> <a href="/docs/motivation/benefits-of-content-addressing/">Benefits of Content Addressing</a></li> <li> <a href="/docs/motivation/data-to-data-structures/">From Data to Data Structures</a></li></ul></li> <li> <a href="/docs/codecs/">Codecs</a><ul> <li> <a href="/docs/codecs/known/">Known Codecs</a><ul> <li> <a href="/docs/codecs/known/dag-cbor/">DAG-CBOR</a></li> <li> <a href="/docs/codecs/known/dag-json/">DAG-JSON</a></li> <li> <a href="/docs/codecs/known/dag-pb/">DAG-PB</a></li></ul></li></ul></li> <li> <a href="/docs/data-model/">Data Model</a><ul> <li> <a href="/docs/data-model/node/">Nodes</a></li> <li> <a href="/docs/data-model/kinds/">Kinds</a></li> <li> <a href="/docs/data-model/pathing/">Pathing</a></li> <li> <a href="/docs/data-model/traversal/">Traversal</a></li></ul></li> <li> <a href="/docs/advanced-data-layouts/">Advanced Data Layouts</a><ul> <li> <a href="/docs/advanced-data-layouts/intro/">Intro to ADLs</a></li> <li> <a href="/docs/advanced-data-layouts/naming/">ADL Naming</a></li> <li> <a href="/docs/advanced-data-layouts/signalling/">Signalling ADLs</a></li> <li> <a href="/docs/advanced-data-layouts/dynamic-loading/">Dynamic Loading</a></li> <li> <a href="/docs/advanced-data-layouts/known/">Known ADLs</a></li></ul></li> <li> <a href="/docs/schemas/">Schemas</a><ul> <li> <a href="/docs/schemas/intro/">Introduction</a><ul> <li> <a href="/docs/schemas/intro/compare/">compare</a></li> <li> <a href="/docs/schemas/intro/goals/">Goals</a></li> <li> <a href="/docs/schemas/intro/feature-summary/">Feature Summary</a></li></ul></li> <li> <a href="/docs/schemas/features/">Features</a><ul> <li> <a href="/docs/schemas/features/typekinds/">Type Kinds</a></li> <li> <a href="/docs/schemas/features/representation-strategies/">Representation Strategies</a></li> <li> <a href="/docs/schemas/features/links/">Links</a></li> <li> <a href="/docs/schemas/features/indicating-adls/">Using ADLs in Schemas</a></li></ul></li> <li> <a href="/docs/schemas/using/">Using Wisely</a><ul> <li class="active-page"> <a href="/docs/schemas/using/authoring-guide/">Authoring Guide</a></li> <li> <a href="/docs/schemas/using/migrations/">Migrations</a></li></ul></li></ul></li> <li> <a href="/docs/synthesis/">Synthesis</a><ul> <li> <a href="/docs/synthesis/gtd/">Getting Things Done</a></li> <li> <a href="/docs/synthesis/building-in-alignment/">Building in Alignment</a></li> <li> <a href="/docs/synthesis/how-ipfs-web-gateways-work/">How IPFS Web Gateways Work</a></li> <li> <a href="/docs/synthesis/encryption/">Working With Encryption</a></li></ul></li></ul></li> <li> <a href="/specs/">Specs</a><ul> <li> <a href="/specs/about/">About the Specifications</a></li> <li> <a href="/specs/codecs/">Codecs</a><ul> <li> <a href="/specs/codecs/dag-cbor/">DAG-CBOR</a><ul> <li> <a href="/specs/codecs/dag-cbor/fixtures/">DAG-CBOR Test Fixtures</a><ul> <li> <a href="/specs/codecs/dag-cbor/fixtures/cross-codec/">cross-codec</a></li></ul></li> <li> <a href="/specs/codecs/dag-cbor/spec/">Spec</a></li></ul></li> <li> <a href="/specs/codecs/dag-cosmos/">DAG-COSMOS</a><ul> <li> <a href="/specs/codecs/dag-cosmos/basic_types/">basic_types</a></li> <li> <a href="/specs/codecs/dag-cosmos/cosmos_state/">cosmos_state</a></li> <li> <a href="/specs/codecs/dag-cosmos/crypto_types/">crypto_types</a></li> <li> <a href="/specs/codecs/dag-cosmos/tendermint_chain/">tendermint_chain</a></li> <li> <a href="/specs/codecs/dag-cosmos/typed_protobuf/">typed_protobuf</a></li></ul></li> <li> <a href="/specs/codecs/dag-eth/">DAG-ETH</a><ul> <li> <a href="/specs/codecs/dag-eth/basic_types/">basic_types</a></li> <li> <a href="/specs/codecs/dag-eth/chain/">chain</a></li> <li> <a href="/specs/codecs/dag-eth/convenience_types/">convenience_types</a></li> <li> <a href="/specs/codecs/dag-eth/state/">state</a></li></ul></li> <li> <a href="/specs/codecs/dag-jose/">DAG-JOSE</a><ul> <li> <a href="/specs/codecs/dag-jose/fixtures/">fixtures</a></li> <li> <a href="/specs/codecs/dag-jose/spec/">Spec</a></li></ul></li> <li> <a href="/specs/codecs/dag-json/">DAG-JSON</a><ul> <li> <a href="/specs/codecs/dag-json/fixtures/">DAG-JSON Test Fixtures</a><ul> <li> <a href="/specs/codecs/dag-json/fixtures/cross-codec/">cross-codec</a></li></ul></li> <li> <a href="/specs/codecs/dag-json/spec/">Spec</a></li></ul></li> <li> <a href="/specs/codecs/dag-pb/">DAG-PB</a><ul> <li> <a href="/specs/codecs/dag-pb/fixtures/">DAG-PB Test Fixtures</a><ul> <li> <a href="/specs/codecs/dag-pb/fixtures/cross-codec/">cross-codec</a></li></ul></li> <li> <a href="/specs/codecs/dag-pb/spec/">Spec</a></li></ul></li></ul></li> <li> <a href="/specs/advanced-data-layouts/">Advanced Data Layouts</a><ul> <li> <a href="/specs/advanced-data-layouts/fbl/">FBL ADL</a><ul> <li> <a href="/specs/advanced-data-layouts/fbl/spec/">spec</a></li></ul></li> <li> <a href="/specs/advanced-data-layouts/hamt/">HAMT ADL</a><ul> <li> <a href="/specs/advanced-data-layouts/hamt/fixture/">HashMap (HAMT) Test Fixtures</a><ul> <li> <a href="/specs/advanced-data-layouts/hamt/fixture/alice-words/">alice-words</a></li></ul></li> <li> <a href="/specs/advanced-data-layouts/hamt/spec/">spec</a></li></ul></li></ul></li> <li> <a href="/specs/schemas/">Schemas</a><ul> <li> <a href="/specs/schemas/prelude/">prelude</a></li></ul></li> <li> <a href="/specs/transport/">Transports</a><ul> <li> <a href="/specs/transport/car/">CAR</a><ul> <li> <a href="/specs/transport/car/carv1/">CARv1 Specification</a></li> <li> <a href="/specs/transport/car/carv2/">CARv2 Specification</a></li> <li> <a href="/specs/transport/car/fixture/">CAR Test Fixtures</a><ul> <li> <a href="/specs/transport/car/fixture/carv1-basic/">carv1-basic</a></li> <li> <a href="/specs/transport/car/fixture/carv2-basic/">carv2-basic</a></li></ul></li></ul></li> <li> <a href="/specs/transport/graphsync/">Graphsync</a><ul> <li> <a href="/specs/transport/graphsync/known_extensions/">known_extensions</a></li></ul></li> <li> <a href="/specs/transport/trustless-pathing/">Trustless Pathing</a><ul> <li> <a href="/specs/transport/trustless-pathing/fixtures/">Trustless Pathing Fixtures</a><ul> <li> <a href="/specs/transport/trustless-pathing/fixtures/unixfs_20m_variety/">unixfs_20m_variety</a></li></ul></li></ul></li></ul></li> <li> <a href="/specs/selectors/">Selectors</a><ul> <li> <a href="/specs/selectors/fixtures/">fixtures</a><ul> <li> <a href="/specs/selectors/fixtures/selector-fixtures-1/">selector-fixtures-1</a></li> <li> <a href="/specs/selectors/fixtures/selector-fixtures-adl/">selector-fixtures-adl</a></li> <li> <a href="/specs/selectors/fixtures/selector-fixtures-recursion/">selector-fixtures-recursion</a></li></ul></li></ul></li> <li> <a href="/specs/patch/">Patch</a><ul> <li> <a href="/specs/patch/fixtures/">IPLD Patch Test Fixtures</a><ul> <li> <a href="/specs/patch/fixtures/fixtures-1/">fixtures-1</a></li></ul></li></ul></li></ul></li> <li> <a href="/libraries/">Libraries</a><ul> <li> <a href="/libraries/golang/">Golang</a></li> <li> <a href="/libraries/javascript/">JavaScript</a></li> <li> <a href="/libraries/python/">Python</a></li> <li> <a href="/libraries/rust/">Rust</a></li></ul></li> <li> <a href="/design/">Design</a><ul> <li> <a href="/design/objectives/">Objectives</a></li> <li> <a href="/design/concepts/">Concepts</a><ul> <li> <a href="/design/concepts/type-theory-glossary/">type-theory-glossary</a></li></ul></li> <li> <a href="/design/libraries/">Libraries</a><ul> <li> <a href="/design/libraries/nodes-and-kinds/">nodes-and-kinds</a></li></ul></li> <li> <a href="/design/tricky-choices/">Tricky Choices</a><ul> <li> <a href="/design/tricky-choices/dag-pb-forms-impl-and-use/">dag-pb-forms-impl-and-use</a></li> <li> <a href="/design/tricky-choices/map-key-domain/">map-key-domain</a></li> <li> <a href="/design/tricky-choices/numeric-domain/">numeric-domain</a></li> <li> <a href="/design/tricky-choices/ordering/">ordering</a></li> <li> <a href="/design/tricky-choices/string-domain/">string-domain</a></li></ul></li> <li> <a href="/design/open-research/">Open Research</a><ul> <li> <a href="/design/open-research/ADL-autoexecution/">ADL autoexecution</a></li></ul></li></ul></li> <li> <a href="/tools/">Tools</a></li> <li> <a href="/glossary/">Glossary</a></li> <li> <a href="/media/">Media</a></li> <li> <a href="/FAQ/">FAQ</a></li></ul> </nav> </aside> <main> <div class=content> <h1>Authoring IPLD Schemas</h1> <ul> <li><a href="#basics">Basics</a> <ul> <li><a href="#records-type-and-advanced">Records: <code>type</code> and <code>advanced</code></a></li> <li><a href="#newlines-and-whitespace">Newlines and Whitespace</a></li> <li><a href="#comments">Comments</a></li> </ul> </li> <li><a href="#schema-kinds">Schema Kinds</a></li> <li><a href="#naming-types">Naming Types</a></li> <li><a href="#named-scalar-types-typedefs">Named Scalar Types (typedefs)</a></li> <li><a href="#links">Links</a></li> <li><a href="#inline-recursive-types">Inline Recursive Types</a></li> <li><a href="#representations">Representations</a> <ul> <li><a href="#representation-parameters">Representation Parameters</a> <ul> <li><a href="#general-representation-parameters">General Representation Parameters</a></li> <li><a href="#field-specific-representation-parameters">Field-specific Representation Parameters</a></li> </ul> </li> </ul> </li> <li><a href="#structs">Structs</a></li> <li><a href="#enums">Enums</a></li> <li><a href="#unions">Unions</a> <ul> <li><a href="#introduction-to-unions-kinded-unions">Introduction to Unions: Kinded Unions</a></li> <li><a href="#limitations-of-union-discrimination">Limitations of Union Discrimination</a></li> <li><a href="#alternative-discrimination-strategies">Alternative Discrimination Strategies</a> <ul> <li><a href="#keyed">Keyed</a></li> <li><a href="#envelope">Envelope</a></li> <li><a href="#inline">Inline</a></li> </ul> </li> <li><a href="#stringprefix-unions-for-strings">Stringprefix Unions for Strings</a></li> <li><a href="#bytesprefix-unions-for-bytes">Bytesprefix Unions for Bytes</a></li> </ul> </li> <li><a href="#copy">Copy</a></li> <li><a href="#advanced-data-layouts">Advanced Data Layouts</a></li> <li><a href="#schemas-in-markdown">Schemas in Markdown</a></li> </ul> <p>IPLD Schemas can be represented in a compact, human-friendly <a href="https://en.wikipedia.org/wiki/Domain_specific_language">DSL</a>. IPLD Schemas can also be naturally represented as an IPLD node graph, typically presented in JSON form. The human-friendly DSL compiles into this IPLD-native format.</p> <h2 id="basics" tabindex="-1"><a class="header-anchor" href="#basics">Basics</a></h2> <h3 id="records-type-and-advanced" tabindex="-1"><a class="header-anchor" href="#records-type-and-advanced">Records: <code>type</code> and <code>advanced</code></a></h3> <p>IPLD Schemas typically comprise a collection of optionally interdependent types. Each type definition starts with a <code>type</code> prefix at the beginning of a line, followed by the type's name and then its definition. One other style of record optionally exists within an IPLD Schema, Advanced Data Layouts. These replace the <code>type</code> keyword with <code>advanced</code> and have specific rules about their contents. More on this below.</p> <h3 id="newlines-and-whitespace" tabindex="-1"><a class="header-anchor" href="#newlines-and-whitespace">Newlines and Whitespace</a></h3> <p>The DSL treats newlines as significant, they are used to break up records (<code>type</code> and <code>advanced</code>) and descriptors within records. Newlines are used in a similar way to programming languages that substitute C-style <code>;</code> breaks with significant newlines.</p> <p>Multiple newline characters are folded in to one during parsing, so newlines may be used for formatting and documentation purposes where appropriate. It is also not necessary to separate records by a specific number of newlines, although a single blank line is typical.</p> <p>Whitespace characters (tab and space) are also folded in to a single space during parsing, so may be used for formatting and documentation purposes where appropriate. Most tokens that don't need to be proceeded by a newline should be separated by at least one newline character. There are other tokens that don't strictly require a newline (e.g. <code>{String:Int}</code> for Map definitions where 5 tokens may be conjoined, but also may be separated, <code>{ String : Int }</code>). Indenting is not strictly required for record component descriptors but are typical as they can be used to express intent.</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> a Int b Int msg Message <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Message</span></span> <span class="token keyword">string</span></code></pre> <p>In this example:</p> <ul> <li>The whitespace between non-punctuation tokens is required (<code>typeMessagestring</code> would be nonsense!)</li> <li>At least one newline between each component of the <code>Foo</code> record are required such that <code>a</code>, <code>b</code>, and <code>msg</code> are all on separate lines.</li> <li>The indenting for <code>a</code>, <code>b</code>, and <code>msg</code> is optional but helps express the ownership of these items to the parent record.</li> <li>The additional spaces between <code>a</code>, <code>b</code> and their <code>Int</code> type descriptors is optional and used as a formatting nicety to line up the types in a Struct.</li> <li>The blank line between the close of <code>Foo</code> and <code>Message</code> is optional but is intended to help with readability.</li> <li>Newline and space rules for <code>{</code> and <code>}</code> are lax but convention is to use the locations and spacing in this example.</li> </ul> <h3 id="comments" tabindex="-1"><a class="header-anchor" href="#comments">Comments</a></h3> <p>All characters on a line following a <code>#</code> character are ignored during parsing. This allows for full-line comments and comments trailing Schema DSL tokens:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token comment">#</span> <span class="token comment"># This is a (pseudo)block comment</span> <span class="token comment">#</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> a Int <span class="token comment"># An inline comment</span> b Int msg Message <span class="token punctuation">}</span> <span class="token comment"># Another full-line comment</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Message</span></span> <span class="token keyword">string</span></code></pre> <h2 id="schema-kinds" tabindex="-1"><a class="header-anchor" href="#schema-kinds">Schema Kinds</a></h2> <p>See <a href="/docs/schemas/features/typekinds/">IPLD Schema Typekinds</a> for more information on this topic.</p> <p>The schema kinds have matching tokens that appear throughout IPLD Schemas. Depending on context, the tokens are either lower-case (e.g. <code>int</code>) or title-case (e.g. <code>Int</code>), or may omitted entirely because they can be reliably inferred. This will become clear as we proceed.</p> <ul> <li>Null: may appear as a typedef'd <code>null</code> but there is discussion regarding the possibility of changing the semantics of how Null is used in Schemas. It is not commonly useful outside of the <code>nullable</code> signifier for Struct fields.</li> <li>Boolean: may appear as <code>Bool</code> for a component specifier or <code>bool</code> as a typedef.</li> <li>Integer: May appear as <code>Int</code> for a component specifier or <code>int</code> as a typedef. There are no additional specifiers for integer size or signedness (although this may appear as adjuncts for codegen in the future).</li> <li>Float: May appear as <code>Float</code> for a component specifier or <code>float</code> as a typedef. There are no additional specifiers for size or byte representation (although this may appear as adjuncts for codegen in the future).</li> <li>String: May appear as <code>String</code> for a component specifier or <code>string</code> as a typedef. The Data Model assumes unicode. Specific string encodings also appear as representation forms, see below. The <code>stringprefix</code> Union representation strategy is a special case indicating a prefix string dictating the type of the proceeding characters, see below.</li> <li>Bytes: May appear as <code>Bytes</code> for a component specifier or <code>bytes</code> as a typedef. There are no additional specifiers for byte array length and there is no way to specify a single byte. The <code>bytesprefix</code> Union representation type is a special case indicating prefix string of one or more bytes dictates the type of the proceeding bytes, see below.</li> <li>List: Is inferred by the <code>[Type]</code> shorthand for both typedefs and inline component specification. The token "List" is not used in the Schema DSL and all Lists must have value type specified (although Unions allow for significant flexibility here).</li> <li>Map: Is inferred by the <code>{KeyType:ValueType}</code> shorthand for both typedefs and inline component specification. The token "Map" is not used in the Schema DSL and all Maps must have key and value type specified (although Unions allow for significant flexibility here).</li> <li>Link: The <code>&</code> token prefixing a type is used as a shorthand for links. A generic link to an untyped resource uses the special <code>&Any</code>, while a link where there is an expected type to be found uses that type name as a hinting mechanism, <code>&Foo</code>. See below and <a href="/docs/schemas/features/links/">Links in IPLD Schemas</a> for more information.</li> <li>Union: Appears as <code>union</code> following <code>type</code> and the Union's type name.</li> <li>Struct: Appears as <code>struct</code> following <code>type</code> and the Struct's type name.</li> <li>Enum: Appears as <code>enum</code> following <code>type</code> and the Enum's type name.</li> <li>Copy: Uses the shorthand <code>=</code> to indicate a copy type, as in <code>type Foo = Bar</code>. The token "Copy" does not directly appear in the Schema DSL.</li> </ul> <h2 id="naming-types" tabindex="-1"><a class="header-anchor" href="#naming-types">Naming Types</a></h2> <p>Type names <em>must</em> only contain alphanumeric ASCII characters and underscores. The first character <em>must</em> be a capital letter. Multiple connected underscores <em>should</em> be avoided (they should be reserved for codegen purposes). A strict regular expression for type names would be: <code>[a-zA-Z][a-zA-Z0-9_]*</code>. A regular expression following convention would be: <code>[A-Z][a-zA-Z0-9_]*</code> (disregarding the multiple-underscore rule for simplicity).</p> <p>Camel case with an upper case first character is recommended. Underscore <code>_</code> should be used sparingly. <code>ThisIsRecommend</code>, <code>This_Not_So_Much</code>, <code>Thisisnotrecommended</code>, <code>neitherIsThis</code>.</p> <p>Type names are unique within a Schema and are ideally unique within related Schema documents; overlapping names are generally not ideal for documentation purposes. Certain forms of Schema kind identifiers are forbidden and those forms that are not forbidden should be avoided to save confusion for documentation purposes. i.e. <code>Null</code>, <code>Boolean</code>, <code>Int</code>, <code>Float</code>, <code>String</code>, <code>Bytes</code> are strictly not allowed as type names (they are already implicit type names), and their lower-case counterparts and the additional schema kinds should be avoided.</p> <p><strong>Type names should be used as a documentation tool</strong>. They don't need to be short if long names are more helpfully descriptive.</p> <h2 id="named-scalar-types-typedefs" tabindex="-1"><a class="header-anchor" href="#named-scalar-types-typedefs">Named Scalar Types (typedefs)</a></h2> <p>The non-recursive (scalar) Schema kinds (Boolean, Integer, Float, String, Bytes, Link) may all appear as typedef'd types. That is, a unique name may be assigned to a kind and that name may be used in place of the kind later in the schema. Multiple unique type names may share the same kind.</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token keyword">string</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Bar</span></span> <span class="token keyword">int</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Boom</span></span> <span class="token punctuation">{</span>Foo<span class="token punctuation">:</span>Bar<span class="token punctuation">}</span></code></pre> <p>In terms of data layout, this is equivalent to:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Boom</span></span> <span class="token punctuation">{</span>String<span class="token punctuation">:</span>Int<span class="token punctuation">}</span></code></pre> <p>(Note that even though the Data Model only allows for string keys of maps, the indirection through type <code>Foo</code> is allowed since it has a string representation.)</p> <p>There are a number of reasons to typedef a scalar Schema kind:</p> <ul> <li>Documentation: A stand-alone type can be more easily documented in the Schema DSL. This may be helpful where there are additional rules that surround a type that are not expressible in the DSL but readers of the Schema may need to be aware of. You will find a lot of such typedefs in the <a href="/specs/schemas/schema-schema.ipldsch">schema-schema</a>.</li> <li>Highlighting re-use: Where the re-use of a particular Schema kind is noteworthy, naming it may help in expressing intent.</li> <li>Codegen: the use of named types will have implications for codegen tools. It may be desirable for code generated from a Schema to have recognizable type names in certain positions.</li> </ul> <h2 id="links" tabindex="-1"><a class="header-anchor" href="#links">Links</a></h2> <p>Links in IPLD Schemas are a special-case. The Data Model kind "Link" is expressed by a token prefixed with the <code>&</code> character. The remainder of the token should be <code>Any</code> or the name of a type.</p> <p>Links can be typedef'd, <code>type Foo &Bar</code> or can appear inline: <code>type Baz {String:&Bang}</code>.</p> <p>Further, the type name is not a strict assertion that can be directly tested against underlying data, it is simply a hint regarding what should be found when following the link identified by the <a href="https://github.com/ipld/specs/blob/master/block-layer/CID.md">CID</a> at the position indicated by the Schema link. Strict assertions of this expected type may be applied at layers above the Schema validation layer when the link is resolved and the node decoded.</p> <p>For more information about Links in Schemas, see <a href="/docs/schemas/features/links/">Links in IPLD Schemas</a>.</p> <h2 id="inline-recursive-types" tabindex="-1"><a class="header-anchor" href="#inline-recursive-types">Inline Recursive Types</a></h2> <p>The scalar types (Boolean, Integer, Float, String, Bytes, Link) may appear inline or be typedef'd. In addition, both Map and Link types may appear both inline and as their own type. The additional Schema kinds (Struct, Enum, Union, Copy) do not have an inline variant.</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">IntList</span></span> <span class="token punctuation">[</span>Int<span class="token punctuation">]</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">MapOfIntLists</span></span> <span class="token punctuation">{</span>String<span class="token punctuation">:</span>IntList<span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> id Int data MapOfIntLists <span class="token punctuation">}</span></code></pre> <p>is equivalent to:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> id Int data <span class="token punctuation">{</span>String<span class="token punctuation">:</span><span class="token punctuation">[</span>Int<span class="token punctuation">]</span><span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>As with typedef'd scalar kinds, this has implications for codegen and other API interactions with Schema types. Rather than having a explicit names, <code>MapOfIntLists</code> and <code>IntList</code>, auto-generated names may be applied to <code>Foo->data</code> and the type of the List nodes found within that Map. (e.g. perhaps <code>Foo__dataType</code>, <code>Foo__data__valueType</code>).</p> <p>The inline facility is provided for convenience but explicitness is always recommended above expedience, including this case, in order to improve the documentation role of Schemas. By naming Map and List elements the author can express intent to the user and provide clarity through Schema-consuming tools.</p> <h2 id="representations" tabindex="-1"><a class="header-anchor" href="#representations">Representations</a></h2> <p>The concept of "representations" is a key component of IPLD Schemas and should be understood in order to create and read effective IPLD Schemas.</p> <p>In the Data Model there are only 9 kinds (Null, Boolean, Integer, Float, String, Bytes, List, Map & Link). The Schema layer adds 4 more (Union, Struct, Enum & Copy). These aren't present at the Data Model and are opaque to serialization formats. Instead, they must be "represented" as a base Data Model kind. Each data type at the Schema layer, therefore, has a "representation kind". Scalar kinds are represented as the same kind at the Data Model layer (except in the case of Advanced Data Layouts, see below).</p> <p>A Struct is represented as a Map by default when serialized and deserialized. The Struct adds the ability to apply additional constraints about the keys, the types found when consuming the value nodes of the Map, whether certain keys must be present and what to do when they aren't present. Enums also have a default representation; when one is not specified, they are assumed to be represented as Strings when serialized or deserialized, but with constraints about valid strings for the node(s) where the Enum appears.</p> <p>A Copy type is a special case, it copies all properties of the copied type other than its name, including the representation.</p> <p>Unions don't have a default representation as they express a concept that is commonly represented in a number of ways, so a representation must be supplied when defining a Union type.</p> <p>Some Schema kinds have alternative representation "strategies" that dictate how a type is to be represented in serialized form. Most of these strategies change the representation kind of the type but some retain the same kind and simply alter how the type is encoded within that kind. The <code>stringjoin</code> and <code>stringpairs</code> representation strategies that can be used for Struct types both change the representation kind for a Struct from the default Map to a String. The method for encoding to a single String is different for both. A <code>stringjoin</code> strategy appends the fields in order separated by a delimiter (e.g. <code>"v1,v2"</code>) while a <code>stringpairs</code> strategy include the field names, requiring a field delimited as well as an entry delimited (e.g. <code>"f1=v1,f2=v2"</code>). Similarly, the <code>listpairs</code> and <code>tuple</code> Struct representations both use a List representation kind but use different strategies to encode within a List.</p> <p>To specify a type's representation, the keyword <code>representation</code> is supplied after the main type definition and is followed by a representation strategy name valid for that type.</p> <p>For example, consider this Struct:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> fieldOne <span class="token keyword">nullable</span> String fieldTwo Bool <span class="token punctuation">}</span></code></pre> <p>We could decode the following JSON (using the DAG-JSON codec) into a <code>Foo</code> type:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"fieldOne"</span><span class="token operator">:</span> <span class="token string">"This is field one of Foo"</span><span class="token punctuation">,</span> <span class="token property">"fieldTwo"</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span></code></pre> <p>A Struct can also have the default representation expressed explicitly:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> fieldOne <span class="token keyword">nullable</span> String fieldTwo Bool <span class="token representation">} <span class="token builtin">representation</span></span> map</code></pre> <p>These two descriptors of <code>Foo</code> are identical when parsed as the <code>representation map</code> is implicit for Structs when a representation is not supplied.</p> <p>The Struct can also be represented as a List when we supply the <code>tuple</code> representation strategy:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> fieldOne <span class="token keyword">nullable</span> String fieldTwo Bool <span class="token representation">} <span class="token builtin">representation</span></span> tuple</code></pre> <p>When encountering a Map at the Data Layer where this variant of <code>Foo</code> is expected, an error or failed-validation would occur. Instead, the data for this Struct is a simple List of two elements, the first one a String and the second a Bool. In JSON this may look like:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">[</span> <span class="token string">"This is field one of Foo"</span><span class="token punctuation">,</span> <span class="token boolean">false</span> <span class="token punctuation">]</span></code></pre> <p>A full list of the available representation strategies and their kinds that can be supplied for various Schema kinds can be found in <a href="/docs/schemas/features/representation-strategies/">Representations Strategies</a>.</p> <h3 id="representation-parameters" tabindex="-1"><a class="header-anchor" href="#representation-parameters">Representation Parameters</a></h3> <p>Some representation strategies have additional parameters that can be supplied and some have required parameters that are required in order to properly shape the type representation. There are two methods that representation parameters are supplied: within the <code>representation</code> block for general parameters and inline adjacent to type fields in parens where representation parameters are specific to fields.</p> <h4 id="general-representation-parameters" tabindex="-1"><a class="header-anchor" href="#general-representation-parameters">General Representation Parameters</a></h4> <p>Our <code>Foo</code> struct with a <code>tuple</code> representation may be serialized in an alternate field order by supplying the general <code>fieldOrder</code> parameter:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> fieldOne <span class="token keyword">nullable</span> String fieldTwo Bool <span class="token representation">} <span class="token builtin">representation</span></span> tuple <span class="token punctuation">{</span> fieldOrder <span class="token punctuation">[</span><span class="token string">"fieldTwo"</span>, <span class="token string">"fieldOne"</span><span class="token punctuation">]</span> <span class="token punctuation">}</span></code></pre> <p>Serialization of such a type in JSON may appear as:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">[</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token string">"This is field one of Foo"</span> <span class="token punctuation">]</span></code></pre> <p>The <code>stringjoin</code> representation for Structs has a required parameter, <code>join</code>. There is no default for this parameter, so a Schema specifying a <code>stringjoin</code> Struct without it is invalid:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> fieldOne <span class="token keyword">nullable</span> String fieldTwo Bool <span class="token representation">} <span class="token builtin">representation</span></span> stringjoin <span class="token punctuation">{</span> join <span class="token string">":"</span> <span class="token punctuation">}</span></code></pre> <p>This representation for <code>Foo</code> would serialize into a single String node:</p> <pre class="language-json"><code class="language-json"><span class="token string">"This is field one of Foo:false"</span></code></pre> <p>This representation for Structs has limitations as there is no escaping mechanism for the join character, so it should be used with caution. Similar restrictions apply to the <code>stringpairs</code> Map representation. See <a href="/docs/schemas/features/representation-strategies/">Representations Strategies</a> for more details on such restrictions.</p> <h4 id="field-specific-representation-parameters" tabindex="-1"><a class="header-anchor" href="#field-specific-representation-parameters">Field-specific Representation Parameters</a></h4> <p>The content in the main <code>type</code> declaration block (between opening <code>{</code> and closing <code>}</code>) is intended to represent the type as a user-facing concept, including the <a href="/docs/schemas/features/typekinds/#understanding-cardinality">cardinality</a> of the fields. However, content in parens (<code>(</code>, <code>)</code>) presented next to individual fields is an exception to this rule. This content is field-specific representation parameters. That is, the parameters presented inside these parens would ordinarily belong below in the <code>representation</code> block because it regards the interaction with the serialized form. It is present next to the fields to primarily avoid the duplication of re-declaring the fields in the <code>representation</code> block.</p> <p>Two common field-specific representation parameters for Structs are <code>implicit</code> and <code>rename</code>:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> fieldOne <span class="token keyword">nullable</span> String <span class="token punctuation">(</span>rename <span class="token string">"one"</span><span class="token punctuation">)</span> fieldTwo Bool <span class="token punctuation">(</span>rename <span class="token string">"two"</span> implicit <span class="token string">"false"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre> <p>A cleaner declaration that separates type declaration from serialized form representation details might present this as:</p> <pre><code># This is not valid IPLD Schema but is presented to illustrate the additional verbosity being avoided type Foo struct { fieldOne nullable String fieldTwo Bool } representation map { fields { fieldOne rename "one" fieldTwo rename "two" implicit "false" } } </code></pre> <p>In our example we can see that <code>nullable</code> is a distinct parameter for the field compared to <code>rename</code> and <code>implicit</code>. This is because <code>nullable</code> impacts the shape of the user-facing API for <code>Foo</code>, whereas <code>rename</code> and <code>implicit</code> only impact the serialization (representation) of <code>Foo</code> so are effectively hidden to the user.</p> <p>See <a href="/docs/schemas/features/typekinds/#value-type-modifiers">Value Type Modifiers</a> for a discussion on such matters as well as the impacts on value cardinality.</p> <p>A <code>rename</code> parameter specifies that at serialization and deserialization, a field has an alternate name than that present in the Schema. An <code>implicit</code> specifies that, when not present in the serialized form, the field should have a certain value.</p> <p>Recall our original serialized form for <code>Foo</code>:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"fieldOne"</span><span class="token operator">:</span> <span class="token string">"This is field one of Foo"</span><span class="token punctuation">,</span> <span class="token property">"fieldTwo"</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span></code></pre> <p>With the <code>rename</code> and <code>implicit</code> parameters above, this same data would be serialized as:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"one"</span><span class="token operator">:</span> <span class="token string">"This is field one of Foo"</span> <span class="token punctuation">}</span></code></pre> <p>See <a href="/docs/schemas/features/typekinds/#fields-with-implicit-values">Fields with Implicit Values</a> for more information on <code>implicit</code>. In the same document you will also find a discussion regarding combining <code>nullable</code>, <code>optional</code> and <code>implicit</code> and the limitations thereof.</p> <p>Whenever a value appears in a representation parameter, it must be quoted, regardless of type. In our example above, <code>implicit "false"</code> quoted a Bool parameter. This will be interpreted appropriately depending on context, in this case it is clear that the type of the quoted value should be a Bool.</p> <p>Another example of field parameters is the <code>int</code> representation for Enums, where the field parameter is mandatory:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Status</span></span> <span class="token builtin">enum</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Nope <span class="token punctuation">(</span><span class="token string">"0"</span><span class="token punctuation">)</span> <span class="token punctuation">|</span> Yep <span class="token punctuation">(</span><span class="token string">"1"</span><span class="token punctuation">)</span> <span class="token punctuation">|</span> Maybe <span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">)</span> <span class="token representation">} <span class="token builtin">representation</span></span> <span class="token keyword">int</span></code></pre> <p>In this case we are mapping Int values at in the serialized form to the three Enum values. Note also that the values are again quoted, but will be interpreted appropriately as integers because the context makes that clear.</p> <h2 id="structs" tabindex="-1"><a class="header-anchor" href="#structs">Structs</a></h2> <p>The basic DSL form of a Struct has the following structure:</p> <pre><code>type TypeName struct { field1Name Field1Type field2Name Field2Type ... etc. } </code></pre> <p>Where <code>TypeName</code> is a unique name for the type and follows the naming rules above. Field names follow the same rules as for type naming except that a lower-case first character is allowed and is encouraged as the conventional form. All fields have a type and the type should be one of the existing implicit Schema types (<code>Int</code>, <code>String</code> etc.) or be present as a named type elsewhere within the document. Field types can be recursive in that they can refer to the parent type, indicating a nested data structure (obviously such a nested data structure must have nullable or optional elements that prevent it from being necessarily infinitely recursive).</p> <p>Structs must always have a body, enclosed by <code>{</code>, <code>}</code>. Fields must new-line delimited and should be indented for clarity.</p> <p>The <code>representation</code> strategy for Structs is <code>map</code> by default, so may be omitted. More details can be found in the feature detail pages about <a href="/docs/schemas/features/representation-strategies/">Representations Strategies</a>.</p> <p>Field representation parameters are presented in parens when present and representations requiring additional general parameters is presented in a separate <code>representation</code> block enclosed by <code>{</code>, <code>}</code>. For example, a Struct with both field representation parameters and general representation parameters:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Foo</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> fieldOne <span class="token keyword">nullable</span> String <span class="token punctuation">(</span>rename <span class="token string">"one"</span><span class="token punctuation">)</span> fieldTwo Bool <span class="token punctuation">(</span>rename <span class="token string">"two"</span> implicit <span class="token string">"false"</span><span class="token punctuation">)</span> <span class="token representation">} <span class="token builtin">representation</span></span> stringpairs <span class="token punctuation">{</span> innerDelim <span class="token string">"="</span> entryDelim <span class="token string">","</span> <span class="token punctuation">}</span></code></pre> <p>Leading to a serialized form such as:</p> <pre class="language-json"><code class="language-json"><span class="token string">"one=This is field one of Foo,two=true"</span></code></pre> <p>More details regarding <code>stringpairs</code> can be found below, and in the feature detail pages about <a href="/docs/schemas/features/representation-strategies/">Representations Strategies</a>.</p> <p>Valid representation strategies for Structs are:</p> <ul> <li><code>map</code></li> <li><code>tuple</code></li> <li><code>stringpairs</code></li> <li><code>stringjoin</code></li> <li><code>listpairs</code></li> </ul> <p>More details about these representation strategies, including the data model kinds the map to, and their various parameters, scan be found in the feature detail pages about <a href="/docs/schemas/features/representation-strategies/">Representations Strategies</a>.</p> <h2 id="enums" tabindex="-1"><a class="header-anchor" href="#enums">Enums</a></h2> <p>Enums are used to indicate a distinct, fixed list of values. Enums in IPLD Schemas have a String representation kind, using the value token as the serialized value by default.</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Status</span></span> <span class="token builtin">enum</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Nope <span class="token punctuation">|</span> Yep <span class="token punctuation">|</span> Maybe <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Response</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> timestamp Int status Status <span class="token punctuation">}</span></code></pre> <p>In this example, where <code>Status</code> is used, as the <code>status</code> field in the <code>Response</code> Struct, we expect to find a String in the serialized form that is one of <code>"Nope"</code>, <code>"Yep"</code> or <code>"Maybe"</code>. This string value is not presented via an API interacting via this Schema, rather, the special tokens <code>Nope</code>, <code>Yep</code> and <code>Maybe</code> may be used instead. Codegen would present these values as distinct types that can be passed to a struct / class implementing <code>Response</code> when interacting with the <code>status</code> field.</p> <p>The serialized strings may be different from values:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Status</span></span> <span class="token builtin">enum</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Nope <span class="token punctuation">(</span><span class="token string">"Nay"</span><span class="token punctuation">)</span> <span class="token punctuation">|</span> Yep <span class="token punctuation">(</span><span class="token string">"Yay"</span><span class="token punctuation">)</span> <span class="token punctuation">|</span> Maybe <span class="token punctuation">}</span></code></pre> <p>Creating a differential between the Strings at the Data Model layer and the tokens that an API may use at the Schema layer.</p> <p>An alternate representation strategy for Enums may be specified: <code>int</code>. With an <code>int</code> representation strategy, the values are serialized and deserialized as Data Model Ints but the Enum value tokens are presented at the Schema Layer:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Status</span></span> <span class="token builtin">enum</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Nope <span class="token punctuation">(</span><span class="token string">"0"</span><span class="token punctuation">)</span> <span class="token punctuation">|</span> Yep <span class="token punctuation">(</span><span class="token string">"1"</span><span class="token punctuation">)</span> <span class="token punctuation">|</span> Maybe <span class="token punctuation">(</span><span class="token string">"100"</span><span class="token punctuation">)</span> <span class="token representation">} <span class="token builtin">representation</span></span> <span class="token keyword">int</span></code></pre> <p>Note again that the Int values are quoted in the field representation parens, they will be interpreted and validated as integers when parsing as the context of an <code>int</code> representation strategy makes this clear.</p> <p>More details can be found in the feature detail pages about <a href="/docs/schemas/features/representation-strategies/">Representations Strategies</a>.</p> <h2 id="unions" tabindex="-1"><a class="header-anchor" href="#unions">Unions</a></h2> <h3 id="introduction-to-unions-kinded-unions" tabindex="-1"><a class="header-anchor" href="#introduction-to-unions-kinded-unions">Introduction to Unions: Kinded Unions</a></h3> <p>IPLD Schema Unions describe various means for nodes that may be one of a number of kinds or forms. Consider a node that contains the following data, perhaps as part of a signalling protocol:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Something bad happened"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token string">"ERROR"</span> <span class="token punctuation">}</span></code></pre> <p>And an alternative form that is also acceptable but signals a different state and meaning:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"All good"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"percent"</span><span class="token operator">:</span> <span class="token number">0.6</span><span class="token punctuation">,</span> <span class="token property">"last"</span><span class="token operator">:</span> <span class="token string">"61626378797a"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>In this example, we have a Map that can be represented as a Struct since it has only two fields, but the <code>payload</code> field doesn't have a stable kind so we can't use any of the existing Schema types to represent the field type. Instead, we can introduce a Union and can take different forms depending on the different acceptable forms.</p> <p>IPLD Schemas are intended to be efficient, so the ability to discriminate on Union types is limited to what we can find <em>at the current node</em>. That is, we can't inspect whether a node has a child that takes a particular form and use that as a discriminator (such as inspecting the keys or values of a Map). A Schema must be able to fail validation at a node being inspected where the data does not match the expected form.</p> <p>In our example, the discriminator for type found at <code>payload</code> is the <em>kind</em> of node present. It is either a String kind of a Map kind. We can make an immediate determination of type based on this piece of information.</p> <p>Our Schema for this data could be written as:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Message</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> msg String payload Payload <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Payload</span></span> <span class="token builtin">union</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Error <span class="token keyword">string</span> <span class="token punctuation">|</span> Progress map <span class="token representation">} <span class="token builtin">representation</span></span> kinded <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Error</span></span> <span class="token keyword">string</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Progress</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> percent Float last String <span class="token punctuation">}</span></code></pre> <p>Our <code>Payload</code> Union can be read as "one of <code>Error</code> or <code>Progress</code>" and could have additional elements if there are different forms that a <code>"payload"</code> could take. All Unions require a representation strategy to be stated, there is no default strategy. In this case we are specifying the <code>kinded</code> strategy, so we are opting to discriminate the type by inspecting the kind present at the data model layer. If we find a String at the data model layer then we can safely assume it is an <code>Error</code>. If we find a Map then we assume it's a <code>Progress</code> type but we have to proceed to validate it against <code>Progress</code> and check whether the Map has the required two elements, but at this point the validation job of <code>Payload</code> is done, it only needs to check for the presence of String or Map.</p> <h3 id="limitations-of-union-discrimination" tabindex="-1"><a class="header-anchor" href="#limitations-of-union-discrimination">Limitations of Union Discrimination</a></h3> <p>Authoring Unions in IPLD Schemas help expose some of the limitations of quickly validating data that is allowed to vary. If we extend our example and introduce another acceptable form of <code>"payload"</code> we can see how this ability to quickly discriminate breaks down and introduces the need to do child-contents checking to discriminate:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Ping"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"ts"</span><span class="token operator">:</span> <span class="token number">1572935564043</span><span class="token punctuation">,</span> <span class="token property">"nonce"</span><span class="token operator">:</span> <span class="token string">"424f524b"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>We've introduced a new message type but lost the ability to discriminate based in kind as our new type is also a Map. A Schema that accommodates for this additional payload type is possible but forces the burden of discrimination and onto the consumer of the data as well as some additional validation burden:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Message</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> msg String payload Payload <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Payload</span></span> <span class="token builtin">union</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Error <span class="token keyword">string</span> <span class="token punctuation">|</span> ProgressOrPing map <span class="token representation">} <span class="token builtin">representation</span></span> kinded <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Error</span></span> <span class="token keyword">string</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">ProgressOrPing</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> percent <span class="token keyword">optional</span> Float last <span class="token keyword">optional</span> String ts <span class="token keyword">optional</span> Int nonce <span class="token keyword">optional</span> String <span class="token punctuation">}</span></code></pre> <p>Now the user of such a Schema must do their own field inspection to determine whether a <code>ProgressOrPing</code> is a progress message or a ping. Additionally, the burden of ensuring that both <code>percent</code> and <code>last</code> are present <em>or</em> <code>ts</code> and <code>nonce</code> are present is left to the user, the Schema layer can't help here. The trade-off present in this scenario regards validation of a node by inspection of its child nodes. This type of data is common in the real world but IPLD Schemas encourage better data shape design to allow for fast validation through clear discrimination where such variance exists.</p> <h3 id="alternative-discrimination-strategies" tabindex="-1"><a class="header-anchor" href="#alternative-discrimination-strategies">Alternative Discrimination Strategies</a></h3> <p>If we are designing the data layout for our example protocol (rather than consuming something we have no control over the design of), we could choose a alternate strategy that would allow more efficient discrimination. Unions allow for five different representation strategies that allow for different kinds of discrimination.</p> <h4 id="keyed" tabindex="-1"><a class="header-anchor" href="#keyed">Keyed</a></h4> <p>By making our <code>"payload"</code> object contain a specific key that discriminates the type of the payload, we could use a <code>keyed</code> Union:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Something bad happened"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"error"</span><span class="token operator">:</span> <span class="token string">"ERROR"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"All good"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"progress"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"percent"</span><span class="token operator">:</span> <span class="token number">0.6</span><span class="token punctuation">,</span> <span class="token property">"last"</span><span class="token operator">:</span> <span class="token string">"61626378797a"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Ping"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"ping"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"ts"</span><span class="token operator">:</span> <span class="token number">1572935564043</span><span class="token punctuation">,</span> <span class="token property">"nonce"</span><span class="token operator">:</span> <span class="token string">"424f524b"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>We can now easily handle this data with the following Schema:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Message</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> msg String payload Payload <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Payload</span></span> <span class="token builtin">union</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Error <span class="token string">"error"</span> <span class="token punctuation">|</span> Progress <span class="token string">"progress"</span> <span class="token punctuation">|</span> Ping <span class="token string">"ping"</span> <span class="token representation">} <span class="token builtin">representation</span></span> keyed <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Error</span></span> <span class="token keyword">string</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Progress</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> percent Float last String <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Ping</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> ts Int nonce String <span class="token punctuation">}</span></code></pre> <p>Our <code>Payload</code> union now has the <code>keyed</code> representation strategy. This strategy means the <code>Payload</code> will have a Map representation kind, and that map will be required to have exactly one of the various keys that are used to discriminate the type present. Syntactically in the Schema DSL, <code>Payload</code> now lists quotes string keys next to the types, rather than the kinds of the previous <code>kinded</code> Union -- these are the discriminate values that will be seen in the map.</p> <p>Validation of such data can now check for the presence of each of these keys, <em>exactly one</em> of them exists, and then hand off validation to the expected type at the node found in the valued of that key. If an <code>"error"</code> key is found, it will proceed to validate <code>Error</code> which assumes that the node is a String. If a <code>"progress"</code> key is found, it will proceed to validate that it finds a Map at the value node and that it matches the <code>Progress</code> type, etc.</p> <h4 id="envelope" tabindex="-1"><a class="header-anchor" href="#envelope">Envelope</a></h4> <p>A strategy similar to <code>keyed</code>, but more explicit and allowing for the retention of the <code>"payload"</code> node is the <code>envelope</code> representation strategy. With this strategy we expect that the type will be present as the value of a fixed key of a Map (<code>"payload"</code>), but we can discriminate the type of data to be found by inspecting the value of another key in the Map:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Something bad happened"</span><span class="token punctuation">,</span> <span class="token property">"envelope"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"tag"</span><span class="token operator">:</span> <span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token string">"ERROR"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"All good"</span><span class="token punctuation">,</span> <span class="token property">"envelope"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"tag"</span><span class="token operator">:</span> <span class="token string">"progress"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"percent"</span><span class="token operator">:</span> <span class="token number">0.6</span><span class="token punctuation">,</span> <span class="token property">"last"</span><span class="token operator">:</span> <span class="token string">"61626378797a"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Ping"</span><span class="token punctuation">,</span> <span class="token property">"envelope"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"tag"</span><span class="token operator">:</span> <span class="token string">"ping"</span><span class="token punctuation">,</span> <span class="token property">"payload"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"ts"</span><span class="token operator">:</span> <span class="token number">1572935564043</span><span class="token punctuation">,</span> <span class="token property">"nonce"</span><span class="token operator">:</span> <span class="token string">"424f524b"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>This strategy results in the payload data being in a predictable position in the document, as well as the discriminator value being in a predictable position in the document, but the structure in the payload part of the document varies.</p> <p>Our Schema can now take the following form:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Message</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> msg String envelope Payload <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Payload</span></span> <span class="token builtin">union</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Error <span class="token string">"error"</span> <span class="token punctuation">|</span> Progress <span class="token string">"progress"</span> <span class="token punctuation">|</span> Ping <span class="token string">"ping"</span> <span class="token representation">} <span class="token builtin">representation</span></span> envelope <span class="token punctuation">{</span> discriminantKey <span class="token string">"tag"</span> contentKey <span class="token string">"payload"</span> <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Error</span></span> <span class="token keyword">string</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Progress</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> percent Float last String <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Ping</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> ts Int nonce String <span class="token punctuation">}</span></code></pre> <p>This <code>envelope</code> representation strategy requires the parameters <code>discriminantKey</code> and <code>contentKey</code>. The <code>discriminantKey</code> tells the Schema the key of the discriminator value, while the discriminator values are listed next to the types of the Union (in this case, the same values as we used in the <code>keyed</code> Union example, above).</p> <h4 id="inline" tabindex="-1"><a class="header-anchor" href="#inline">Inline</a></h4> <p>An <code>inline</code> representation strategy <em>pulls up</em> nested structures into the current node rather than navigating down to a child nodes to interpret the constituent type as per the previous Union representation strategies. Discrimination between types use a <code>discriminantKey</code>, also in the current node. This necessarily means that the current node must be a map representation kind and constituent types of a Union must also have map representation kinds.</p> <p>Our example must be extended so that the <code>Error</code> type can be extracted from a map representation:</p> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Something bad happened"</span><span class="token punctuation">,</span> <span class="token property">"union"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"tag"</span><span class="token operator">:</span> <span class="token string">"error"</span><span class="token punctuation">,</span> <span class="token property">"message"</span><span class="token operator">:</span> <span class="token string">"ERROR"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"All good"</span><span class="token punctuation">,</span> <span class="token property">"union"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"tag"</span><span class="token operator">:</span> <span class="token string">"progress"</span><span class="token punctuation">,</span> <span class="token property">"percent"</span><span class="token operator">:</span> <span class="token number">0.6</span><span class="token punctuation">,</span> <span class="token property">"last"</span><span class="token operator">:</span> <span class="token string">"61626378797a"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <pre class="language-json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"msg"</span><span class="token operator">:</span> <span class="token string">"Ping"</span><span class="token punctuation">,</span> <span class="token property">"union"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"tag"</span><span class="token operator">:</span> <span class="token string">"ping"</span><span class="token punctuation">,</span> <span class="token property">"ts"</span><span class="token operator">:</span> <span class="token number">1572935564043</span><span class="token punctuation">,</span> <span class="token property">"nonce"</span><span class="token operator">:</span> <span class="token string">"424f524b"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> <p>For types in the union which are a struct with only one field (like the first example data above), this looks very similar to envelope unions... except notice that there's no <code>contentKey</code> in our union's representation definition -- so the string of the other map key in that example comes from the struct's field name! The behavior of <code>inline</code> union becomes clearer as the contained types get more fields: the tag field is always just <em>next to</em> the other map keys.</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Message</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> msg String union Payload <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Payload</span></span> <span class="token builtin">union</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Error <span class="token string">"error"</span> <span class="token punctuation">|</span> Progress <span class="token string">"progress"</span> <span class="token punctuation">|</span> Ping <span class="token string">"ping"</span> <span class="token representation">} <span class="token builtin">representation</span></span> inline <span class="token punctuation">{</span> discriminantKey <span class="token string">"tag"</span> <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Error</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> message String <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Progress</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> percent Float last String <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Ping</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> ts Int nonce String <span class="token punctuation">}</span></code></pre> <p>The interface presented by this Schema is adjusted in comparison to the previous Unions as <code>Error</code> is now a Struct with a <code>message</code> field.</p> <h3 id="stringprefix-unions-for-strings" tabindex="-1"><a class="header-anchor" href="#stringprefix-unions-for-strings">Stringprefix Unions for Strings</a></h3> <p>A special case union exists for handling String kinds. Where a node contains a string, we may want to different between two different uses of that string at the application layer. Such prefix discriminators are common in configuration option schemes, for example. Stringprefix unions reinterpret a string, stripping out the matched prefix and present the application layer with only the type, and remainder string type, that was matched. Advanced usage may also involve higher-level types that use a string representation strategy being layered on top of a matching type (e.g. a <code>stringjoin</code> map).</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Username</span></span> <span class="token keyword">string</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Credentials</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> credType String credToken String <span class="token representation">} <span class="token builtin">representation</span></span> stringjoin <span class="token punctuation">{</span> join <span class="token string">":"</span> <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Authorization</span></span> <span class="token builtin">union</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> Username <span class="token string">"user:"</span> <span class="token punctuation">|</span> Credentials <span class="token string">"auth:"</span> <span class="token representation">} <span class="token builtin">representation</span></span> stringprefix</code></pre> <p>By declaring a <code>stringprefix</code> union, we specify that the first characters of the string matching the <code>Authorization</code> node will discriminate which <code>type</code> the public key is. Those first characters will be sliced off and expected to be either <code>user:</code> or <code>auth:</code>, then the remainder of the string will be extracted and encapsulated inside either <code>Username</code> or further decoded as the <code>Credentials</code> type (by further splitting it by <code>:</code>) depending on the discriminator prefix.</p> <h3 id="bytesprefix-unions-for-bytes" tabindex="-1"><a class="header-anchor" href="#bytesprefix-unions-for-bytes">Bytesprefix Unions for Bytes</a></h3> <p>A special case union exists for handling Bytes kinds. Where a node contains a byte array (Bytes kind), we may want to discriminate between two different uses of that byte array at the application layer. For example, consider two different encoding schemes where we store a "key" field that is distinct for the each encoding scheme. For practical purposes they are both byte arrays, but at the application layer it helps to have them separated into distinct forms, perhaps so we can make simple assertions about getting the expected key type for the given encoding scheme. There are additional documentation clarity benefits for extracting distinct forms and naming them in a Schema that may factor in to such a decision.</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Authorization</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> key PublicKey keySize Int <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">PublicKey</span></span> <span class="token builtin">union</span> <span class="token punctuation">{</span> <span class="token punctuation">|</span> RsaPubkey <span class="token string">"00"</span> <span class="token punctuation">|</span> Ed25519Pubkey <span class="token string">"01"</span> <span class="token representation">} <span class="token builtin">representation</span></span> bytesprefix <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">RsaPubkey</span></span> <span class="token keyword">bytes</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Ed25519Pubkey</span></span> <span class="token keyword">bytes</span></code></pre> <p>By declaring a <code>bytesprefix</code> union, we specify that the bytes of the byte array found at the <code>key</code> node of <code>Authorization</code> will discriminate which <code>type</code> the public key is. Those first bytes will be expected to be either <code>0x00</code> or <code>0x01</code>, then the remainder of the byte array will be extracted and encapsulated inside either <code>RsaPubkey</code> or <code>Ed25519Pubkey</code> depending on the discriminator byte.</p> <p>Discriminators must be at least one byte long and not conflict. They are represented as properly formed hexadecimal strings, using upper-case characters only.</p> <h2 id="copy" tabindex="-1"><a class="header-anchor" href="#copy">Copy</a></h2> <p>The Copy Schema kind is a special case that provides a mechanism for copying the definition of one named type into a new name. It uses the <code>=</code> token after the new type's name followed by name of the type being copied. It is not possible to copy an unnamed (anonymous) type.</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Ping</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> ts Int nonce String <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Pong</span></span> <span class="token operator">=</span> Ping</code></pre> <p>This example is strictly equivalent to the following in terms of the interaction above the Schema layer:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Ping</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> ts Int nonce String <span class="token punctuation">}</span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">Pong</span></span> <span class="token builtin">struct</span> <span class="token punctuation">{</span> ts Int nonce String <span class="token punctuation">}</span></code></pre> <p>The Schema tooling and the reified form of the Schema retains a <code>copy</code> kind marker, but tooling that consumes Schemas is expected to treat this marker as an indirection to the named type being copied and copy the entirety of that type's definition to the new name.</p> <p>The Copy type is provided for convenience and should also prove beneficial in pointing out relationships between types.</p> <h2 id="advanced-data-layouts" tabindex="-1"><a class="header-anchor" href="#advanced-data-layouts">Advanced Data Layouts</a></h2> <p>Advanced Data Layouts (ADL) are a mechanism for breaking out of Schema processing into custom logic where such logic cannot be expressed in Schemas but where connection with Schema kinds may be beneficial.</p> <p>ADLs are not considered <code>type</code>s in the Schema sense, rather, they masquerade as types, or more specifically, have the ability to masquerade as Schema kinds when used in certain conditions.</p> <p>Declaration of an ADL is similar to declaring a <code>type</code> but only requires a name:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">advanced</span> <span class="token class-name">ROT13</span></span></code></pre> <p>Once declared as an entity in the Schema, the name (<code>ROT13</code> in this case) may be used as a <em>representation</em> elsewhere in the Schema. We do this with <code>representation advanced</code> followed by the name:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">MyString</span></span> <span class="token keyword">string</span> representation advanced ROT13</code></pre> <p>Coupling this <code>type</code> and the <code>advanced</code> definition, we are declaring that there exists above the Schema layer some logic labelled <code>ROT13</code> that is able to interact with the Data Model layer on behalf of <code>MyString</code> and present a standard String kind interface for such a purpose.</p> <p>How the ADL logic is wired in to the Schema tooling will be language and tooling specific. For the purpose of Schema authoring, an <code>advanced</code> definition and usage can be considered as a mechanism to break out of the standard <em>Data-model-to-Schema</em> processing that is performed, and instead, inserting custom logic in that flow for the particular node in question such that it becomes <em>Data-model-to-ADL-to-Schema</em>.</p> <p>The interaction with the Data Model is also left up to the ADL, so it is not limited to consuming a particular node. Rather, it can consume any number of nodes (or no nodes!) and even traverse links in an opaque fashion. Another example of an ADL example provides an example of this. In this case, we declare a sharded Map kind which may be used to scale to Maps of very large size and therefore include multiple, independent, blocks:</p> <pre class="language-ipldsch"><code class="language-ipldsch"><span class="token typedef"><span class="token keyword">advanced</span> <span class="token class-name">ShardedMap</span></span> <span class="token typedef"><span class="token keyword">type</span> <span class="token class-name">MyMap</span></span> <span class="token punctuation">{</span> String <span class="token punctuation">:</span> <span class="token punctuation">&</span>Any <span class="token punctuation">}</span> representation advanced ShardedMap</code></pre> <p>In this case, we declare a <code>MyMap</code> type that is considered a Map kind for the purpose of the rest of the Schema and presents as such above the Schema layer. Meanwhile we have inserted custom logic, labelled <code>ShardedMap</code>, that takes care of the decode/encode and traversal required to present a standard Map kind to the user of such a Schema.</p> <p><strong><code>representation advanced</code> is currently only available for Map, List and Bytes kinds</strong>. Additional use cases (such as the hypothetical String kind above) may be considered in the future.</p> <p>See <a href="/docs/advanced-data-layouts">Advanced Layouts document</a> for more details regarding Advanced Data Layouts, and <a href="/docs/schemas/features/indicating-adls/">Indicating ADLs in Schemas</a> for more details on how to use them when using IPLD Schemas.</p> <h2 id="schemas-in-markdown" tabindex="-1"><a class="header-anchor" href="#schemas-in-markdown">Schemas in Markdown</a></h2> <p>IPLD Schemas are intended to serve a documentation role as well as a programmatic declarative role. In this documentation role, inline comments (<code>#</code>) can be helpful to expand on declarations with explanations, but expanding this documentation form to embedding IPLD Schemas in consumable Markdown is also possible. When embedded in Markdown in code blocks with the right language marker, IPLD Schema tooling can accept Markdown files and extract only those IPLD Schema portions it finds, substituting for a stand-alone Schema file.</p> <p>When embedding IPLD Schema declarations in Markdown, use code blocks with the language marker <code>ipldsch</code>, i.e.:</p> <pre class="language-markdown"><code> ```ipldsch type Foo struct { a Int b Int msg Message } type Message string ``` </code></pre> <p>Any such block found in a Markdown document will be extracted and stitched together to form a single Schema document.</p> <p>Additionally, it is also possible to perform this process across multiple Markdown documents for sufficiently complex Schema declarations. When the IPLD Schema tooling is provided a list of Markdown files it will extract the <code>ipldsch</code> blocks and stitch them all together and assume they comprise a single stand-alone Schema document.</p> </div> </main> </body> </html>