1 line
169 KiB
JSON
1 line
169 KiB
JSON
{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Introduction","text":"<p>Sophia is a functional language designed for smart contract development. It is strongly typed and has restricted mutable state.</p> <p>Sophia is customized for smart contracts, which can be published to a blockchain. Thus some features of conventional languages, such as floating point arithmetic, are not present in Sophia, and some \u00e6ternity blockchain specific primitives, constructions and types have been added.</p> <p>Note</p> <ul> <li>For rapid prototyping of smart contracts check out AEstudio!</li> <li>For playing around and diving deeper into the language itself check out the REPL!</li> </ul>"},{"location":"CHANGELOG/","title":"Changelog","text":"<p>All notable changes to this project will be documented in this file.</p> <p>The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.</p>"},{"location":"CHANGELOG/#unreleased","title":"Unreleased","text":""},{"location":"CHANGELOG/#added","title":"Added","text":""},{"location":"CHANGELOG/#changed","title":"Changed","text":""},{"location":"CHANGELOG/#removed","title":"Removed","text":""},{"location":"CHANGELOG/#fixed","title":"Fixed","text":""},{"location":"CHANGELOG/#730","title":"7.3.0","text":""},{"location":"CHANGELOG/#fixed_1","title":"Fixed","text":"<ul> <li>Fixed a bug with polymorphism that allowed functions with the same name but different type to be considered as implementations for their corresponding interface function.</li> <li>Fixed a bug in the byte code optimization that incorrectly reordered dependent instructions.</li> </ul>"},{"location":"CHANGELOG/#721","title":"7.2.1","text":""},{"location":"CHANGELOG/#fixed_2","title":"Fixed","text":"<ul> <li>Fixed bugs with the newly added debugging symbols</li> </ul>"},{"location":"CHANGELOG/#720","title":"7.2.0","text":""},{"location":"CHANGELOG/#added_1","title":"Added","text":"<ul> <li>Toplevel compile-time constants <pre><code>namespace N =\n let nc = 1\ncontract C =\n let cc = 2\n</code></pre></li> <li>API functions for encoding/decoding Sophia values to/from FATE.</li> </ul>"},{"location":"CHANGELOG/#removed_1","title":"Removed","text":"<ul> <li>Remove the mapping from variables to FATE registers from the compilation output.</li> </ul>"},{"location":"CHANGELOG/#fixed_3","title":"Fixed","text":"<ul> <li>Warning about unused include when there is no include.</li> </ul>"},{"location":"CHANGELOG/#710","title":"7.1.0","text":""},{"location":"CHANGELOG/#added_2","title":"Added","text":"<ul> <li>Options to enable/disable certain optimizations.</li> <li>The ability to call a different instance of the current contract <pre><code>contract Main =\n entrypoint spend(x : int) : int = x\n entrypoint f(c : Main) : int = c.spend(10)\n</code></pre></li> <li>Return a mapping from variables to FATE registers in the compilation output.</li> <li>Hole expression.</li> </ul>"},{"location":"CHANGELOG/#changed_1","title":"Changed","text":"<ul> <li>Type definitions serialised to ACI as <code>typedefs</code> field instead of <code>type_defs</code> to increase compatibility.</li> <li>Check contracts and entrypoints modifiers when implementing interfaces.</li> <li>Contracts can no longer be used as namespaces.</li> <li>Do not show unused stateful warning for functions that call other contracts with a non-zero value argument.</li> </ul>"},{"location":"CHANGELOG/#fixed_4","title":"Fixed","text":"<ul> <li>Typechecker crashes if Chain.create or Chain.clone are used without arguments.</li> </ul>"},{"location":"CHANGELOG/#701","title":"7.0.1","text":""},{"location":"CHANGELOG/#added_3","title":"Added","text":"<ul> <li>Add CONTRIBUTING.md file.</li> </ul>"},{"location":"CHANGELOG/#changed_2","title":"Changed","text":"<ul> <li>Update Sophia syntax docs to include missing information about existing syntax.</li> </ul>"},{"location":"CHANGELOG/#fixed_5","title":"Fixed","text":"<ul> <li>404 Contract polymorphism crashes on non-obvious child contract typing.</li> </ul>"},{"location":"CHANGELOG/#700","title":"7.0.0","text":""},{"location":"CHANGELOG/#added_4","title":"Added","text":"<ul> <li>Added support for <code>EXIT</code> opcode via <code>exit : (string) => 'a</code> function (behaves same as <code>ABORT</code>, but consumes all gas).</li> <li>Compiler warnings for the following: shadowing, negative spends, division by zero, unused functions, unused includes, unused stateful annotations, unused variables, unused parameters, unused user-defined type, dead return value.</li> <li>The pipe operator |> <pre><code>[1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3]))\n</code></pre></li> <li>Allow binary operators to be used as lambdas <pre><code>function sum(l : list(int)) : int = foldl((+), 0, l)\nfunction logical_and(x, y) = (&&)(x, y)\n</code></pre></li> <li>Contract interfaces polymorphism</li> </ul>"},{"location":"CHANGELOG/#changed_3","title":"Changed","text":"<ul> <li>Error messages have been restructured (less newlines) to provide more unified errors. Also <code>pp_oneline/1</code> has been added.</li> <li>Ban empty record definitions (e.g. <code>record r = {}</code> would give an error).</li> </ul>"},{"location":"CHANGELOG/#removed_2","title":"Removed","text":"<ul> <li>Support for AEVM has been entirely wiped</li> </ul>"},{"location":"CHANGELOG/#610-2021-10-20","title":"6.1.0 - 2021-10-20","text":""},{"location":"CHANGELOG/#added_5","title":"Added","text":"<ul> <li><code>Bitwise</code> stdlib</li> <li><code>Set</code> stdlib</li> <li><code>Option.force_msg</code></li> <li>Loading namespaces into the current scope (e.g. <code>using Pair</code>)</li> <li>Assign patterns to variables (e.g. <code>let x::(t = y::_) = [1, 2, 3, 4]</code> where <code>t == [2, 3, 4]</code>)</li> <li>Add builtin types (<code>AENS.name, AENS.pointee, Chain.ttl, Chain.base_tx, Chain.ga_meta_tx, Chain.paying_for_tx</code>) to the calldata and result decoder</li> <li>Patterns guards <pre><code>switch(x)\n a::[] | a > 10 => 1\n _ => 2\n</code></pre> <pre><code>function\n f(a::[]) | a > 10 = 1\n f(_) = 2\n</code></pre></li> </ul>"},{"location":"CHANGELOG/#changed_4","title":"Changed","text":"<ul> <li>Fixed the ACI renderer, it shouldn't drop the <code>stateful</code> modifier</li> </ul>"},{"location":"CHANGELOG/#602-2021-07-05","title":"6.0.2 2021-07-05","text":""},{"location":"CHANGELOG/#changed_5","title":"Changed","text":"<ul> <li><code>List.from_to_step</code> now forbids non-positive step (this change does not alter the behavior of the previously deployed contracts)</li> <li>Fixed leaking state between contracts</li> </ul>"},{"location":"CHANGELOG/#601-2021-06-24","title":"6.0.1 2021-06-24","text":""},{"location":"CHANGELOG/#changed_6","title":"Changed","text":"<ul> <li>Fixed a bug in calldata encoding for contracts containing multiple contracts</li> <li>Fixed a missing <code>include</code> in the <code>Frac</code> standard library</li> </ul>"},{"location":"CHANGELOG/#600-2021-05-26","title":"6.0.0 2021-05-26","text":""},{"location":"CHANGELOG/#added_6","title":"Added","text":"<ul> <li>Child contracts</li> <li><code>Chain.clone</code></li> <li><code>Chain.create</code></li> <li><code>Chain.bytecode_hash</code></li> <li>Minor support for variadic functions</li> <li><code>void</code> type that represents an empty type</li> <li><code>Call.fee</code> builtin</li> </ul>"},{"location":"CHANGELOG/#changed_7","title":"Changed","text":"<ul> <li>Contract interfaces must be now invocated by <code>contract interface</code> keywords</li> <li><code>main</code> keyword to indicate the main contract in case there are child contracts around</li> <li><code>List.sum</code> and <code>List.product</code> no longer use <code>List.foldl</code></li> </ul>"},{"location":"CHANGELOG/#removed_3","title":"Removed","text":""},{"location":"CHANGELOG/#500-2021-04-30","title":"5.0.0 2021-04-30","text":""},{"location":"CHANGELOG/#added_7","title":"Added","text":"<ul> <li>A new and improved <code>String</code> standard library has been added. Use it by <code>include \"String.aes\"</code>. It includes functions for turning strings into lists of characters for detailed manipulation. For example: <pre><code>include \"String.aes\"\ncontract C =\n entrypoint filter_all_a(s: string) : string =\n String.from_list(List.filter((c : char) => c != 'a', String.to_list(s)))\n</code></pre> will return a list with all <code>a</code>'s removed.</li> </ul> <p>There are also convenience functions <code>split</code>, <code>concat</code>, <code>to_upper</code>, <code>to_lower</code>, etc.</p> <p>All String functions in FATEv2 operate on unicode code points. - Operations for pairing-based cryptography has been added the operations are in the standard library BLS12_381. With these operations it is possible to do Zero Knowledge-proofs, etc. The operations are for the BLS12-381 curve (as the name suggests). - Calls to functions in other contracts (i.e. remote calls) can now be <code>protected</code>. If a contract call fails for any reason (for instance, the remote contract crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong type) the parent call also fails. To make it possible to recover from failures, contract calls takes a named argument <code>protected : bool</code> (default <code>false</code>).</p> <p>If <code>protected = true</code> the result of the contract call is wrapped in an <code>option</code>, and <code>Some(value)</code> indicates a succesful execution and <code>None</code> indicates that the contract call failed. Note: any gas consumed until the failure is still charged, but all side effects in the remote contract are rolled back on failure. - A new chain operation <code>AENS.update</code> is supported. - New chain exploring operations <code>AENS.lookup</code> and <code>Oracle.expiry</code> to look up an AENS record and the expiry of an Oracle respectively, are added. - Transaction introspection (<code>Auth.tx</code>) has been added. When a Generalized account is authorized, the authorization function needs access to the transaction (and the transaction hash) for the wrapped transaction. The transaction and the transaction hash is available <code>Auth.tx</code>, it is only available during authentication if invoked by a normal contract call it returns <code>None</code>. Example: <pre><code>switch(Auth.tx)\n None => abort(\"Not in Auth context\")\n Some(tx0) =>\n switch(tx0.tx)\n Chain.SpendTx(_, amount, _) => amount > 400\n Chain.ContractCallTx(_, _) => true\n _ => false\n</code></pre> - A debug mode is a added to the compiler. Right now its only use is to turn off hermetization.</p>"},{"location":"CHANGELOG/#changed_8","title":"Changed","text":"<ul> <li>The function <code>Chain.block_hash(height)</code> is now (in FATEv2) defined for the current height - this used to be an error.</li> <li>Standard library: Sort is optimized to do <code>mergesort</code> and a <code>contains</code> function is added.</li> <li>Improved type errors and explicit errors for some syntax errors (empty code blocks, etc.).</li> <li>Compiler optimization: The ACI is generated alongside bytecode. This means that multiple compiler passes can be avoided.</li> <li>Compiler optimization: Improved parsing (less stack used when transpiled).</li> <li>A bug where constraints were handled out of order fixed.</li> <li>Fixed calldata decoding for singleton records.</li> <li>Improved the documentation w.r.t. signatures, especially stressing the fact that the network ID is a part of what is signed.</li> </ul>"},{"location":"CHANGELOG/#removed_4","title":"Removed","text":""},{"location":"CHANGELOG/#430","title":"4.3.0","text":""},{"location":"CHANGELOG/#added_8","title":"Added","text":"<ul> <li>Added documentation (moved from <code>protocol</code>)</li> <li><code>Frac.aes</code> \u2013 library for rational numbers</li> <li>Added some more meaningful error messages</li> <li>Exported several parsing functionalities</li> <li>With option <code>keep_included</code> it is possible to see which files were included during the parse</li> <li>There is a function <code>run_parser</code> that be used to evaluate any parsing rule</li> <li>Exported parsers: <code>body</code>, <code>type</code> and <code>decl</code></li> </ul>"},{"location":"CHANGELOG/#changed_9","title":"Changed","text":"<ul> <li>Performance improvements in the standard library</li> <li>Fixed ACI encoder to handle <code>-</code> unary operator</li> <li>Fixed including by absolute path</li> <li>Fixed variant type printing in the ACI error messages</li> <li>Fixed pretty printing of combined function clauses</li> </ul>"},{"location":"CHANGELOG/#removed_5","title":"Removed","text":"<ul> <li><code>let</code> definitions are no longer supported in the toplevel of the contract</li> <li>type declarations are no longer supported</li> </ul>"},{"location":"CHANGELOG/#420-2020-01-15","title":"4.2.0 - 2020-01-15","text":""},{"location":"CHANGELOG/#added_9","title":"Added","text":"<ul> <li>Allow separate entrypoint/function type signature and definition, and pattern matching in left-hand sides: <pre><code> function\n length : list('a) => int\n length([]) = 0\n length(x :: xs) = 1 + length(xs)\n</code></pre></li> <li>Allow pattern matching in list comprehension generators (filtering out match failures): <pre><code> function somes(xs : list(option('a))) : list('a) =\n [ x | Some(x) <- xs ]\n</code></pre></li> <li>Allow pattern matching in let-bindings (aborting on match failures): <pre><code> function test(m : map(int, int)) =\n let Some(x) = Map.lookup(m, 0)\n x\n</code></pre></li> </ul>"},{"location":"CHANGELOG/#changed_10","title":"Changed","text":"<ul> <li>FATE code generator improvements.</li> <li>Bug fix: Handle qualified constructors in patterns.</li> <li>Bug fix: Allow switching also on negative numbers.</li> </ul>"},{"location":"CHANGELOG/#removed_6","title":"Removed","text":""},{"location":"CHANGELOG/#410-2019-11-26","title":"4.1.0 - 2019-11-26","text":""},{"location":"CHANGELOG/#added_10","title":"Added","text":"<ul> <li>Support encoding and decoding bit fields in call arguments and results.</li> </ul>"},{"location":"CHANGELOG/#changed_11","title":"Changed","text":"<ul> <li>Various improvements to FATE code generator.</li> </ul>"},{"location":"CHANGELOG/#removed_7","title":"Removed","text":""},{"location":"CHANGELOG/#400-2019-10-11","title":"4.0.0 - 2019-10-11","text":""},{"location":"CHANGELOG/#added_11","title":"Added","text":"<ul> <li><code>Address.to_contract</code> - casts an address to a (any) contract type.</li> <li>Pragma to check compiler version, e.g. <code>@compiler >= 4.0</code>.</li> <li>Handle numeric escapes, i.e. <code>\"\\x19Ethereum Signed Message:\\n\"</code>, and similar strings.</li> <li><code>Bytes.concat</code> and <code>Bytes.split</code> are added to be able to (de-)construct byte arrays.</li> <li><code>[a..b]</code> language construct, returning the list of numbers between <code>a</code> and <code>b</code> (inclusive). Returns the empty list if <code>a</code> > <code>b</code>.</li> <li>Standard libraries</li> <li>Checks that <code>init</code> is not called from other functions.</li> <li>FATE backend - the compiler is able to produce VM code for both <code>AEVM</code> and <code>FATE</code>. Many of the APIs now take <code>{backend, aevm | fate}</code> to decide wich backend to produce artifacts for.</li> <li>New builtin functions <code>Crypto.ecrecover_secp256k1: (hash, bytes(65)) => option(bytes(20))</code> and <code>Crypto.ecverify_secp256k1 : (hash, bytes(20), bytes(65)) => bool</code> for recovering and verifying an Ethereum address for a message hash and a signature.</li> <li>Sophia supports list comprehensions known from languages like Python, Haskell or Erlang. Example syntax: <pre><code>[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]\n// yields [12,13,14,20,21,22,30,31,32]\n</code></pre></li> <li>A new contract, and endpoint, modifier <code>payable</code> is introduced. Contracts, and enpoints, that shall be able to receive funds should be marked as payable. <code>Address.is_payable(a)</code> can be used to check if an (contract) address is payable or not.</li> </ul>"},{"location":"CHANGELOG/#changed_12","title":"Changed","text":"<ul> <li>Nice type error if contract function is called as from a namespace.</li> <li>Fail on function definitions in contracts other than the main contract.</li> <li>Bug fix in variable optimization - don't discard writes to the store/state.</li> <li>Bug fixes in error reporting.</li> <li>Bug fix in variable liveness analysis for FATE.</li> <li>Error messages are changed into a uniform format, and more helpful messages have been added.</li> <li><code>Crypto.<hash_fun></code> and <code>String.<hash_fun></code> for byte arrays now only hash the actual byte array - not the internal ABI format.</li> <li>More strict checks for polymorphic oracles and higher order oracles and entrypoints.</li> <li><code>AENS.claim</code> is updated with a <code>NameFee</code> field - to be able to do name auctions within contracts.</li> <li>Fixed a bug in <code>Bytes.to_str</code> for AEVM.</li> <li>New syntax for tuple types. Now 0-tuple type is encoded as <code>unit</code> instead of <code>()</code> and regular tuples are encoded by interspersing inner types with <code>*</code>, for instance <code>int * string</code>. Parens are not necessary. Note it only affects the types, values remain as their were before, so <code>(1, \"a\") : int * string</code></li> <li>The <code>AENS.transfer</code> and <code>AENS.revoke</code> functions have been updated to take a name <code>string</code> instead of a name <code>hash</code>.</li> <li>Fixed a bug where the <code>AEVM</code> backend complained about a missing <code>init</code> function when trying to generate calldata from an ACI-generated interface.</li> <li>Compiler now returns the ABI-version in the compiler result map.</li> <li>Renamed <code>Crypto.ecverify</code> and <code>Crypto.ecverify_secp256k1</code> into <code>Crypto.verify_sig</code> and <code>Crypto.verify_sig_secp256k1</code> respectively.</li> </ul>"},{"location":"CHANGELOG/#removed_8","title":"Removed","text":""},{"location":"CHANGELOG/#320-2019-06-28","title":"3.2.0 - 2019-06-28","text":""},{"location":"CHANGELOG/#added_12","title":"Added","text":"<ul> <li>New builtin function <code>require : (bool, string) => ()</code>. Defined as <pre><code>function require(b, err) = if(!b) abort(err)\n</code></pre></li> <li>New builtin functions <pre><code>Bytes.to_str : bytes(_) => string\nBytes.to_int : bytes(_) => int\n</code></pre> for converting a byte array to a hex string and interpreting it as a big-endian encoded integer respectively.</li> </ul>"},{"location":"CHANGELOG/#changed_13","title":"Changed","text":"<ul> <li>Public contract functions must now be declared as entrypoints: <pre><code>contract Example =\n // Exported\n entrypoint exported_fun(x) = local_fun(x)\n // Not exported\n function local_fun(x) = x\n</code></pre> Functions in namespaces still use <code>function</code> (and <code>private function</code> for private functions).</li> <li>The return type of <code>Chain.block_hash(height)</code> has changed, it used to be <code>int</code>, where <code>0</code> denoted an incorrect height. New return type is <code>option(hash)</code>, where <code>None</code> represents an incorrect height.</li> <li>Event name hashes now use BLAKE2b instead of Keccak256.</li> <li>Fixed bugs when defining record types in namespaces.</li> <li>Fixed a bug in include path handling when passing options to the compiler.</li> </ul>"},{"location":"CHANGELOG/#removed_9","title":"Removed","text":""},{"location":"CHANGELOG/#310-2019-06-03","title":"3.1.0 - 2019-06-03","text":""},{"location":"CHANGELOG/#added_13","title":"Added","text":""},{"location":"CHANGELOG/#changed_14","title":"Changed","text":"<ul> <li>Keyword <code>indexed</code> is now optional for word typed (<code>bool</code>, <code>int</code>, <code>address</code>, ...) event arguments.</li> <li>State variable pretty printing now produce <code>'a, 'b, ...</code> instead of <code>'1, '2, ...</code>.</li> <li>ACI is restructured and improved:<ul> <li><code>state</code> and <code>event</code> types (if present) now appear at the top level.</li> <li>Namespaces and remote interfaces are no longer ignored.</li> <li>All type definitions are included in the interface rendering.</li> <li>API functions are renamed, new functions are <code>contract_interface</code> and <code>render_aci_json</code>.</li> </ul> </li> <li>Fixed a bug in <code>create_calldata</code>/<code>to_sophia_value</code> - it can now handle negative literals.</li> </ul>"},{"location":"CHANGELOG/#removed_10","title":"Removed","text":""},{"location":"CHANGELOG/#300-2019-05-21","title":"3.0.0 - 2019-05-21","text":""},{"location":"CHANGELOG/#added_14","title":"Added","text":"<ul> <li><code>stateful</code> annotations are now properly enforced. Functions must be marked stateful in order to update the state or spend tokens.</li> <li>Primitives <code>Contract.creator</code>, <code>Address.is_contract</code>, <code>Address.is_oracle</code>, <code>Oracle.check</code> and <code>Oracle.check_query</code> has been added to Sophia.</li> <li>A byte array type <code>bytes(N)</code> has been added to generalize <code>hash (== bytes(32))</code> and <code>signature (== bytes(64))</code> and allow for byte arrays of arbitrary fixed length.</li> <li><code>Crypto.ecverify_secp256k1</code> has been added.</li> </ul>"},{"location":"CHANGELOG/#changed_15","title":"Changed","text":"<ul> <li>Address literals (+ Oracle, Oracle query and remote contracts) have been changed from <code>#<hex></code> to address as <code>ak_<base58check></code>, oracle <code>ok_<base58check></code>, oracle query <code>oq_<base58check></code> and remote contract <code>ct_<base58check></code>.</li> <li>The compilation and typechecking of <code>letfun</code> (e.g. <code>let m(f, xs) = map(f, xs)</code>) was not working properly and has been fixed.</li> </ul>"},{"location":"CHANGELOG/#removed_11","title":"Removed","text":"<ul> <li><code>let rec</code> has been removed from the language, it has never worked.</li> <li>The standalone CLI compiler is served in the repo <code>aeternity/aesophia_cli</code> and has been completely removed from <code>aesophia</code>.</li> </ul>"},{"location":"CHANGELOG/#210-2019-04-11","title":"2.1.0 - 2019-04-11","text":""},{"location":"CHANGELOG/#added_15","title":"Added","text":"<ul> <li>Stubs (not yet wired up) for compilation to FATE</li> <li>Add functions specific for Calldata decoding</li> <li>Support for <code>Auth.tx_hash</code>, not available in AEVM until Fortuna release</li> </ul>"},{"location":"CHANGELOG/#changed_16","title":"Changed","text":"<ul> <li>Improvements to the ACI generator</li> </ul>"},{"location":"CHANGELOG/#200-2019-03-11","title":"2.0.0 - 2019-03-11","text":""},{"location":"CHANGELOG/#added_16","title":"Added","text":"<ul> <li>Add <code>Crypto.ecverify</code> to the compiler.</li> <li>Add <code>Crypto.sha3</code>, <code>Crypto.blake2</code>, <code>Crypto.sha256</code>, <code>String.blake2</code> and <code>String.sha256</code> to the compiler.</li> <li>Add the <code>bits</code> type for working with bit fields in Sophia.</li> <li>Add Namespaces to Sophia in order to simplify using library contracts, etc.</li> <li>Add a missig type check on the <code>init</code> function - detects programmer errors earlier.</li> <li>Add the ACI (Aeternity Contract Interface) generator.</li> </ul>"},{"location":"CHANGELOG/#changed_17","title":"Changed","text":"<ul> <li>Use native bit shift operations in builtin functions, reducing gas cost.</li> <li>Improve type checking of <code>record</code> fields - generates more understandable error messages.</li> <li>Improved, more coherent, error messages.</li> <li>Simplify calldata creation - instead of passing a compiled contract, simply pass a (stubbed) contract string.</li> </ul>"},{"location":"aeso_aci/","title":"aeso_aci","text":""},{"location":"aeso_aci/#module","title":"Module","text":""},{"location":"aeso_aci/#aeso_aci_1","title":"aeso_aci","text":"<p>The ACI interface encoder and decoder.</p>"},{"location":"aeso_aci/#description","title":"Description","text":"<p>This module provides an interface to generate and convert between Sophia contracts and a suitable JSON encoding of contract interface. As yet the interface is very basic.</p> <p>Encoding this contract:</p> <pre><code>contract Answers =\n record state = { a : answers }\n type answers() = map(string, int)\n\n stateful function init() = { a = {} }\n private function the_answer() = 42\n function new_answer(q : string, a : int) : answers() = { [q] = a }\n</code></pre> <p>generates the following JSON structure representing the contract interface:</p> <pre><code>{\n\"contract\": {\n\"functions\": [\n{\n\"arguments\": [],\n\"name\": \"init\",\n\"returns\": \"Answers.state\",\n\"stateful\": true\n},\n{\n\"arguments\": [\n{\n\"name\": \"q\",\n\"type\": \"string\"\n},\n{\n\"name\": \"a\",\n\"type\": \"int\"\n}\n],\n\"name\": \"new_answer\",\n\"returns\": {\n\"map\": [\n\"string\",\n\"int\"\n]\n},\n\"stateful\": false\n}\n],\n\"name\": \"Answers\",\n\"state\": {\n\"record\": [\n{\n\"name\": \"a\",\n\"type\": \"Answers.answers\"\n}\n]\n},\n\"typedefs\": [\n{\n\"name\": \"answers\",\n\"typedef\": {\n\"map\": [\n\"string\",\n\"int\"\n]\n},\n\"vars\": []\n}\n]\n}\n}\n</code></pre> <p>When that encoding is decoded the following include definition is generated:</p> <pre><code>contract Answers =\n record state = {a : Answers.answers}\n type answers = map(string, int)\n function init : () => Answers.state\n function new_answer : (string, int) => map(string, int)\n</code></pre>"},{"location":"aeso_aci/#types","title":"Types","text":"<pre><code>-type aci_type() :: json | string.\n-type json() :: jsx:json_term().\n-type json_text() :: binary().\n</code></pre>"},{"location":"aeso_aci/#exports","title":"Exports","text":""},{"location":"aeso_aci/#contract_interfaceaci_type-string-ok-json-string-error-term","title":"contract_interface(aci_type(), string()) -> {ok, json() | string()} | {error, term()}","text":"<p>Generate the JSON encoding of the interface to a contract. The type definitions and non-private functions are included in the JSON string.</p>"},{"location":"aeso_aci/#render_aci_jsonjson-json_text-string","title":"render_aci_json(json() | json_text()) -> string().","text":"<p>Take a JSON encoding of a contract interface and generate a contract interface that can be included in another contract.</p>"},{"location":"aeso_aci/#example-run","title":"Example run","text":"<p>This is an example of using the ACI generator from an Erlang shell. The file called <code>aci_test.aes</code> contains the contract in the description from which we want to generate files <code>aci_test.json</code> which is the JSON encoding of the contract interface and <code>aci_test.include</code> which is the contract definition to be included inside another contract.</p> <pre><code>1> {ok,Contract} = file:read_file(\"aci_test.aes\").\n{ok,<<\"contract Answers =\\n record state = { a : answers }\\n type answers() = map(string, int)\\n\\n stateful function\"...>>}\n2> {ok,JsonACI} = aeso_aci:contract_interface(json, Contract).\n{ok,[#{contract =>\n#{functions =>\n[#{arguments => [],name => <<\"init\">>,\nreturns => <<\"Answers.state\">>,stateful => true},\n#{arguments =>\n[#{name => <<\"q\">>,type => <<\"string\">>},\n#{name => <<\"a\">>,type => <<\"int\">>}],\nname => <<\"new_answer\">>,\nreturns => #{<<\"map\">> => [<<\"string\">>,<<\"int\">>]},\nstateful => false}],\nname => <<\"Answers\">>,\nstate =>\n#{record =>\n[#{name => <<\"a\">>,type => <<\"Answers.answers\">>}]},\ntypedefs =>\n[#{name => <<\"answers\">>,\ntypedef => #{<<\"map\">> => [<<\"string\">>,<<\"int\">>]},\nvars => []}]}}]}\n3> file:write_file(\"aci_test.aci\", jsx:encode(JsonACI)).\nok\n4> {ok,InterfaceStub} = aeso_aci:render_aci_json(JsonACI).\n{ok,<<\"contract Answers =\\n record state = {a : Answers.answers}\\n type answers = map(string, int)\\n function init \"...>>}\n5> file:write_file(\"aci_test.include\", InterfaceStub).\nok\n6> jsx:prettify(jsx:encode(JsonACI)).\n<<\"[\\n {\\n \\\"contract\\\": {\\n \\\"functions\\\": [\\n {\\n \\\"arguments\\\": [],\\n \\\"name\\\": \\\"init\\\",\\n \"...>>\n</code></pre> <p>The final call to <code>jsx:prettify(jsx:encode(JsonACI))</code> returns the encoding in a more easily readable form. This is what is shown in the description above.</p>"},{"location":"aeso_compiler/","title":"aeso_compiler","text":""},{"location":"aeso_compiler/#module","title":"Module","text":""},{"location":"aeso_compiler/#aeso_compiler_1","title":"aeso_compiler","text":"<p>The Sophia compiler</p>"},{"location":"aeso_compiler/#description","title":"Description","text":"<p>This module provides the interface to the standard Sophia compiler. It returns the compiled module in a map which can then be loaded.</p>"},{"location":"aeso_compiler/#types","title":"Types","text":"<pre><code>contract_string() = string() | binary()\ncontract_map() = #{bytecode => binary(),\ncompiler_version => binary(),\ncontract_souce => string(),\ntype_info => type_info()}\ntype_info()\nerrorstring() = binary()\n</code></pre>"},{"location":"aeso_compiler/#exports","title":"Exports","text":""},{"location":"aeso_compiler/#filefile","title":"file(File)","text":""},{"location":"aeso_compiler/#filefile-options-compret","title":"file(File, Options) -> CompRet","text":""},{"location":"aeso_compiler/#from_stringcontractstring-options-compret","title":"from_string(ContractString, Options) -> CompRet","text":"<p>Types</p> <pre><code>ContractString = contract_string()\nOptions = [Option]\nCompRet = {ok,ContractMap} | {error,ErrorString}\nContractMap = contract_map()\nErrorString = errorstring()\n</code></pre> <p>Compile a contract defined in a file or in a string.</p> <p>The pp_ options all print to standard output the following:</p> <p><code>pp_sophia_code</code> - print the input Sophia code.</p> <p><code>pp_ast</code> - print the AST of the code</p> <p><code>pp_types</code> - print information about the types</p> <p><code>pp_typed_ast</code> - print the AST with type information at each node</p> <p><code>pp_assembler</code> - print the generated assembler code</p> <p>The option <code>include_child_contract_symbols</code> includes the symbols of child contracts functions in the generated fate code. It is turned off by default to avoid making contracts bigger on chain.</p>"},{"location":"aeso_compiler/#options-to-control-which-compiler-optimizations-should-run","title":"Options to control which compiler optimizations should run:","text":"<p>By default all optimizations are turned on, to disable an optimization, it should be explicitly set to false and passed as a compiler option.</p> <p>List of optimizations:</p> <ul> <li>optimize_inliner</li> <li>optimize_inline_local_functions</li> <li>optimize_bind_subexpressions</li> <li>optimize_let_floating</li> <li>optimize_simplifier</li> <li>optimize_drop_unused_lets</li> <li>optimize_push_consume</li> <li>optimize_one_shot_var</li> <li>optimize_write_to_dead_var</li> <li>optimize_inline_switch_target</li> <li>optimize_swap_push</li> <li>optimize_swap_pop</li> <li>optimize_swap_write</li> <li>optimize_constant_propagation</li> <li>optimize_prune_impossible_branches</li> <li>optimize_single_successful_branch</li> <li>optimize_inline_store</li> <li>optimize_float_switch_bod</li> </ul>"},{"location":"aeso_compiler/#check_callcontractstring-options-checkret","title":"check_call(ContractString, Options) -> CheckRet","text":"<p>Types <pre><code>ContractString = string() | binary()\nCheckRet = {ok,string(),{Types,Type | any()},Terms} | {error,Term}\nTypes = [Type]\nType = term()\n</code></pre> Check a call in contract through the <code>__call</code> function.</p>"},{"location":"aeso_compiler/#version-ok-version-error-term","title":"version() -> {ok, Version} | {error, term()}","text":"<p>Types</p> <pre><code>Version = binary()\n</code></pre> <p>Get the current version of the Sophia compiler.</p>"},{"location":"sophia/","title":"Sophia","text":"<p>This file has been moved here</p>"},{"location":"sophia_examples/","title":"Contract examples","text":""},{"location":"sophia_examples/#crowdfunding","title":"Crowdfunding","text":"<pre><code>/*\n * A simple crowd-funding example\n */\ncontract FundMe =\n\nrecord spend_args = { recipient : address,\namount : int }\n\nrecord state = { contributions : map(address, int),\ntotal : int,\nbeneficiary : address,\ndeadline : int,\ngoal : int }\n\nstateful function spend(args : spend_args) =\nChain.spend(args.recipient, args.amount)\n\nentrypoint init(beneficiary, deadline, goal) : state =\n{ contributions = {},\nbeneficiary = beneficiary,\ndeadline = deadline,\ntotal = 0,\ngoal = goal }\n\nfunction is_contributor(addr) =\nMap.member(addr, state.contributions)\n\nstateful entrypoint contribute() =\nif(Chain.block_height >= state.deadline)\nspend({ recipient = Call.caller, amount = Call.value }) // Refund money\nfalse\nelse\nlet amount =\nswitch(Map.lookup(Call.caller, state.contributions))\nNone => Call.value\nSome(n) => n + Call.value\nput(state{ contributions[Call.caller] = amount,\ntotal @ tot = tot + Call.value })\ntrue\n\nstateful entrypoint withdraw() =\nif(Chain.block_height < state.deadline)\nabort(\"Cannot withdraw before deadline\")\nif(Call.caller == state.beneficiary)\nwithdraw_beneficiary()\nelif(is_contributor(Call.caller))\nwithdraw_contributor()\nelse\nabort(\"Not a contributor or beneficiary\")\n\nstateful function withdraw_beneficiary() =\nrequire(state.total >= state.goal, \"Project was not funded\")\nspend({recipient = state.beneficiary,\namount = Contract.balance })\n\nstateful function withdraw_contributor() =\nif(state.total >= state.goal)\nabort(\"Project was funded\")\nlet to = Call.caller\nspend({recipient = to,\namount = state.contributions[to]})\nput(state{ contributions @ c = Map.delete(to, c) })\n</code></pre>"},{"location":"sophia_examples/#repositories","title":"Repositories","text":"<p>This is a list with repositories that include smart contracts written in Sophia:</p> <ul> <li>aepp-sophia-examples<ul> <li>A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.</li> </ul> </li> </ul>"},{"location":"sophia_features/","title":"Features","text":""},{"location":"sophia_features/#contracts","title":"Contracts","text":"<p>The main unit of code in Sophia is the contract.</p> <ul> <li>A contract implementation, or simply a contract, is the code for a smart contract and consists of a list of types, entrypoints and local functions. Only the entrypoints can be called from outside the contract.</li> <li>A contract instance is an entity living on the block chain (or in a state channel). Each instance has an address that can be used to call its entrypoints, either from another contract or in a call transaction.</li> <li>A contract may define a type <code>state</code> encapsulating its local state. When creating a new contract the <code>init</code> entrypoint is executed and the state is initialized to its return value.</li> </ul> <p>The language offers some primitive functions to interact with the blockchain and contracts. Please refer to the Chain, Contract and the Call namespaces in the documentation.</p>"},{"location":"sophia_features/#calling-other-contracts","title":"Calling other contracts","text":"<p>To call a function in another contract you need the address to an instance of the contract. The type of the address must be a contract type, which consists of a number of type definitions and entrypoint declarations. For instance,</p> <pre><code>// A contract type\ncontract interface VotingType =\nentrypoint vote : string => unit\n</code></pre> <p>Now given contract address of type <code>VotingType</code> you can call the <code>vote</code> entrypoint of that contract:</p> <pre><code>contract VoteTwice =\nentrypoint voteTwice(v : VotingType, alt : string) =\nv.vote(alt)\nv.vote(alt)\n</code></pre> <p>Contract calls take two optional named arguments <code>gas : int</code> and <code>value : int</code> that lets you set a gas limit and provide tokens to a contract call. If omitted the defaults are no gas limit and no tokens. Suppose there is a fee for voting:</p> <pre><code> entrypoint voteTwice(v : VotingType, fee : int, alt : string) =\nv.vote(value = fee, alt)\nv.vote(value = fee, alt)\n</code></pre> <p>Named arguments can be given in any order.</p> <p>Note that reentrant calls are not permitted. In other words, when calling another contract it cannot call you back (directly or indirectly).</p> <p>To construct a value of a contract type you can give a contract address literal (for instance <code>ct_2gPXZnZdKU716QBUFKaT4VdBZituK93KLvHJB3n4EnbrHHw4Ay</code>), or convert an account address to a contract address using <code>Address.to_contract</code>. Note that if the contract does not exist, or it doesn't have the entrypoint, or the type of the entrypoint does not match the stated contract type, the call fails.</p> <p>To recover the underlying <code>address</code> of a contract instance there is a field <code>address : address</code>. For instance, to send tokens to the voting contract (given that it is payable) without calling it you can write</p> <pre><code> entrypoint pay(v : VotingType, amount : int) =\nChain.spend(v.address, amount)\n</code></pre>"},{"location":"sophia_features/#protected-contract-calls","title":"Protected contract calls","text":"<p>If a contract call fails for any reason (for instance, the remote contract crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong type) the parent call also fails. To make it possible to recover from failures, contract calls takes a named argument <code>protected : bool</code> (default <code>false</code>).</p> <p>The protected argument must be a literal boolean, and when set to <code>true</code> changes the type of the contract call, wrapping the result in an <code>option</code> type. If the call fails the result is <code>None</code>, otherwise it's <code>Some(r)</code> where <code>r</code> is the return value of the call.</p> <pre><code>contract interface VotingType =\nentrypoint : vote : string => unit\n\ncontract Voter =\nentrypoint tryVote(v : VotingType, alt : string) =\nswitch(v.vote(alt, protected = true) : option(unit))\nNone => \"Voting failed\"\nSome(_) => \"Voting successful\"\n</code></pre> <p>Any gas that was consumed by the contract call before the failure stays consumed, which means that in order to protect against the remote contract running out of gas it is necessary to set a gas limit using the <code>gas</code> argument. However, note that errors that would normally consume all the gas in the transaction still only uses up the gas spent running the contract.</p> <p>Any side effects (state change, token transfers, etc.) made by a failing protected call is rolled back, just like they would be in the unprotected case.</p>"},{"location":"sophia_features/#contract-factories-and-child-contracts","title":"Contract factories and child contracts","text":"<p>Since the version 6.0.0 Sophia supports deploying contracts by other contracts. This can be done in two ways:</p> <ul> <li>Contract cloning via <code>Chain.clone</code></li> <li>Direct deploy via <code>Chain.create</code></li> </ul> <p>These functions take variable number of arguments that must match the created contract's <code>init</code> function. Beside that they take some additional named arguments \u2013 please refer to their documentation for the details.</p> <p>While <code>Chain.clone</code> requires only a <code>contract interface</code> and a living instance of a given contract on the chain, <code>Chain.create</code> needs a full definition of a to-create contract defined by the standard <code>contract</code> syntax, for example</p> <pre><code>contract IntHolder =\ntype state = int\nentrypoint init(x) = x\nentrypoint get() = state\n\nmain contract IntHolderFactory =\nstateful entrypoint new(x : int) : IntHolder =\nlet ih = Chain.create(x) : IntHolder\nih\n</code></pre> <p>In case of a presence of child contracts (<code>IntHolder</code> in this case), the main contract must be pointed out with the <code>main</code> keyword as shown in the example.</p>"},{"location":"sophia_features/#contract-interfaces-and-polymorphism","title":"Contract interfaces and polymorphism","text":"<p>Contracts can implement one or multiple interfaces, the contract has to define every entrypoint from the implemented interface and the entrypoints in both the contract and implemented interface should have compatible types.</p> <pre><code>contract interface Animal =\n entrypoint sound : () => string\n\n contract Cat : Animal =\n entrypoint sound() = \"Cat sound\"\n</code></pre> <p>Contract interfaces can extend other interfaces. An extended interface has to declare all entrypoints from every parent interface. All the declarations in the extended interface must have types compatible with the declarations from the parent interface.</p> <pre><code>contract interface II =\n entrypoint f : () => unit\n\ncontract interface I : II =\n entrypoint f : () => unit\n entrypoint g : () => unit\n\ncontract C : I =\n entrypoint f() = ()\n entrypoint g() = ()\n</code></pre> <p>It is only possible to implement (or extend) an interface that has been already defined earlier in the file (or in an included file). Therefore recursive interface implementation is not allowed in Sophia.</p> <pre><code>// The following code would show an error\n\ncontract interface X : Z =\n entrypoint x : () => int\n\n contract interface Y : X =\n entrypoint x : () => int\n entrypoint y : () => int\n\n contract interface Z : Y =\n entrypoint x : () => int\n entrypoint y : () => int\n entrypoint z : () => int\n\n contract C : Z =\n entrypoint x() = 1\n entrypoint y() = 1\n entrypoint z() = 1\n</code></pre>"},{"location":"sophia_features/#adding-or-removing-modifiers","title":"Adding or removing modifiers","text":"<p>When a <code>contract</code> or a <code>contract interface</code> implements another <code>contract interface</code>, the <code>payable</code> and <code>stateful</code> modifiers can be kept or changed, both in the contract and in the entrypoints, according to the following rules:</p> <ol> <li>A <code>payable</code> contract or interface can implement a <code>payable</code> interface or a non-<code>payable</code> interface.</li> <li>A non-<code>payable</code> contract or interface can only implement a non-<code>payable</code> interface, and cannot implement a <code>payable</code> interface.</li> <li>A <code>payable</code> entrypoint can implement a <code>payable</code> entrypoint or a non-<code>payable</code> entrypoint.</li> <li>A non-<code>payable</code> entrypoint can only implement a non-<code>payable</code> entrypoint, and cannot implement a <code>payable</code> entrypoint.</li> <li>A non-<code>stateful</code> entrypoint can implement a <code>stateful</code> entrypoint or a non-<code>stateful</code> entrypoint.</li> <li>A <code>stateful</code> entrypoint can only implement a <code>stateful</code> entrypoint, and cannot implement a non-<code>stateful</code> entrypoint.</li> </ol>"},{"location":"sophia_features/#subtyping-and-variance","title":"Subtyping and variance","text":"<p>Subtyping in Sophia follows common rules that take type variance into account. As described by Wikipedia, </p> <p>Variance refers to how subtyping between more complex types relates to subtyping between their components.</p> <p>This concept plays an important role in complex types such as tuples, <code>datatype</code>s and functions. Depending on the context, it can apply to positions in the structure of a type, or type parameters of generic types. There are four kinds of variances:</p> <ul> <li>covariant</li> <li>contravariant</li> <li>invariant</li> <li>bivariant</li> </ul> <p>A type is said to be on a \"covariant\" position when it describes output or a result of some computation. Analogously, position is \"contravariant\" when it is an input, or a parameter. Intuitively, when a part of the type is produced by values of it, it is covariant. When it is consumed, it is contravariant. When a type appears to be simultaneously input and output, it is described as invariant. If a type is neither of those (that is, it's unused) it's bivariant. Furthermore, whenever a complex type appears on a contravariant position, all its covariant components become contravariant and vice versa.</p> <p>Variance influences how subtyping is applied. Types on covariant positions are subtyped normally, while contravariant the opposite way. Invariant types have to be exactly the same in order for subtyping to work. Bivariant types are always compatible.</p> <p>A good example of where it matters can be pictured by subtyping of function types. Let us assume there is a contract interface <code>Animal</code> and two contracts that implement it: <code>Dog</code> and <code>Cat</code>.</p> <pre><code>contract interface Animal =\nentrypoint age : () => int\n\ncontract Dog : Animal =\nentrypoint age() = // ...\nentrypoint woof() = \"woof\"\n\ncontract Cat : Animal =\nentrypoint age() = // ...\nentrypoint meow() = \"meow\"\n</code></pre> <p>The assumption of this exercise is that cats do not bark (because <code>Cat</code> does not define the <code>woof</code> entrypoint). If subtyping rules were applied naively, that is if we let <code>Dog => Dog</code> be a subtype of <code>Animal => Animal</code>, the following code would break:</p> <pre><code>let f : (Dog) => string = d => d.woof()\nlet g : (Animal) => string = f\nlet c : Cat = Chain.create()\ng(c) // Cat barking!\n</code></pre> <p>That is because arguments of functions are contravariant, as opposed to return the type which is covariant. Because of that, the assignment of <code>f</code> to <code>g</code> is invalid - while <code>Dog</code> is a subtype of <code>Animal</code>, <code>Dog => string</code> is not a subtype of <code>Animal => string</code>. However, <code>Animal => string</code> is a subtype of <code>Dog => string</code>. More than that, <code>(Dog => Animal) => Dog</code> is a subtype of <code>(Animal => Dog) => Animal</code>.</p> <p>This has consequences on how user-defined generic types work. A type variable gains its variance from its role in the type definition as shown in the example:</p> <pre><code>datatype co('a) = Co('a) // co is covariant on 'a\ndatatype ct('a) = Ct('a => unit) // ct is contravariant on 'a\ndatatype in('a) = In('a => 'a) // in is invariant on 'a\ndatatype bi('a) = Bi // bi is bivariant on 'a\n</code></pre> <p>The following facts apply here:</p> <ul> <li><code>co('a)</code> is a subtype of <code>co('b)</code> when <code>'a</code> is a subtype of <code>'b</code></li> <li><code>ct('a)</code> is a subtype of <code>ct('b)</code> when <code>'b</code> is a subtype of <code>'a</code></li> <li><code>in('a)</code> is a subtype of <code>in('b)</code> when <code>'a</code> is equal to <code>'b</code></li> <li><code>bi('a)</code> is a subtype of <code>bi('b)</code> always</li> </ul> <p>That altogether induce the following rules of subtyping in Sophia:</p> <ul> <li> <p>A function type <code>(Args1) => Ret1</code> is a subtype of <code>(Args2) => Ret2</code> when <code>Ret1</code> is a subtype of <code>Ret2</code> and each argument type from <code>Args2</code> is a subtype of its counterpart in <code>Args1</code>.</p> </li> <li> <p>A list type <code>list(A)</code> is a subtype of <code>list(B)</code> if <code>A</code> is a subtype of <code>B</code>.</p> </li> <li> <p>An option type <code>option(A)</code> is a subtype of <code>option(B)</code> if <code>A</code> is a subtype of <code>B</code>.</p> </li> <li> <p>A map type <code>map(A1, A2)</code> is a subtype of <code>map(B1, B2)</code> if <code>A1</code> is a subtype of <code>B1</code>, and <code>A2</code> is a subtype of <code>B2</code>.</p> </li> <li> <p>An oracle type <code>oracle(A1, A2)</code> is a subtype of <code>oracle(B1, B2)</code> if <code>B1</code> is a subtype of <code>A1</code>, and <code>A2</code> is a subtype of <code>B2</code>.</p> </li> <li> <p>An oracle_query type <code>oracle_query(A1, A2)</code> is a subtype of <code>oracle_query(B1, B2)</code> if <code>A1</code> is a subtype of <code>B1</code>, and <code>A2</code> is a subtype of <code>B2</code>.</p> </li> <li> <p>A user-defined datatype <code>t(Args1)</code> is a subtype of <code>t(Args2)</code></p> </li> <li> <p>When a user-defined type <code>t('a)</code> is covariant in <code>'a</code>, then <code>t(A)</code> is a subtype of <code>t(B)</code> when <code>A</code> is a subtype of <code>B</code>.</p> </li> <li> <p>When a user-defined type <code>t('a)</code> is contravariant in <code>'a</code>, then <code>t(A)</code> is a subtype of <code>t(B)</code> when <code>B</code> is a subtype of <code>A</code>.</p> </li> <li> <p>When a user-defined type <code>t('a)</code> is binvariant in <code>'a</code>, then <code>t(A)</code> is a subtype of <code>t(B)</code> when either <code>A</code> is a subtype of <code>B</code> or when <code>B</code> is a subtype of <code>A</code>.</p> </li> <li> <p>When a user-defined type <code>t('a)</code> is invariant in <code>'a</code>, then <code>t(A)</code> can never be a subtype of <code>t(B)</code>.</p> </li> </ul>"},{"location":"sophia_features/#mutable-state","title":"Mutable state","text":"<p>Sophia does not have arbitrary mutable state, but only a limited form of state associated with each contract instance.</p> <ul> <li>Each contract defines a type <code>state</code> encapsulating its mutable state. The type <code>state</code> defaults to the <code>unit</code>.</li> <li>The initial state of a contract is computed by the contract's <code>init</code> function. The <code>init</code> function is pure and returns the initial state as its return value. If the type <code>state</code> is <code>unit</code>, the <code>init</code> function defaults to returning the value <code>()</code>. At contract creation time, the <code>init</code> function is executed and its result is stored as the contract state.</li> <li>The value of the state is accessible from inside the contract through an implicitly bound variable <code>state</code>.</li> <li>State updates are performed by calling a function <code>put : state => unit</code>.</li> <li>Aside from the <code>put</code> function (and similar functions for transactions and events), the language is purely functional.</li> <li>Functions modifying the state need to be annotated with the <code>stateful</code> keyword (see below).</li> </ul> <p>To make it convenient to update parts of a deeply nested state Sophia provides special syntax for map/record updates.</p>"},{"location":"sophia_features/#stateful-functions","title":"Stateful functions","text":"<p>Top-level functions and entrypoints must be annotated with the <code>stateful</code> keyword to be allowed to affect the state of the running contract. For instance,</p> <pre><code> stateful entrypoint set_state(s : state) =\nput(s)\n</code></pre> <p>Without the <code>stateful</code> annotation the compiler does not allow the call to <code>put</code>. A <code>stateful</code> annotation is required to</p> <ul> <li>Use a stateful primitive function. These are</li> <li><code>put</code></li> <li><code>Chain.spend</code></li> <li><code>Oracle.register</code></li> <li><code>Oracle.query</code></li> <li><code>Oracle.respond</code></li> <li><code>Oracle.extend</code></li> <li><code>AENS.preclaim</code></li> <li><code>AENS.claim</code></li> <li><code>AENS.transfer</code></li> <li><code>AENS.revoke</code></li> <li><code>AENS.update</code></li> <li>Call a <code>stateful</code> function in the current contract</li> <li>Call another contract with a non-zero <code>value</code> argument.</li> </ul> <p>A <code>stateful</code> annotation is not required to</p> <ul> <li>Read the contract state.</li> <li>Issue an event using the <code>event</code> function.</li> <li>Call another contract with <code>value = 0</code>, even if the called function is stateful.</li> </ul>"},{"location":"sophia_features/#payable","title":"Payable","text":""},{"location":"sophia_features/#payable-contracts","title":"Payable contracts","text":"<p>A concrete contract is by default not payable. Any attempt at spending to such a contract (either a <code>Chain.spend</code> or a normal spend transaction) will fail. If a contract shall be able to receive funds in this way it has to be declared <code>payable</code>:</p> <pre><code>// A payable contract\npayable contract ExampleContract =\nstateful entrypoint do_stuff() = ...\n</code></pre> <p>If in doubt, it is possible to check if an address is payable using <code>Address.is_payable(addr)</code>.</p>"},{"location":"sophia_features/#payable-entrypoints","title":"Payable entrypoints","text":"<p>A contract entrypoint is by default not payable. Any call to such a function (either a Remote call or a contract call transaction) that has a non-zero <code>value</code> will fail. Contract entrypoints that should be called with a non-zero value should be declared <code>payable</code>.</p> <pre><code>payable stateful entrypoint buy(to : address) =\nif(Call.value > 42)\ntransfer_item(to)\nelse\nabort(\"Value too low\")\n</code></pre>"},{"location":"sophia_features/#namespaces","title":"Namespaces","text":"<p>Code can be split into libraries using the <code>namespace</code> construct. Namespaces can appear at the top-level and can contain type and function definitions, but not entrypoints. Outside the namespace you can refer to the (non-private) names by qualifying them with the namespace (<code>Namespace.name</code>). For example,</p> <pre><code>namespace Library =\ntype number = int\nfunction inc(x : number) : number = x + 1\n\ncontract MyContract =\nentrypoint plus2(x) : Library.number =\nLibrary.inc(Library.inc(x))\n</code></pre> <p>Functions in namespaces have access to the same environment (including the <code>Chain</code>, <code>Call</code>, and <code>Contract</code>, builtin namespaces) as function in a contract, with the exception of <code>state</code>, <code>put</code> and <code>Chain.event</code> since these are dependent on the specific state and event types of the contract.</p> <p>To avoid mentioning the namespace every time it is used, Sophia allows including the namespace in the current scope with the <code>using</code> keyword: <pre><code>include \"Pair.aes\"\nusing Pair\ncontract C =\n type state = int\n entrypoint init() =\n let p = (1, 2)\n fst(p) // this is the same as Pair.fst(p)\n</code></pre></p> <p>It is also possible to make an alias for the namespace with the <code>as</code> keyword: <pre><code>include \"Pair.aes\"\ncontract C =\n using Pair as P\n type state = int\n entrypoint init() =\n let p = (1, 2)\n P.fst(p) // this is the same as Pair.fst(p)\n</code></pre></p> <p>Having the same alias for multiple namespaces is possible and it allows referening functions that are defined in different namespaces and have different names with the same alias: <pre><code>namespace Xa = function f() = 1\nnamespace Xb = function g() = 2\ncontract Cntr =\n using Xa as A\n using Xb as A\n type state = int\n entrypoint init() = A.f() + A.g()\n</code></pre></p> <p>Note that using functions with the same name would result in an ambiguous name error: <pre><code>namespace Xa = function f() = 1\nnamespace Xb = function f() = 2\ncontract Cntr =\n using Xa as A\n using Xb as A\n type state = int\n\n // the next line has an error because f is defined in both Xa and Xb\n entrypoint init() = A.f()\n</code></pre></p> <p>Importing specific parts of a namespace or hiding these parts can also be done like this: <pre><code>using Pair for [fst, snd] // this will only import fst and snd\nusing Triple hiding [fst, snd] // this will import everything except for fst and snd\n</code></pre></p> <p>Note that it is possible to use a namespace in the top level of the file, in the contract level, namespace level, or in the function level.</p>"},{"location":"sophia_features/#splitting-code-over-multiple-files","title":"Splitting code over multiple files","text":"<p>Code from another file can be included in a contract using an <code>include</code> statement. These must appear at the top-level (outside the main contract). The included file can contain one or more namespaces and abstract contracts. For example, if the file <code>library.aes</code> contains</p> <pre><code>namespace Library =\nfunction inc(x) = x + 1\n</code></pre> <p>you can use it from another file using an <code>include</code>:</p> <pre><code>include \"library.aes\"\ncontract MyContract =\nentrypoint plus2(x) = Library.inc(Library.inc(x))\n</code></pre> <p>This behaves as if the contents of <code>library.aes</code> was textually inserted into the file, except that error messages will refer to the original source locations. The language will try to include each file at most one time automatically, so even cyclic includes should be working without any special tinkering.</p>"},{"location":"sophia_features/#standard-library","title":"Standard library","text":"<p>Sophia offers standard library which exposes some primitive operations and some higher level utilities. The builtin namespaces like <code>Chain</code>, <code>Contract</code>, <code>Map</code> are included by default and are supported internally by the compiler. Others like <code>List</code>, <code>Frac</code>, <code>Option</code> need to be manually included using the <code>include</code> directive. For example <pre><code>include \"List.aes\"\ninclude \"Pair.aes\"\n-- Map is already there!\n\nnamespace C =\nentrypoint keys(m : map('a, 'b)) : list('a) =\nList.map(Pair.fst, (Map.to_list(m)))\n</code></pre></p>"},{"location":"sophia_features/#types","title":"Types","text":"<p>Sophia has the following types:</p> Type Description Example int A 2-complement integer <code>-1</code> address \u00e6ternity address, 32 bytes <code>Call.origin</code> bool A Boolean <code>true</code> bits A bit field <code>Bits.none</code> bytes(n) A byte array with <code>n</code> bytes <code>#fedcba9876543210</code> string An array of bytes <code>\"Foo\"</code> list A homogeneous immutable singly linked list. <code>[1, 2, 3]</code> ('a, 'b) => 'c A function. Parentheses can be skipped if there is only one argument <code>(x : int, y : int) => x + y</code> tuple An ordered heterogeneous array <code>(42, \"Foo\", true)</code> record An immutable key value store with fixed key names and typed values <code>record balance = { owner: address, value: int }</code> map An immutable key value store with dynamic mapping of keys of one type to values of one type <code>type accounts = map(string, address)</code> option('a) An optional value either None or Some('a) <code>Some(42)</code> state A user defined type holding the contract state <code>record state = { owner: address, magic_key: bytes(4) }</code> event An append only list of blockchain events (or log entries) <code>datatype event = EventX(indexed int, string)</code> hash A 32-byte hash - equivalent to <code>bytes(32)</code> signature A signature - equivalent to <code>bytes(64)</code> Chain.ttl Time-to-live (fixed height or relative to current block) <code>FixedTTL(1050)</code> <code>RelativeTTL(50)</code> oracle('a, 'b) And oracle answering questions of type 'a with answers of type 'b <code>Oracle.register(acct, qfee, ttl)</code> oracle_query('a, 'b) A specific oracle query <code>Oracle.query(o, q, qfee, qttl, rttl)</code> contract A user defined, typed, contract address <code>function call_remote(r : RemoteContract) = r.fun()</code>"},{"location":"sophia_features/#literals","title":"Literals","text":"Type Constant/Literal example(s) int <code>-1</code>, <code>2425</code>, <code>4598275923475723498573485768</code> address <code>ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt</code> bool <code>true</code>, <code>false</code> bits <code>Bits.none</code>, <code>Bits.all</code> bytes(8) <code>#fedcba9876543210</code> string <code>\"This is a string\"</code> list <code>[1, 2, 3]</code>, <code>[(true, 24), (false, 19), (false, -42)]</code> tuple <code>(42, \"Foo\", true)</code> record <code>{ owner = Call.origin, value = 100000000 }</code> map <code>{[\"foo\"] = 19, [\"bar\"] = 42}</code>, <code>{}</code> option(int) <code>Some(42)</code>, <code>None</code> state <code>state{ owner = Call.origin, magic_key = #a298105f }</code> event <code>EventX(0, \"Hello\")</code> hash <code>#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f</code> signature <code>#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f</code> Chain.ttl <code>FixedTTL(1050)</code>, <code>RelativeTTL(50)</code> oracle('a, 'b) <code>ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5</code> oracle_query('a, 'b) <code>oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY</code> contract <code>ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ</code>"},{"location":"sophia_features/#hole-expression","title":"Hole expression","text":"<p>Hole expressions, written as <code>???</code>, are expressions that are used as a placeholder. During compilation, the compiler will generate a type error indication the type of the hole expression.</p> <pre><code>include \"List.aes\"\ncontract C =\n entrypoint f() =\n List.sum(List.map(???, [1,2,3]))\n</code></pre> <p>A hole expression found in the example above will generate the error <code>Found a hole of type `(int) => int`</code>. This says that the compiler expects a function from <code>int</code> to <code>int</code> in place of the <code>???</code> placeholder.</p>"},{"location":"sophia_features/#constants","title":"Constants","text":"<p>Constants in Sophia are contract-level bindings that can be used in either contracts or namespaces. The value of a constant can be a literal, another constant, or arithmetic operations applied to other constants. Lists, tuples, maps, and records can also be used to define a constant as long as their elements are also constants.</p> <p>The following visibility rules apply to constants: * Constants defined inside a contract are private in that contract. Thus, cannot be accessed through instances of their defining contract. * Constants defined inside a namespace are public. Thus, can be used in other contracts or namespaces. * Constants cannot be defined inside a contract interface.</p> <p>When a constant is shadowed, it can be accessed using its qualified name:</p> <pre><code>contract C =\n let c = 1\n entrypoint f() =\n let c = 2\n c + C.c // the result is 3\n</code></pre> <p>The name of the constant must be an id; therefore, no pattern matching is allowed when defining a constant:</p> <pre><code>contract C\n let x::y::_ = [1,2,3] // this will result in an error\n</code></pre>"},{"location":"sophia_features/#arithmetic","title":"Arithmetic","text":"<p>Sophia integers (<code>int</code>) are represented by arbitrary-sized signed words and support the following arithmetic operations: - addition (<code>x + y</code>) - subtraction (<code>x - y</code>) - multiplication (<code>x * y</code>) - division (<code>x / y</code>), truncated towards zero - remainder (<code>x mod y</code>), satisfying <code>y * (x / y) + x mod y == x</code> for non-zero <code>y</code> - exponentiation (<code>x ^ y</code>)</p> <p>All operations are safe with respect to overflow and underflow. The division and modulo operations throw an arithmetic error if the right-hand operand is zero.</p>"},{"location":"sophia_features/#bit-fields","title":"Bit fields","text":"<p>Sophia integers do not support bit arithmetic. Instead there is a separate type <code>bits</code>. See the standard library documentation.</p> <p>A bit field can be of arbitrary size (but it is still represented by the corresponding integer, so setting very high bits can be expensive).</p>"},{"location":"sophia_features/#type-aliases","title":"Type aliases","text":"<p>Type aliases can be introduced with the <code>type</code> keyword and can be parameterized. For instance</p> <pre><code>type number = int\ntype string_map('a) = map(string, 'a)\n</code></pre> <p>A type alias and its definition can be used interchangeably. Sophia does not support higher-kinded types, meaning that following type alias is invalid: <code>type wrap('f, 'a) = 'f('a)</code></p>"},{"location":"sophia_features/#algebraic-data-types","title":"Algebraic data types","text":"<p>Sophia supports algebraic data types (variant types) and pattern matching. Data types are declared by giving a list of constructors with their respective arguments. For instance,</p> <pre><code>datatype one_or_both('a, 'b) = Left('a) | Right('b) | Both('a, 'b)\n</code></pre> <p>Elements of data types can be pattern matched against, using the <code>switch</code> construct:</p> <pre><code>function get_left(x : one_or_both('a, 'b)) : option('a) =\nswitch(x)\nLeft(x) => Some(x)\nRight(_) => None\nBoth(x, _) => Some(x)\n</code></pre> <p>or directly in the left-hand side: <pre><code>function\nget_left : one_or_both('a, 'b) => option('a)\nget_left(Left(x)) = Some(x)\nget_left(Right(_)) = None\nget_left(Both(x, _)) = Some(x)\n</code></pre></p> <p>NOTE: Data types cannot currently be recursive.</p> <p>Sophia also supports the assignment of patterns to variables: <pre><code>function f(x) = switch(x)\nh1::(t = h2::_) => (h1 + h2)::t // same as `h1::h2::k => (h1 + h2)::h2::k`\n_ => x\n\nfunction g(p : int * option(int)) : int =\nlet (a, (o = Some(b))) = p // o is equal to Pair.snd(p)\nb\n</code></pre></p> <p>Guards are boolean expressions that can be used on patterns in both switch statements and functions definitions. If a guard expression evaluates to <code>true</code>, then the corresponding body will be used. Otherwise, the next pattern will be checked:</p> <pre><code>function get_left_if_positive(x : one_or_both(int, 'b)) : option(int) =\nswitch(x)\nLeft(x) | x > 0 => Some(x)\nBoth(x, _) | x > 0 => Some(x)\n_ => None\n</code></pre> <pre><code>function\nget_left_if_positive : one_or_both(int, 'b) => option(int)\nget_left_if_positive(Left(x)) | x > 0 = Some(x)\nget_left_if_positive(Both(x, _)) | x > 0 = Some(x)\nget_left_if_positive(_) = None\n</code></pre> <p>Guards cannot be stateful even when used inside a stateful function.</p>"},{"location":"sophia_features/#lists","title":"Lists","text":"<p>A Sophia list is a dynamically sized, homogenous, immutable, singly linked list. A list is constructed with the syntax <code>[1, 2, 3]</code>. The elements of a list can be any of datatype but they must have the same type. The type of lists with elements of type <code>'e</code> is written <code>list('e)</code>. For example we can have the following lists:</p> <pre><code>[1, 33, 2, 666] : list(int)\n[(1, \"aaa\"), (10, \"jjj\"), (666, \"the beast\")] : list(int * string)\n[{[1] = \"aaa\", [10] = \"jjj\"}, {[5] = \"eee\", [666] = \"the beast\"}] : list(map(int, string))\n</code></pre> <p>New elements can be prepended to the front of a list with the <code>::</code> operator. So <code>42 :: [1, 2, 3]</code> returns the list <code>[42, 1, 2, 3]</code>. The concatenation operator <code>++</code> appends its second argument to its first and returns the resulting list. So concatenating two lists <code>[1, 22, 33] ++ [10, 18, 55]</code> returns the list <code>[1, 22, 33, 10, 18, 55]</code>.</p> <p>Sophia supports list comprehensions known from languages like Python, Haskell or Erlang. Example syntax: <pre><code>[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]\n// yields [12,13,14,20,21,22,30,31,32]\n</code></pre></p> <p>Lists can be constructed using the range syntax using special <code>..</code> operator: <pre><code>[1..4] == [1,2,3,4]\n</code></pre> The ranges are always ascending and have step equal to 1.</p> <p>Please refer to the standard library for the predefined functionalities.</p>"},{"location":"sophia_features/#maps-and-records","title":"Maps and records","text":"<p>A Sophia record type is given by a fixed set of fields with associated, possibly different, types. For instance <pre><code> record account = { name : string,\nbalance : int,\nhistory : list(transaction) }\n</code></pre></p> <p>Maps, on the other hand, can contain an arbitrary number of key-value bindings, but of a fixed type. The type of maps with keys of type <code>'k</code> and values of type <code>'v</code> is written <code>map('k, 'v)</code>. The key type can be any type that does not contain a map or a function type.</p> <p>Please refer to the standard library for the predefined functionalities.</p>"},{"location":"sophia_features/#constructing-maps-and-records","title":"Constructing maps and records","text":"<p>A value of record type is constructed by giving a value for each of the fields. For the example above, <pre><code> function new_account(name) =\n{name = name, balance = 0, history = []}\n</code></pre> Maps are constructed similarly, with keys enclosed in square brackets <pre><code> function example_map() : map(string, int) =\n{[\"key1\"] = 1, [\"key2\"] = 2}\n</code></pre> The empty map is written <code>{}</code>.</p>"},{"location":"sophia_features/#accessing-values","title":"Accessing values","text":"<p>Record fields access is written <code>r.f</code> and map lookup <code>m[k]</code>. For instance, <pre><code> function get_balance(a : address, accounts : map(address, account)) =\naccounts[a].balance\n</code></pre> Looking up a non-existing key in a map results in contract execution failing. A default value to return for non-existing keys can be provided using the syntax <code>m[k = default]</code>. See also <code>Map.member</code> and <code>Map.lookup</code> below.</p>"},{"location":"sophia_features/#updating-a-value","title":"Updating a value","text":"<p>Record field updates are written <code>r{f = v}</code>. This creates a new record value which is the same as <code>r</code>, but with the value of the field <code>f</code> replaced by <code>v</code>. Similarly, <code>m{[k] = v}</code> constructs a map with the same values as <code>m</code> except that <code>k</code> maps to <code>v</code>. It makes no difference if <code>m</code> has a mapping for <code>k</code> or not.</p> <p>It is possible to give a name to the old value of a field or mapping in an update: instead of <code>acc{ balance = acc.balance + 100 }</code> it is possible to write <code>acc{ balance @ b = b + 100 }</code>, binding <code>b</code> to <code>acc.balance</code>. When giving a name to a map value (<code>m{ [k] @ x = v }</code>), the corresponding key must be present in the map or execution fails, but a default value can be provided: <code>m{ [k = default] @ x = v }</code>. In this case <code>x</code> is bound to <code>default</code> if <code>k</code> is not in the map.</p> <p>Updates can be nested: <pre><code>function clear_history(a : address, accounts : map(address, account)) : map(address, account) =\naccounts{ [a].history = [] }\n</code></pre> This is equivalent to <code>accounts{ [a] @ acc = acc{ history = [] } }</code> and thus requires <code>a</code> to be present in the accounts map. To have <code>clear_history</code> create an account if <code>a</code> is not in the map you can write (given a function <code>empty_account</code>): <pre><code> accounts{ [a = empty_account()].history = [] }\n</code></pre></p>"},{"location":"sophia_features/#map-implementation","title":"Map implementation","text":"<p>Internally in the VM maps are implemented as hash maps and support fast lookup and update. Large maps can be stored in the contract state and the size of the map does not contribute to the gas costs of a contract call reading or updating it.</p>"},{"location":"sophia_features/#strings","title":"Strings","text":"<p>There is a builtin type <code>string</code>, which can be seen as an array of bytes. Strings can be compared for equality (<code>==</code>, <code>!=</code>), used as keys in maps and records, and used in builtin functions <code>String.length</code>, <code>String.concat</code> and the hash functions described below.</p> <p>Please refer to the <code>String</code> library documentation.</p>"},{"location":"sophia_features/#chars","title":"Chars","text":"<p>There is a builtin type <code>char</code> (the underlying representation being an integer), mainly used to manipulate strings via <code>String.to_list</code>/<code>String.from_list</code>.</p> <p>Characters can also be introduced as character literals (`'x', '+', ...).</p> <p>Please refer to the <code>Char</code> library documentation.</p>"},{"location":"sophia_features/#byte-arrays","title":"Byte arrays","text":"<p>Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system, for example the literal <code>#cafe</code> creates a two-element array of bytes <code>ca</code> (202) and <code>fe</code> (254) and thus is a value of type <code>bytes(2)</code>.</p> <p>Please refer to the <code>Bytes</code> library documentation.</p>"},{"location":"sophia_features/#cryptographic-builtins","title":"Cryptographic builtins","text":"<p>Libraries Crypto and String provide functions to hash objects, verify signatures etc. The <code>hash</code> is a type alias for <code>bytes(32)</code>.</p>"},{"location":"sophia_features/#authorization-interface","title":"Authorization interface","text":"<p>When a Generalized account is authorized, the authorization function needs access to the transaction and the transaction hash for the wrapped transaction. (A <code>GAMetaTx</code> wrapping a transaction.) The transaction and the transaction hash is available in the primitive <code>Auth.tx</code> and <code>Auth.tx_hash</code> respectively, they are only available during authentication if invoked by a normal contract call they return <code>None</code>.</p>"},{"location":"sophia_features/#oracle-interface","title":"Oracle interface","text":"<p>You can attach an oracle to the current contract and you can interact with oracles through the Oracle interface.</p> <p>For a full description of how Oracle works see Oracles. For a functionality documentation refer to the standard library.</p>"},{"location":"sophia_features/#example","title":"Example","text":"<p>Example for an oracle answering questions of type <code>string</code> with answers of type <code>int</code>: <pre><code>contract Oracles =\n\nstateful entrypoint registerOracle(acct : address,\nsign : signature, // Signed network id + oracle address + contract address\nqfee : int,\nttl : Chain.ttl) : oracle(string, int) =\nOracle.register(acct, signature = sign, qfee, ttl)\n\nentrypoint queryFee(o : oracle(string, int)) : int =\nOracle.query_fee(o)\n\npayable stateful entrypoint createQuery(o : oracle_query(string, int),\nq : string,\nqfee : int,\nqttl : Chain.ttl,\nrttl : int) : oracle_query(string, int) =\nrequire(qfee =< Call.value, \"insufficient value for qfee\")\nOracle.query(o, q, qfee, qttl, RelativeTTL(rttl))\n\nstateful entrypoint extendOracle(o : oracle(string, int),\nttl : Chain.ttl) : unit =\nOracle.extend(o, ttl)\n\nstateful entrypoint signExtendOracle(o : oracle(string, int),\nsign : signature, // Signed network id + oracle address + contract address\nttl : Chain.ttl) : unit =\nOracle.extend(o, signature = sign, ttl)\n\nstateful entrypoint respond(o : oracle(string, int),\nq : oracle_query(string, int),\nsign : signature, // Signed network id + oracle query id + contract address\nr : int) =\nOracle.respond(o, q, signature = sign, r)\n\nentrypoint getQuestion(o : oracle(string, int),\nq : oracle_query(string, int)) : string =\nOracle.get_question(o, q)\n\nentrypoint hasAnswer(o : oracle(string, int),\nq : oracle_query(string, int)) =\nswitch(Oracle.get_answer(o, q))\nNone => false\nSome(_) => true\n\nentrypoint getAnswer(o : oracle(string, int),\nq : oracle_query(string, int)) : option(int) =\nOracle.get_answer(o, q)\n</code></pre></p>"},{"location":"sophia_features/#sanity-checks","title":"Sanity checks","text":"<p>When an Oracle literal is passed to a contract, no deep checks are performed. For extra safety Oracle.check and Oracle.check_query functions are provided.</p>"},{"location":"sophia_features/#aens-interface","title":"AENS interface","text":"<p>Contracts can interact with the \u00e6ternity naming system. For this purpose the AENS library was exposed.</p>"},{"location":"sophia_features/#example_1","title":"Example","text":"<p>In this example we assume that the name <code>name</code> already exists, and is owned by an account with address <code>addr</code>. In order to allow a contract <code>ct</code> to handle <code>name</code> the account holder needs to create a signature <code>sig</code> of <code>addr | name.hash | ct.address</code>.</p> <p>Armed with this information we can for example write a function that extends the name if it expires within 1000 blocks: <pre><code> stateful entrypoint extend_if_necessary(addr : address, name : string, sig : signature) =\nswitch(AENS.lookup(name))\nNone => ()\nSome(AENS.Name(_, FixedTTL(expiry), _)) =>\nif(Chain.block_height + 1000 > expiry)\nAENS.update(addr, name, Some(RelativeTTL(50000)), None, None, signature = sig)\n</code></pre></p> <p>And we can write functions that adds and removes keys from the pointers of the name: <pre><code> stateful entrypoint add_key(addr : address, name : string, key : string,\npt : AENS.pointee, sig : signature) =\nswitch(AENS.lookup(name))\nNone => ()\nSome(AENS.Name(_, _, ptrs)) =>\nAENS.update(addr, name, None, None, Some(ptrs{[key] = pt}), signature = sig)\n\nstateful entrypoint delete_key(addr : address, name : string,\nkey : string, sig : signature) =\nswitch(AENS.lookup(name))\nNone => ()\nSome(AENS.Name(_, _, ptrs)) =>\nlet ptrs = Map.delete(key, ptrs)\nAENS.update(addr, name, None, None, Some(ptrs), signature = sig)\n</code></pre></p> <p>Note: From the Iris hardfork more strict rules apply for AENS pointers, when a Sophia contract lookup or update (bad) legacy pointers, the bad keys are automatically removed so they will not appear in the pointers map.</p>"},{"location":"sophia_features/#events","title":"Events","text":"<p>Sophia contracts log structured messages to an event log in the resulting blockchain transaction. The event log is quite similar to Events in Solidity. Events are further discussed in the protocol.</p> <p>To use events a contract must declare a datatype <code>event</code>, and events are then logged using the <code>Chain.event</code> function:</p> <pre><code> datatype event\n= Event1(int, int, string)\n| Event2(string, address)\n\nChain.event(e : event) : unit\n</code></pre> <p>The event can have 0-3 indexed fields, and an optional payload field. A field is indexed if it fits in a 32-byte word, i.e. - <code>bool</code> - <code>int</code> - <code>bits</code> - <code>address</code> - <code>oracle(_, _)</code> - <code>oracle_query(_, _)</code> - contract types - <code>bytes(n)</code> for <code>n</code> \u2264 32, in particular <code>hash</code></p> <p>The payload field must be either a string or a byte array of more than 32 bytes. The fields can appear in any order.</p> <p>NOTE: Indexing is not part of the core \u00e6ternity node.</p> <p>Events are emitted by using the <code>Chain.event</code> function. The following function will emit one Event of each kind in the example.</p> <pre><code> entrypoint emit_events() : () =\nChain.event(Event1(42, 34, \"foo\"))\nChain.event(Event2(\"This is not indexed\", Contract.address))\n</code></pre>"},{"location":"sophia_features/#argument-order","title":"Argument order","text":"<p>It is only possible to have one (1) <code>string</code> parameter in the event, but it can be placed in any position (and its value will end up in the <code>data</code> field), i.e. <pre><code>AnotherEvent(string, indexed address)\n\n...\n\nChain.event(AnotherEvent(\"This is not indexed\", Contract.address))\n</code></pre> would yield exactly the same result in the example above!</p>"},{"location":"sophia_features/#compiler-pragmas","title":"Compiler pragmas","text":"<p>To enforce that a contract is only compiled with specific versions of the Sophia compiler, you can give one or more <code>@compiler</code> pragmas at the top-level (typically at the beginning) of a file. For instance, to enforce that a contract is compiled with version 4.3 of the compiler you write</p> <pre><code>@compiler >= 4.3\n@compiler < 4.4\n</code></pre> <p>Valid operators in compiler pragmas are <code><</code>, <code>=<</code>, <code>==</code>, <code>>=</code>, and <code>></code>. Version numbers are given as a sequence of non-negative integers separated by dots. Trailing zeros are ignored, so <code>4.0.0 == 4</code>. If a constraint is violated an error is reported and compilation fails.</p>"},{"location":"sophia_features/#exceptions","title":"Exceptions","text":"<p>Contracts can fail with an (uncatchable) exception using the built-in function</p> <pre><code>abort(reason : string) : 'a\n</code></pre> <p>Calling abort causes the top-level call transaction to return an error result containing the <code>reason</code> string. Only the gas used up to and including the abort call is charged. This is different from termination due to a crash which consumes all available gas.</p> <p>For convenience the following function is also built-in:</p> <pre><code>function require(b : bool, err : string) =\nif(!b) abort(err)\n</code></pre> <p>Aside from that, there is an almost equivalent function <code>exit</code></p> <pre><code>exit(reason : string) : 'a\n</code></pre> <p>Just like <code>abort</code>, it breaks the execution with the given reason. The difference however is in the gas consumption \u2014 while <code>abort</code> returns unused gas, a call to <code>exit</code> burns it all.</p>"},{"location":"sophia_features/#delegation-signature","title":"Delegation signature","text":"<p>Some chain operations (<code>Oracle.<operation></code> and <code>AENS.<operation></code>) have an optional delegation signature. This is typically used when a user/accounts would like to allow a contract to act on it's behalf. The exact data to be signed varies for the different operations, but in all cases you should prepend the signature data with the <code>network_id</code> (<code>ae_mainnet</code> for the \u00e6ternity mainnet, etc.).</p>"},{"location":"sophia_stdlib/","title":"Standard library","text":""},{"location":"sophia_stdlib/#standard-library","title":"Standard library","text":"<p>Sophia language offers standard library that consists of several namespaces. Some of them are already in the scope and do not need any actions to be used, while the others require some files to be included.</p> <p>The out-of-the-box namespaces are:</p> <ul> <li>Address</li> <li>AENS</li> <li>Auth</li> <li>Bits</li> <li>Bytes</li> <li>Call</li> <li>Chain</li> <li>Char</li> <li>Contract</li> <li>Crypto</li> <li>Int</li> <li>Map</li> <li>Oracle</li> </ul> <p>The following ones need to be included as regular files with <code>.aes</code> suffix, for example <pre><code>include \"List.aes\"\n</code></pre></p> <ul> <li>Bitwise</li> <li>BLS12_381</li> <li>Func</li> <li>Frac</li> <li>List</li> <li>Option</li> <li>Pair</li> <li>Set</li> <li>String</li> <li>Triple</li> </ul>"},{"location":"sophia_stdlib/#builtin-namespaces","title":"Builtin namespaces","text":"<p>They are available without any explicit includes.</p>"},{"location":"sophia_stdlib/#address","title":"Address","text":""},{"location":"sophia_stdlib/#to_str","title":"to_str","text":"<pre><code>Address.to_str(a : address) : string\n</code></pre> <p>Base58 encoded string</p>"},{"location":"sophia_stdlib/#is_contract","title":"is_contract","text":"<pre><code>Address.is_contract(a : address) : bool\n</code></pre> <p>Is the address a contract</p>"},{"location":"sophia_stdlib/#is_oracle","title":"is_oracle","text":"<pre><code>Address.is_oracle(a : address) : bool\n</code></pre> <p>Is the address a registered oracle</p>"},{"location":"sophia_stdlib/#is_payable","title":"is_payable","text":"<pre><code>Address.is_payable(a : address) : bool\n</code></pre> <p>Can the address be spent to</p>"},{"location":"sophia_stdlib/#to_contract","title":"to_contract","text":"<pre><code>Address.to_contract(a : address) : C\n</code></pre> <p>Cast address to contract type C (where <code>C</code> is a contract)</p>"},{"location":"sophia_stdlib/#aens","title":"AENS","text":"<p>The following functionality is available for interacting with the \u00e6ternity naming system (AENS). If <code>owner</code> is equal to <code>Contract.address</code> the signature <code>signature</code> is ignored, and can be left out since it is a named argument. Otherwise we need a signature to prove that we are allowed to do AENS operations on behalf of <code>owner</code>. The signature is tied to a network id, i.e. the signature material should be prefixed by the network id.</p>"},{"location":"sophia_stdlib/#types","title":"Types","text":""},{"location":"sophia_stdlib/#name","title":"name","text":"<pre><code>datatype name = Name(address, Chain.ttl, map(string, AENS.pointee))\n</code></pre>"},{"location":"sophia_stdlib/#pointee","title":"pointee","text":"<pre><code>datatype pointee = AccountPt(address) | OraclePt(address)\n | ContractPt(address) | ChannelPt(address)\n</code></pre>"},{"location":"sophia_stdlib/#functions","title":"Functions","text":""},{"location":"sophia_stdlib/#resolve","title":"resolve","text":"<pre><code>AENS.resolve(name : string, key : string) : option('a)\n</code></pre> <p>Name resolution. Here <code>name</code> should be a registered name and <code>key</code> one of the attributes associated with this name (for instance <code>\"account_pubkey\"</code>). The return type (<code>'a</code>) must be resolved at compile time to an atomic type and the value is type checked against this type at run time.</p>"},{"location":"sophia_stdlib/#lookup","title":"lookup","text":"<pre><code>AENS.lookup(name : string) : option(AENS.name)\n</code></pre> <p>If <code>name</code> is an active name <code>AENS.lookup</code> returns a name object. The three arguments to <code>Name</code> are <code>owner</code>, <code>expiry</code> and a map of the <code>pointees</code> for the name. Note: the expiry of the name is always a fixed TTL. For example: <pre><code>let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup(\"example.chain\")\n</code></pre></p>"},{"location":"sophia_stdlib/#preclaim","title":"preclaim","text":"<pre><code>AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit\n</code></pre> <p>The signature should be over <code>network id</code> + <code>owner address</code> + <code>Contract.address</code> (concatenated as byte arrays).</p>"},{"location":"sophia_stdlib/#claim","title":"claim","text":"<pre><code>AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit\n</code></pre> <p>The signature should be over <code>network id</code> + <code>owner address</code> + <code>name_hash</code> + <code>Contract.address</code> (concatenated as byte arrays) using the private key of the <code>owner</code> account for signing.</p>"},{"location":"sophia_stdlib/#transfer","title":"transfer","text":"<pre><code>AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit\n</code></pre> <p>Transfers name to the new owner.</p> <p>The signature should be over <code>network id</code> + <code>owner address</code> + <code>name_hash</code> + <code>Contract.address</code> (concatenated as byte arrays) using the private key of the <code>owner</code> account for signing.</p>"},{"location":"sophia_stdlib/#revoke","title":"revoke","text":"<pre><code>AENS.revoke(owner : address, name : string, <signature : signature>) : unit\n</code></pre> <p>Revokes the name to extend the ownership time.</p> <p>The signature should be over <code>network id</code> + <code>owner address</code> + <code>name_hash</code> + <code>Contract.address</code> (concatenated as byte arrays) using the private key of the <code>owner</code> account for signing.</p>"},{"location":"sophia_stdlib/#update","title":"update","text":"<pre><code>AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),\n new_ptrs : option(map(string, AENS.pointee)), <signature : signature>) : unit\n</code></pre> <p>Updates the name. If the optional parameters are set to <code>None</code> that parameter will not be updated, for example if <code>None</code> is passed as <code>expiry</code> the expiry block of the name is not changed.</p>"},{"location":"sophia_stdlib/#auth","title":"Auth","text":""},{"location":"sophia_stdlib/#tx","title":"tx","text":"<pre><code>Auth.tx : option(Chain.tx)\n</code></pre> <p>Where <code>Chain.tx</code> is (built-in) defined like: <pre><code>namespace Chain =\n record tx = { paying_for : option(Chain.paying_for_tx)\n , ga_metas : list(Chain.ga_meta_tx)\n , actor : address\n , fee : int\n , ttl : int\n , tx : Chain.base_tx }\n\n datatype ga_meta_tx = GAMetaTx(address, int)\n datatype paying_for_tx = PayingForTx(address, int)\n datatype base_tx = SpendTx(address, int, string)\n | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx\n | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)\n | NameRevokeTx(hash) | NameTransferTx(address, string)\n | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |\n | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address)\n | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address)\n | ContractCreateTx(int) | ContractCallTx(address, int)\n | GAAttachTx\n</code></pre></p>"},{"location":"sophia_stdlib/#tx_hash","title":"tx_hash","text":"<pre><code>Auth.tx_hash : option(hash)\n</code></pre> <p>Gets the transaction hash during authentication.</p>"},{"location":"sophia_stdlib/#bits","title":"Bits","text":""},{"location":"sophia_stdlib/#none","title":"none","text":"<pre><code>Bits.none : bits\n</code></pre> <p>A bit field with all bits cleared</p>"},{"location":"sophia_stdlib/#all","title":"all","text":"<pre><code>Bits.all : bits\n</code></pre> <p>A bit field with all bits set</p>"},{"location":"sophia_stdlib/#set","title":"set","text":"<pre><code>Bits.set(b : bits, i : int) : bits\n</code></pre> <p>Set bit i</p>"},{"location":"sophia_stdlib/#clear","title":"clear","text":"<pre><code>Bits.clear(b : bits, i : int) : bits\n</code></pre> <p>Clear bit i</p>"},{"location":"sophia_stdlib/#test","title":"test","text":"<pre><code>Bits.test(b : bits, i : int) : bool\n</code></pre> <p>Check if bit i is set</p>"},{"location":"sophia_stdlib/#sum","title":"sum","text":"<pre><code>Bits.sum(b : bits) : int\n</code></pre> <p>Count the number of set bits</p>"},{"location":"sophia_stdlib/#union","title":"union","text":"<pre><code>Bits.union(a : bits, b : bits) : bits\n</code></pre> <p>Bitwise disjunction</p>"},{"location":"sophia_stdlib/#intersection","title":"intersection","text":"<pre><code>Bits.intersection(a : bits, b : bits) : bits\n</code></pre> <p>Bitwise conjunction</p>"},{"location":"sophia_stdlib/#difference","title":"difference","text":"<pre><code>Bits.difference(a : bits, b : bits) : bits\n</code></pre> <p>Each bit is true if and only if it was 1 in <code>a</code> and 0 in <code>b</code></p>"},{"location":"sophia_stdlib/#bytes","title":"Bytes","text":""},{"location":"sophia_stdlib/#to_int","title":"to_int","text":"<pre><code>Bytes.to_int(b : bytes(n)) : int\n</code></pre> <p>Interprets the byte array as a big endian integer</p>"},{"location":"sophia_stdlib/#to_str_1","title":"to_str","text":"<pre><code>Bytes.to_str(b : bytes(n)) : string\n</code></pre> <p>Returns the hexadecimal representation of the byte array</p>"},{"location":"sophia_stdlib/#concat","title":"concat","text":"<pre><code>Bytes.concat : (a : bytes(m), b : bytes(n)) => bytes(m + n)\n</code></pre> <p>Concatenates two byte arrays</p>"},{"location":"sophia_stdlib/#split","title":"split","text":"<pre><code>Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)\n</code></pre> <p>Splits a byte array at given index</p>"},{"location":"sophia_stdlib/#call","title":"Call","text":"<p>Values related to the call to the current contract</p>"},{"location":"sophia_stdlib/#origin","title":"origin","text":"<pre><code>Call.origin : address\n</code></pre> <p>The address of the account that signed the call transaction that led to this call.</p>"},{"location":"sophia_stdlib/#caller","title":"caller","text":"<pre><code>Call.caller : address\n</code></pre> <p>The address of the entity (possibly another contract) calling the contract.</p>"},{"location":"sophia_stdlib/#value","title":"value","text":"<pre><code>Call.value : int\n</code></pre> <p>The amount of coins transferred to the contract in the call.</p>"},{"location":"sophia_stdlib/#gas_price","title":"gas_price","text":"<pre><code>Call.gas_price : int\n</code></pre> <p>The gas price of the current call.</p>"},{"location":"sophia_stdlib/#fee","title":"fee","text":"<pre><code>Call.fee : int\n</code></pre> <p>The fee of the current call.</p>"},{"location":"sophia_stdlib/#gas_left","title":"gas_left","text":"<pre><code>Call.gas_left() : int\n</code></pre> <p>The amount of gas left for the current call.</p>"},{"location":"sophia_stdlib/#chain","title":"Chain","text":"<p>Values and functions related to the chain itself and other entities that live on it.</p>"},{"location":"sophia_stdlib/#types_1","title":"Types","text":""},{"location":"sophia_stdlib/#tx_1","title":"tx","text":"<pre><code>record tx = { paying_for : option(Chain.paying_for_tx)\n , ga_metas : list(Chain.ga_meta_tx)\n , actor : address\n , fee : int\n , ttl : int\n , tx : Chain.base_tx }\n</code></pre>"},{"location":"sophia_stdlib/#ga_meta_tx","title":"ga_meta_tx","text":"<pre><code>datatype ga_meta_tx = GAMetaTx(address, int)\n</code></pre>"},{"location":"sophia_stdlib/#paying_for_tx","title":"paying_for_tx","text":"<pre><code>datatype paying_for_tx = PayingForTx(address, int)\n</code></pre>"},{"location":"sophia_stdlib/#base_tx","title":"base_tx","text":"<pre><code>datatype base_tx = SpendTx(address, int, string)\n | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx\n | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)\n | NameRevokeTx(hash) | NameTransferTx(address, string)\n | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |\n | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address)\n | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address)\n | ContractCreateTx(int) | ContractCallTx(address, int)\n | GAAttachTx\n</code></pre>"},{"location":"sophia_stdlib/#functions_1","title":"Functions","text":""},{"location":"sophia_stdlib/#balance","title":"balance","text":"<pre><code>Chain.balance(a : address) : int\n</code></pre> <p>The balance of account <code>a</code>.</p>"},{"location":"sophia_stdlib/#block_hash","title":"block_hash","text":"<pre><code>Chain.block_hash(h : int) : option(bytes(32))\n</code></pre> <p>The hash of the block at height <code>h</code>. <code>h</code> has to be within 256 blocks from the current height of the chain or else the function will return <code>None</code>.</p> <p>NOTE: In FATE VM version 1 <code>Chain.block_height</code> was not considered an allowed height. From FATE VM version 2 (IRIS) it will return the block hash of the current generation.</p>"},{"location":"sophia_stdlib/#block_height","title":"block_height","text":"<pre><code>Chain.block_height : int\"\n</code></pre> <p>The height of the current block (i.e. the block in which the current call will be included).</p>"},{"location":"sophia_stdlib/#bytecode_hash","title":"bytecode_hash","text":"<pre><code>Chain.bytecode_hash : 'c => option(hash)\n</code></pre> <p>Returns the hash of the contract's bytecode (or <code>None</code> if it is nonexistent or deployed before FATE2). The type <code>'c</code> must be instantiated with a contract. The charged gas increases linearly to the size of the serialized bytecode of the deployed contract.</p>"},{"location":"sophia_stdlib/#create","title":"create","text":"<pre><code>Chain.create(value : int, ...) => 'c\n</code></pre> <p>Creates and deploys a new instance of a contract <code>'c</code>. All of the unnamed arguments will be passed to the <code>init</code> function. The charged gas increases linearly with the size of the compiled child contract's bytecode. The <code>source_hash</code> on-chain entry of the newly created contract will be the SHA256 hash over concatenation of</p> <ul> <li>whole contract source code</li> <li>single null byte</li> <li>name of the child contract</li> </ul> <p>The resulting contract's public key can be predicted and in case it happens to have some funds before its creation, its balance will be increased by the <code>value</code> parameter.</p> <p>The <code>value</code> argument (default <code>0</code>) is equivalent to the value in the contract creation transaction \u2013 it sets the initial value of the newly created contract charging the calling contract. Note that this won't be visible in <code>Call.value</code> in the <code>init</code> call of the new contract. It will be included in <code>Contract.balance</code>, however.</p> <p>The type <code>'c</code> must be instantiated with a contract.</p> <p>Example usage: <pre><code>payable contract Auction =\n record state = {supply: int, name: string}\n entrypoint init(supply, name) = {supply: supply, name: name}\n stateful payable entrypoint buy(amount) =\n require(Call.value == amount, \"amount_value_mismatch\")\n ...\n stateful entrypoint sell(amount) =\n require(amount >= 0, \"negative_amount\")\n ...\n\nmain contract Market =\n type state = list(Auction)\n entrypoint init() = []\n stateful entrypoint new(name : string) =\n let new_auction = Chain.create(0, name) : Auction\n put(new_auction::state)\n</code></pre></p> <p>The typechecker must be certain about the created contract's type, so it is worth writing it explicitly as shown in the example.</p>"},{"location":"sophia_stdlib/#clone","title":"clone","text":"<pre><code>Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...\n ) => if(protected) option('c) else 'c\n</code></pre> <p>Clones the contract under the mandatory named argument <code>ref</code>. That means a new contract of the same bytecode and the same <code>payable</code> parameter shall be created. NOTE: the <code>state</code> won't be copied and the contract will be initialized with a regular call to the <code>init</code> function with the remaining unnamed arguments. The resulting contract's public key can be predicted and in case it happens to have some funds before its creation, its balance will be increased by the <code>value</code> parameter. This operation is significantly cheaper than <code>Chain.create</code> as it costs a fixed amount of gas.</p> <p>The <code>gas</code> argument (default <code>Call.gas_left</code>) limits the gas supply for the <code>init</code> call of the cloned contract.</p> <p>The <code>value</code> argument (default <code>0</code>) is equivalent to the value in the contract creation transaction \u2013 it sets the initial value of the newly created contract charging the calling contract. Note that this won't be visible in <code>Call.value</code> in the <code>init</code> call of the new contract. It will be included in <code>Contract.balance</code>, however.</p> <p>The <code>protected</code> argument (default <code>false</code>) works identically as in remote calls. If set to <code>true</code> it will change the return type to <code>option('c)</code> and will catch all errors such as <code>abort</code>, out of gas and wrong arguments. Note that it can only take a boolean literal, so other expressions such as variables will be rejected by the compiler.</p> <p>The type <code>'c</code> must be instantiated with a contract.</p> <p>Example usage:</p> <pre><code>payable contract interface Auction =\n entrypoint init : (int, string) => void\n stateful payable entrypoint buy : (int) => ()\n stateful entrypoint sell : (int) => ()\n\nmain contract Market =\n type state = list(Auction)\n entrypoint init() = []\n stateful entrypoint new_of(template : Auction, name : string) =\n switch(Chain.clone(ref=template, protected=true, 0, name))\n None => abort(\"Bad auction!\")\n Some(new_auction) =>\n put(new_auction::state)\n</code></pre> <p>When cloning by an interface, <code>init</code> entrypoint declaration is required. It is a good practice to set its return type to <code>void</code> in order to indicate that this function is not supposed to be called and is state agnostic. Trivia: internal implementation of the <code>init</code> function does not actually return <code>state</code>, but calls <code>put</code> instead. Moreover, FATE prevents even handcrafted calls to <code>init</code>.</p>"},{"location":"sophia_stdlib/#coinbase","title":"coinbase","text":"<pre><code>Chain.coinbase : address\n</code></pre> <p>The address of the account that mined the current block.</p>"},{"location":"sophia_stdlib/#difficulty","title":"difficulty","text":"<pre><code>Chain.difficulty : int\n</code></pre> <p>The difficulty of the current block.</p>"},{"location":"sophia_stdlib/#event","title":"event","text":"<pre><code>Chain.event(e : event) : unit\n</code></pre> <p>Emits the event. To use this function one needs to define the <code>event</code> type as a <code>datatype</code> in the contract.</p>"},{"location":"sophia_stdlib/#gas_limit","title":"gas_limit","text":"<pre><code>Chain.gas_limit : int\n</code></pre> <p>The gas limit of the current block.</p>"},{"location":"sophia_stdlib/#spend","title":"spend","text":"<pre><code>Chain.spend(to : address, amount : int) : unit\n</code></pre> <p>Spend <code>amount</code> tokens to <code>to</code>. Will fail (and abort the contract) if contract doesn't have <code>amount</code> tokens to transfer, or, if <code>to</code> is not <code>payable</code>.</p>"},{"location":"sophia_stdlib/#timestamp","title":"timestamp","text":"<pre><code>Chain.timestamp : int\n</code></pre> <p>The timestamp of the current block (unix time, milliseconds).</p>"},{"location":"sophia_stdlib/#char","title":"Char","text":""},{"location":"sophia_stdlib/#to_int_1","title":"to_int","text":"<p><code>Char.to_int(c : char) : int <pre><code>Returns the UTF-8 codepoint of a character\n\n\n#### from_int\n</code></pre> Char.from_int(i : int) : option(char)</code></p> <p>Opposite of to_int. Returns <code>None</code> if the integer doesn't correspond to a single (normalized) codepoint.</p>"},{"location":"sophia_stdlib/#contract","title":"Contract","text":"<p>Values related to the current contract</p>"},{"location":"sophia_stdlib/#creator","title":"creator","text":"<pre><code>Contract.creator : address\n</code></pre> <p>Address of the entity that signed the contract creation transaction</p>"},{"location":"sophia_stdlib/#address_1","title":"address","text":"<pre><code>Contract.address : address\n</code></pre> <p>Address of the contract account</p>"},{"location":"sophia_stdlib/#balance_1","title":"balance","text":"<pre><code>Contract.balance : int\n</code></pre> <p>Amount of coins in the contract account</p>"},{"location":"sophia_stdlib/#crypto","title":"Crypto","text":""},{"location":"sophia_stdlib/#sha3","title":"sha3","text":"<pre><code>Crypto.sha3(x : 'a) : hash\n</code></pre> <p>Hash any object to SHA3</p>"},{"location":"sophia_stdlib/#sha256","title":"sha256","text":"<pre><code>Crypto.sha256(x : 'a) : hash\n</code></pre> <p>Hash any object to SHA256</p>"},{"location":"sophia_stdlib/#blake2b","title":"blake2b","text":"<pre><code>Crypto.blake2b(x : 'a) : hash\n</code></pre> <p>Hash any object to blake2b</p>"},{"location":"sophia_stdlib/#verify_sig","title":"verify_sig","text":"<pre><code>Crypto.verify_sig(msg : hash, pubkey : address, sig : signature) : bool\n</code></pre> <p>Checks if the signature of <code>msg</code> was made using private key corresponding to the <code>pubkey</code></p>"},{"location":"sophia_stdlib/#ecverify_secp256k1","title":"ecverify_secp256k1","text":"<pre><code>Crypto.ecverify_secp256k1(msg : hash, addr : bytes(20), sig : bytes(65)) : bool\n</code></pre> <p>Verifies a signature for a msg against an Ethereum style address. Note that the signature should be 65 bytes and include the recovery identifier byte <code>V</code>. The expected organization of the signature is (<code>V || R || S</code>).</p>"},{"location":"sophia_stdlib/#ecrecover_secp256k1","title":"ecrecover_secp256k1","text":"<pre><code>Crypto.ecrecover_secp256k1(msg : hash, sig : bytes(65)) : option(bytes(20))\n</code></pre> <p>Recovers the Ethereum style address from a msg hash and respective ECDSA-signature. Note that the signature should be 65 bytes and include the recovery identifier byte <code>V</code>. The expected organization of the signature is (<code>V || R || S</code>).</p>"},{"location":"sophia_stdlib/#verify_sig_secp256k1","title":"verify_sig_secp256k1","text":"<pre><code>Crypto.verify_sig_secp256k1(msg : hash, pubkey : bytes(64), sig : bytes(64)) : bool\n</code></pre> <p>Verifies a standard 64-byte ECDSA signature (<code>R || S</code>).</p>"},{"location":"sophia_stdlib/#int","title":"Int","text":""},{"location":"sophia_stdlib/#to_str_2","title":"to_str","text":"<pre><code>Int.to_str : int => string\n</code></pre> <p>Casts integer to string using decimal representation</p>"},{"location":"sophia_stdlib/#map","title":"Map","text":""},{"location":"sophia_stdlib/#lookup_1","title":"lookup","text":"<p><code>Map.lookup(k : 'k, m : map('k, 'v)) : option('v)</code></p> <p>Returns the value under a key in given map as <code>Some</code> or <code>None</code> if the key is not present</p>"},{"location":"sophia_stdlib/#lookup_default","title":"lookup_default","text":"<p><code>Map.lookup_default(k : 'k, m : map('k, 'v), v : 'v) : 'v</code></p> <p>Returns the value under a key in given map or the default value <code>v</code> if the key is not present</p>"},{"location":"sophia_stdlib/#member","title":"member","text":"<p><code>Map.member(k : 'k, m : map('k, 'v)) : bool</code></p> <p>Checks if the key is present in the map</p>"},{"location":"sophia_stdlib/#delete","title":"delete","text":"<p><code>Map.delete(k : 'k, m : map('k, 'v)) : map('k, 'v)</code></p> <p>Removes the key from the map</p>"},{"location":"sophia_stdlib/#size","title":"size","text":"<p><code>Map.size(m : map('k, 'v)) : int</code></p> <p>Returns the number of elements in the map</p>"},{"location":"sophia_stdlib/#to_list","title":"to_list","text":"<p><code>Map.to_list(m : map('k, 'v)) : list('k * 'v)</code></p> <p>Returns a list containing pairs of keys and their respective elements.</p>"},{"location":"sophia_stdlib/#from_list","title":"from_list","text":"<p><code>Map.from_list(m : list('k * 'v)) : map('k, 'v)</code></p> <p>Turns a list of pairs of form <code>(key, value)</code> into a map</p>"},{"location":"sophia_stdlib/#oracle","title":"Oracle","text":""},{"location":"sophia_stdlib/#register","title":"register","text":"<pre><code>Oracle.register(<signature : bytes(64)>, acct : address, qfee : int, ttl : Chain.ttl) : oracle('a, 'b)\n</code></pre> <p>Registers new oracle answering questions of type <code>'a</code> with answers of type <code>'b</code>.</p> <ul> <li>The <code>acct</code> is the address of the oracle to register (can be the same as the contract).</li> <li><code>signature</code> is a signature proving that the contract is allowed to register the account - the <code>network id</code> + <code>account address</code> + <code>contract address</code> (concatenated as byte arrays) is signed with the private key of the account, proving you have the private key of the oracle to be. If the address is the same as the contract <code>sign</code> is ignored and can be left out entirely.</li> <li>The <code>qfee</code> is the minimum query fee to be paid by a user when asking a question of the oracle.</li> <li>The <code>ttl</code> is the Time To Live for the oracle in key blocks, either relative to the current key block height (<code>RelativeTTL(delta)</code>) or a fixed key block height (<code>FixedTTL(height)</code>).</li> <li>The type <code>'a</code> is the type of the question to ask.</li> <li>The type <code>'b</code> is the type of the oracle answers.</li> </ul> <p>Examples: <pre><code> Oracle.register(addr0, 25, RelativeTTL(400))\n Oracle.register(addr1, 25, RelativeTTL(500), signature = sign1)\n</code></pre></p>"},{"location":"sophia_stdlib/#get_question","title":"get_question","text":"<pre><code>Oracle.get_question(o : oracle('a, 'b), q : oracle_query('a, 'b)) : 'a\n</code></pre> <p>Checks what was the question of query <code>q</code> on oracle <code>o</code></p>"},{"location":"sophia_stdlib/#respond","title":"respond","text":"<pre><code>Oracle.respond(<signature : bytes(64)>, o : oracle('a, 'b), q : oracle_query('a, 'b), 'b) : unit\n</code></pre> <p>Responds to the question <code>q</code> on <code>o</code>. Unless the contract address is the same as the oracle address the <code>signature</code> (which is an optional, named argument) needs to be provided. Proving that we have the private key of the oracle by signing the <code>network id</code> + <code>oracle query id</code> + <code>contract address</code></p>"},{"location":"sophia_stdlib/#extend","title":"extend","text":"<pre><code>Oracle.extend(<signature : bytes(64)>, o : oracle('a, 'b), ttl : Chain.ttl) : unit\n</code></pre> <p>Extends TTL of an oracle. * <code>singature</code> is a named argument and thus optional. Must be the same as for <code>Oracle.register</code> * <code>o</code> is the oracle being extended * <code>ttl</code> must be <code>RelativeTTL</code>. The time to live of <code>o</code> will be extended by this value.</p>"},{"location":"sophia_stdlib/#query_fee","title":"query_fee","text":"<pre><code>Oracle.query_fee(o : oracle('a, 'b)) : int\n</code></pre> <p>Returns the query fee of the oracle</p>"},{"location":"sophia_stdlib/#query","title":"query","text":"<pre><code>Oracle.query(o : oracle('a, 'b), q : 'a, qfee : int, qttl : Chain.ttl, rttl : Chain.ttl) : oracle_query('a, 'b)\n</code></pre> <p>Asks the oracle a question. * The <code>qfee</code> is the query fee debited to the contract account (<code>Contract.address</code>). * The <code>qttl</code> controls the last height at which the oracle can submit a response and can be either fixed or relative. * The <code>rttl</code> must be relative and controls how long an answer is kept on the chain. The call fails if the oracle could expire before an answer.</p>"},{"location":"sophia_stdlib/#get_answer","title":"get_answer","text":"<pre><code>Oracle.get_answer(o : oracle('a, 'b), q : oracle_query('a, 'b)) : option('b)\n</code></pre> <p>Checks what is the optional query answer</p>"},{"location":"sophia_stdlib/#expiry","title":"expiry","text":"<pre><code>Oracle.expiry(o : oracle('a, 'b)) : int\n</code></pre> <p>Ask the oracle when it expires. The result is the block height at which it will happen.</p>"},{"location":"sophia_stdlib/#check","title":"check","text":"<pre><code>Oracle.check(o : oracle('a, 'b)) : bool\n</code></pre> <p>Returns <code>true</code> iff the oracle <code>o</code> exists and has correct type</p>"},{"location":"sophia_stdlib/#check_query","title":"check_query","text":"<pre><code>Oracle.check_query(o : oracle('a, 'b), q : oracle_query('a, 'b)) : bool\n</code></pre> <p>It returns <code>true</code> iff the oracle query exist and has the expected type.</p>"},{"location":"sophia_stdlib/#includable-namespaces","title":"Includable namespaces","text":"<p>These need to be explicitly included (with <code>.aes</code> suffix)</p>"},{"location":"sophia_stdlib/#bitwise","title":"Bitwise","text":"<p>Bitwise operations on arbitrary precision integers.</p>"},{"location":"sophia_stdlib/#bsr","title":"bsr","text":"<pre><code>Bitwise.bsr(n : int, x : int) : int\n</code></pre> <p>Logical bit shift <code>x</code> right <code>n</code> positions.</p>"},{"location":"sophia_stdlib/#bsl","title":"bsl","text":"<pre><code>Bitwise.bsl(n : int, x : int) : int\n</code></pre> <p>Logical bit shift <code>x</code> left <code>n</code> positions.</p>"},{"location":"sophia_stdlib/#bsli","title":"bsli","text":"<pre><code>Bitwise.bsli(n : int, x : int, lim : int) : int\n</code></pre> <p>Logical bit shift <code>x</code> left <code>n</code> positions, limit to <code>lim</code> bits.</p>"},{"location":"sophia_stdlib/#band","title":"band","text":"<pre><code>Bitwise.band(x : int, y : int) : int\n</code></pre> <p>Bitwise <code>and</code> of <code>x</code> and <code>y</code>.</p>"},{"location":"sophia_stdlib/#bor","title":"bor","text":"<pre><code>Bitwise.bor(x : int, y : int) : int\n</code></pre> <p>Bitwise <code>or</code> of <code>x</code> and <code>y</code>.</p>"},{"location":"sophia_stdlib/#bxor","title":"bxor","text":"<pre><code>Bitwise.bxor(x : int, y : int) : int\n</code></pre> <p>Bitwise <code>xor</code> of <code>x</code> and <code>y</code>.</p>"},{"location":"sophia_stdlib/#bnot","title":"bnot","text":"<pre><code>Bitwise.bnot(x : int) : int\n</code></pre> <p>Bitwise <code>not</code> of <code>x</code>. Defined and implemented as <code>bnot(x) = bxor(x, -1)</code>.</p>"},{"location":"sophia_stdlib/#uband","title":"uband","text":"<pre><code>Bitwise.uband(x : int, y : int) : int\n</code></pre> <p>Bitwise <code>and</code> of non-negative numbers <code>x</code> and <code>y</code>.</p>"},{"location":"sophia_stdlib/#ubor","title":"ubor","text":"<pre><code>Bitwise.ubor(x : int, y : int) : int\n</code></pre> <p>Bitwise <code>or</code> of non-negative <code>x</code> and <code>y</code>.</p>"},{"location":"sophia_stdlib/#ubxor","title":"ubxor","text":"<pre><code>Bitwise.ubxor(x : int, y : int) : int\n</code></pre> <p>Bitwise <code>xor</code> of non-negative <code>x</code> and <code>y</code>.</p>"},{"location":"sophia_stdlib/#bls12_381","title":"BLS12_381","text":""},{"location":"sophia_stdlib/#types_2","title":"Types","text":""},{"location":"sophia_stdlib/#fr","title":"fr","text":"<p>Built-in (Montgomery) integer representation 32 bytes</p>"},{"location":"sophia_stdlib/#fp","title":"fp","text":"<p>Built-in (Montgomery) integer representation 48 bytes</p>"},{"location":"sophia_stdlib/#fp2","title":"fp2","text":"<pre><code>record fp2 = { x1 : fp, x2 : fp }`\n</code></pre>"},{"location":"sophia_stdlib/#g1","title":"g1","text":"<pre><code>record g1 = { x : fp, y : fp, z : fp }\n</code></pre>"},{"location":"sophia_stdlib/#g2","title":"g2","text":"<pre><code>record g2 = { x : fp2, y : fp2, z : fp2 }\n</code></pre>"},{"location":"sophia_stdlib/#gt","title":"gt","text":"<pre><code>record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp, x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp }\n</code></pre>"},{"location":"sophia_stdlib/#functions_2","title":"Functions","text":""},{"location":"sophia_stdlib/#pairing_check","title":"pairing_check","text":"<pre><code>BLS12_381.pairing_check(xs : list(g1), ys : list(g2)) : bool\n</code></pre> <p>Pairing check of a list of points, <code>xs</code> and <code>ys</code> should be of equal length.</p>"},{"location":"sophia_stdlib/#int_to_fr","title":"int_to_fr","text":"<pre><code>BLS12_381.int_to_fr(x : int) : fr\n</code></pre> <p>Convert an integer to an <code>fr</code> - a 32 bytes internal (Montgomery) integer representation.</p>"},{"location":"sophia_stdlib/#int_to_fp","title":"int_to_fp","text":"<pre><code>BLS12_381.int_to_fp(x : int) : fp\n</code></pre> <p>Convert an integer to an <code>fp</code> - a 48 bytes internal (Montgomery) integer representation.</p>"},{"location":"sophia_stdlib/#fr_to_int","title":"fr_to_int","text":"<pre><code>BLS12_381.fr_to_int(x : fr) : int\n</code></pre> <p>Convert a <code>fr</code> value into an integer.</p>"},{"location":"sophia_stdlib/#fp_to_int","title":"fp_to_int","text":"<pre><code>BLS12_381.fp_to_int(x : fp) : int\n</code></pre> <p>Convert a <code>fp</code> value into an integer.</p>"},{"location":"sophia_stdlib/#mk_g1","title":"mk_g1","text":"<pre><code>BLS12_381.mk_g1(x : int, y : int, z : int) : g1\n</code></pre> <p>Construct a <code>g1</code> point from three integers.</p>"},{"location":"sophia_stdlib/#mk_g2","title":"mk_g2","text":"<pre><code>BLS12_381.mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2\n</code></pre> <p>Construct a <code>g2</code> point from six integers.</p>"},{"location":"sophia_stdlib/#g1_neg","title":"g1_neg","text":"<pre><code>BLS12_381.g1_neg(p : g1) : g1\n</code></pre> <p>Negate a <code>g1</code> value.</p>"},{"location":"sophia_stdlib/#g1_norm","title":"g1_norm","text":"<pre><code>BLS12_381.g1_norm(p : g1) : g1\n</code></pre> <p>Normalize a <code>g1</code> value.</p>"},{"location":"sophia_stdlib/#g1_valid","title":"g1_valid","text":"<pre><code>BLS12_381.g1_valid(p : g1) : bool\n</code></pre> <p>Check that a <code>g1</code> value is a group member.</p>"},{"location":"sophia_stdlib/#g1_is_zero","title":"g1_is_zero","text":"<pre><code>BLS12_381.g1_is_zero(p : g1) : bool\n</code></pre> <p>Check if a <code>g1</code> value corresponds to the zero value of the group.</p>"},{"location":"sophia_stdlib/#g1_add","title":"g1_add","text":"<pre><code>BLS12_381.g1_add(p : g1, q : g1) : g1\n</code></pre> <p>Add two <code>g1</code> values.</p>"},{"location":"sophia_stdlib/#g1_mul","title":"g1_mul","text":"<pre><code>BLS12_381.g1_mul(k : fr, p : g1) : g1\n</code></pre> <p>Scalar multiplication for <code>g1</code>.</p>"},{"location":"sophia_stdlib/#g2_neg","title":"g2_neg","text":"<pre><code>BLS12_381.g2_neg(p : g2) : g2\n</code></pre> <p>Negate a <code>g2</code> value.</p>"},{"location":"sophia_stdlib/#g2_norm","title":"g2_norm","text":"<pre><code>BLS12_381.g2_norm(p : g2) : g2\n</code></pre> <p>Normalize a <code>g2</code> value.</p>"},{"location":"sophia_stdlib/#g2_valid","title":"g2_valid","text":"<pre><code>BLS12_381.g2_valid(p : g2) : bool\n</code></pre> <p>Check that a <code>g2</code> value is a group member.</p>"},{"location":"sophia_stdlib/#g2_is_zero","title":"g2_is_zero","text":"<pre><code>BLS12_381.g2_is_zero(p : g2) : bool\n</code></pre> <p>Check if a <code>g2</code> value corresponds to the zero value of the group.</p>"},{"location":"sophia_stdlib/#g2_add","title":"g2_add","text":"<pre><code>BLS12_381.g2_add(p : g2, q : g2) : g2\n</code></pre> <p>Add two <code>g2</code> values.</p>"},{"location":"sophia_stdlib/#g2_mul","title":"g2_mul","text":"<pre><code>BLS12_381.g2_mul(k : fr, p : g2) : g2\n</code></pre> <p>Scalar multiplication for <code>g2</code>.</p>"},{"location":"sophia_stdlib/#gt_inv","title":"gt_inv","text":"<pre><code>BLS12_381.gt_inv(p : gt) : gt\n</code></pre> <p>Invert a <code>gt</code> value.</p>"},{"location":"sophia_stdlib/#gt_add","title":"gt_add","text":"<pre><code>BLS12_381.gt_add(p : gt, q : gt) : gt\n</code></pre> <p>Add two <code>gt</code> values.</p>"},{"location":"sophia_stdlib/#gt_mul","title":"gt_mul","text":"<pre><code>BLS12_381.gt_mul(p : gt, q : gt) : gt\n</code></pre> <p>Multiply two <code>gt</code> values.</p>"},{"location":"sophia_stdlib/#gt_pow","title":"gt_pow","text":"<pre><code>BLS12_381.gt_pow(p : gt, k : fr) : gt\n</code></pre> <p>Calculate exponentiation <code>p ^ k</code>.</p>"},{"location":"sophia_stdlib/#gt_is_one","title":"gt_is_one","text":"<pre><code>BLS12_381.gt_is_one(p : gt) : bool\n</code></pre> <p>Compare a <code>gt</code> value to the unit value of the Gt group.</p>"},{"location":"sophia_stdlib/#pairing","title":"pairing","text":"<pre><code>BLS12_381.pairing(p : g1, q : g2) : gt\n</code></pre> <p>Compute the pairing of a <code>g1</code> value and a <code>g2</code> value.</p>"},{"location":"sophia_stdlib/#miller_loop","title":"miller_loop","text":"<pre><code>BLS12_381.miller_loop(p : g1, q : g2) : gt\n</code></pre> <p>Do the Miller loop stage of pairing for <code>g1</code> and <code>g2</code>.</p>"},{"location":"sophia_stdlib/#final_exp","title":"final_exp","text":"<pre><code>BLS12_381.final_exp(p : gt) : gt\n</code></pre> <p>Perform the final exponentiation step of pairing for a <code>gt</code> value.</p>"},{"location":"sophia_stdlib/#func","title":"Func","text":"<p>Functional combinators.</p>"},{"location":"sophia_stdlib/#id","title":"id","text":"<pre><code>Func.id(x : 'a) : 'a\n</code></pre> <p>Identity function. Returns its argument.</p>"},{"location":"sophia_stdlib/#const","title":"const","text":"<pre><code>Func.const(x : 'a) : 'b => 'a = (y) => x\n</code></pre> <p>Constant function constructor. Given <code>x</code> returns a function that returns <code>x</code> regardless of its argument.</p>"},{"location":"sophia_stdlib/#flip","title":"flip","text":"<pre><code>Func.flip(f : ('a, 'b) => 'c) : ('b, 'a) => 'c\n</code></pre> <p>Switches order of arguments of arity 2 function.</p>"},{"location":"sophia_stdlib/#comp","title":"comp","text":"<pre><code>Func.comp(f : 'b => 'c, g : 'a => 'b) : 'a => 'c\n</code></pre> <p>Function composition. <code>comp(f, g)(x) == f(g(x))</code>.</p>"},{"location":"sophia_stdlib/#pipe","title":"pipe","text":"<pre><code>Func.pipe(f : 'a => 'b, g : 'b => 'c) : 'a => 'c\n</code></pre> <p>Flipped function composition. <code>pipe(f, g)(x) == g(f(x))</code>.</p>"},{"location":"sophia_stdlib/#rapply","title":"rapply","text":"<pre><code>Func.rapply(x : 'a, f : 'a => 'b) : 'b\n</code></pre> <p>Reverse application. <code>rapply(x, f) == f(x)</code>.</p>"},{"location":"sophia_stdlib/#recur","title":"recur","text":"<pre><code>Func.recur(f : ('arg => 'res, 'arg) => 'res) : 'arg => 'res\n</code></pre> <p>The Z combinator. Allows performing local recursion and having anonymous recursive lambdas. To make function <code>A => B</code> recursive the user needs to transform it to take two arguments instead \u2013 one of type <code>A => B</code> which is going to work as a self-reference, and the other one of type <code>A</code> which is the original argument. Therefore, transformed function should have <code>(A => B, A) => B</code> signature.</p> <p>Example usage: <pre><code>let factorial = recur((fac, n) => if(n < 2) 1 else n * fac(n - 1))\n</code></pre></p> <p>If the function is going to take more than one argument it will need to be either tuplified or have curried out latter arguments.</p> <p>Example (factorial with custom step):</p> <pre><code>// tuplified version\nlet factorial_t(n, step) =\n let fac(rec, args) =\n let (n, step) = args\n if(n < 2) 1 else n * rec((n - step, step))\n recur(fac)((n, step))\n\n// curried version\nlet factorial_c(n, step) =\n let fac(rec, n) = (step) =>\n if(n < 2) 1 else n * rec(n - 1)(step)\n recur(fac)(n)(step)\n</code></pre>"},{"location":"sophia_stdlib/#iter","title":"iter","text":"<pre><code>Func.iter(n : int, f : 'a => 'a) : 'a => 'a\n</code></pre> <p><code>n</code>th composition of f with itself, for instance <code>iter(3, f)</code> is equivalent to <code>(x) => f(f(f(x)))</code>.</p>"},{"location":"sophia_stdlib/#curry","title":"curry","text":"<pre><code>Func.curry2(f : ('a, 'b) => 'c) : 'a => ('b => 'c)\nFunc.curry3(f : ('a, 'b, 'c) => 'd) : 'a => ('b => ('c => 'd))\n</code></pre> <p>Turns a function that takes n arguments into a curried function that takes one argument and returns a function that waits for the rest in the same manner. For instance <code>curry2((a, b) => a + b)(1)(2) == 3</code>.</p>"},{"location":"sophia_stdlib/#uncurry","title":"uncurry","text":"<pre><code>Func.uncurry2(f : 'a => ('b => 'c)) : ('a, 'b) => 'c\nFunc.uncurry3(f : 'a => ('b => ('c => 'd))) : ('a, 'b, 'c) => 'd\n</code></pre> <p>Opposite to curry.</p>"},{"location":"sophia_stdlib/#tuplify","title":"tuplify","text":"<pre><code>Func.tuplify2(f : ('a, 'b) => 'c) : (('a * 'b)) => 'c\nFunc.tuplify3(f : ('a, 'b, 'c) => 'd) : 'a * 'b * 'c => 'd\n</code></pre> <p>Turns a function that takes n arguments into a function that takes an n-tuple.</p>"},{"location":"sophia_stdlib/#untuplify","title":"untuplify","text":"<pre><code>Func.untuplify2(f : 'a * 'b => 'c) : ('a, 'b) => 'c\nFunc.untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd\n</code></pre> <p>Opposite to tuplify.</p>"},{"location":"sophia_stdlib/#frac","title":"Frac","text":"<p>This namespace provides operations on rational numbers. A rational number is represented as a fraction of two integers which are stored internally in the <code>frac</code> datatype.</p> <p>The datatype consists of three constructors <code>Neg/2</code>, <code>Zero/0</code> and <code>Pos/2</code> which determine the sign of the number. Both values stored in <code>Neg</code> and <code>Pos</code> need to be strictly positive integers. However, when creating a <code>frac</code> you should never use the constructors explicitly. Instead of that, always use provided functions like <code>make_frac</code> or <code>from_int</code>. This helps keeping the internal representation well defined.</p> <p>The described below functions take care of the normalization of the fractions \u2013 they won't grow if it is unnecessary. Please note that the size of <code>frac</code> can be still very big while the value is actually very close to a natural number \u2013 the division of two extremely big prime numbers will be as big as both of them. To face this issue the optimize function is provided. It will approximate the value of the fraction to fit in the given error margin and to shrink its size as much as possible.</p> <p>Important note: <code>frac</code> must not be compared using standard <code><</code>-like operators. The operator comparison is not possible to overload at this moment, nor the language provides checkers to prevent unintended usage of them. Therefore the typechecker will allow that and the results of such comparison will be unspecified. You should use lt, geq, eq etc instead.</p>"},{"location":"sophia_stdlib/#types_3","title":"Types","text":""},{"location":"sophia_stdlib/#frac_1","title":"frac","text":"<pre><code>datatype frac = Pos(int, int) | Zero | Neg(int, int)\n</code></pre> <p>Internal representation of fractional numbers. First integer encodes the numerator and the second the denominator \u2013 both must be always positive, as the sign is being handled by the choice of the constructor.</p>"},{"location":"sophia_stdlib/#functions_3","title":"Functions","text":""},{"location":"sophia_stdlib/#make_frac","title":"make_frac","text":"<p><code>Frac.make_frac(n : int, d : int) : frac</code></p> <p>Creates a fraction out of numerator and denominator. Automatically normalizes, so <code>make_frac(2, 4)</code> and <code>make_frac(1, 2)</code> will yield same results.</p>"},{"location":"sophia_stdlib/#num","title":"num","text":"<p><code>Frac.num(f : frac) : int</code></p> <p>Returns the numerator of a fraction.</p>"},{"location":"sophia_stdlib/#den","title":"den","text":"<p><code>Frac.den(f : frac) : int</code></p> <p>Returns the denominator of a fraction.</p>"},{"location":"sophia_stdlib/#to_pair","title":"to_pair","text":"<p><code>Frac.to_pair(f : frac) : int * int</code></p> <p>Turns a fraction into a pair of numerator and denominator.</p>"},{"location":"sophia_stdlib/#sign","title":"sign","text":"<p><code>Frac.sign(f : frac) : int</code></p> <p>Returns the signum of a fraction, -1, 0, 1 if negative, zero, positive respectively.</p>"},{"location":"sophia_stdlib/#to_str_3","title":"to_str","text":"<p><code>Frac.to_str(f : frac) : string</code></p> <p>Conversion to string. Does not display division by 1 or denominator if equals zero.</p>"},{"location":"sophia_stdlib/#simplify","title":"simplify","text":"<p><code>Frac.simplify(f : frac) : frac</code></p> <p>Reduces fraction to normal form if for some reason it is not in it.</p>"},{"location":"sophia_stdlib/#eq","title":"eq","text":"<p><code>Frac.eq(a : frac, b : frac) : bool</code></p> <p>Checks if <code>a</code> is equal to <code>b</code>.</p>"},{"location":"sophia_stdlib/#neq","title":"neq","text":"<p><code>Frac.neq(a : frac, b : frac) : bool</code></p> <p>Checks if <code>a</code> is not equal to <code>b</code>.</p>"},{"location":"sophia_stdlib/#geq","title":"geq","text":"<p><code>Frac.geq(a : frac, b : frac) : bool</code></p> <p>Checks if <code>a</code> is greater or equal to <code>b</code>.</p>"},{"location":"sophia_stdlib/#leq","title":"leq","text":"<p><code>Frac.leq(a : frac, b : frac) : bool</code></p> <p>Checks if <code>a</code> is lesser or equal to <code>b</code>.</p>"},{"location":"sophia_stdlib/#gt_1","title":"gt","text":"<p><code>Frac.gt(a : frac, b : frac) : bool</code></p> <p>Checks if <code>a</code> is greater than <code>b</code>.</p>"},{"location":"sophia_stdlib/#lt","title":"lt","text":"<p><code>Frac.lt(a : frac, b : frac) : bool</code></p> <p>Checks if <code>a</code> is lesser than <code>b</code>.</p>"},{"location":"sophia_stdlib/#min","title":"min","text":"<p><code>Frac.min(a : frac, b : frac) : frac</code></p> <p>Chooses lesser of the two fractions.</p>"},{"location":"sophia_stdlib/#max","title":"max","text":"<p><code>Frac.max(a : frac, b : frac) : frac</code></p> <p>Chooses greater of the two fractions.</p>"},{"location":"sophia_stdlib/#abs","title":"abs","text":"<p><code>Frac.abs(f : frac) : frac</code></p> <p>Absolute value.</p>"},{"location":"sophia_stdlib/#from_int","title":"from_int","text":"<p><code>Frac.from_int(n : int) : frac</code></p> <p>From integer conversion. Effectively <code>make_frac(n, 1)</code>.</p>"},{"location":"sophia_stdlib/#floor","title":"floor","text":"<p><code>Frac.floor(f : frac) : int</code></p> <p>Rounds a fraction to the nearest lesser or equal integer.</p>"},{"location":"sophia_stdlib/#ceil","title":"ceil","text":"<p><code>Frac.ceil(f : frac) : int</code></p> <p>Rounds a fraction to the nearest greater or equal integer.</p>"},{"location":"sophia_stdlib/#round_to_zero","title":"round_to_zero","text":"<p><code>Frac.round_to_zero(f : frac) : int</code></p> <p>Rounds a fraction towards zero. Effectively <code>ceil</code> if lesser than zero and <code>floor</code> if greater.</p>"},{"location":"sophia_stdlib/#round_from_zero","title":"round_from_zero","text":"<p><code>Frac.round_from_zero(f : frac) : int</code></p> <p>Rounds a fraction from zero. Effectively <code>ceil</code> if greater than zero and <code>floor</code> if lesser.</p>"},{"location":"sophia_stdlib/#round","title":"round","text":"<p><code>Frac.round(f : frac) : int</code></p> <p>Rounds a fraction to a nearest integer. If two integers are in the same distance it will choose the even one.</p>"},{"location":"sophia_stdlib/#add","title":"add","text":"<p><code>Frac.add(a : frac, b : frac) : frac</code></p> <p>Sum of the fractions.</p>"},{"location":"sophia_stdlib/#neg","title":"neg","text":"<p><code>Frac.neg(a : frac) : frac</code></p> <p>Negation of the fraction.</p>"},{"location":"sophia_stdlib/#sub","title":"sub","text":"<p><code>Frac.sub(a : frac, b : frac) : frac</code></p> <p>Subtraction of two fractions.</p>"},{"location":"sophia_stdlib/#inv","title":"inv","text":"<p><code>Frac.inv(a : frac) : frac</code></p> <p>Inverts a fraction. Throws error if <code>a</code> is zero.</p>"},{"location":"sophia_stdlib/#mul","title":"mul","text":"<p><code>Frac.mul(a : frac, b : frac) : frac</code></p> <p>Multiplication of two fractions.</p>"},{"location":"sophia_stdlib/#div","title":"div","text":"<p><code>Frac.div(a : frac, b : frac) : frac</code></p> <p>Division of two fractions.</p>"},{"location":"sophia_stdlib/#int_exp","title":"int_exp","text":"<p><code>Frac.int_exp(b : frac, e : int) : frac</code></p> <p>Takes <code>b</code> to the power of <code>e</code>. The exponent can be a negative value.</p>"},{"location":"sophia_stdlib/#optimize","title":"optimize","text":"<p><code>Frac.optimize(f : frac, loss : frac) : frac</code></p> <p>Shrink the internal size of a fraction as much as possible by approximating it to the point where the error would exceed the <code>loss</code> value.</p>"},{"location":"sophia_stdlib/#is_sane","title":"is_sane","text":"<p><code>Frac.is_sane(f : frac) : bool</code></p> <p>For debugging. If it ever returns false in a code that doesn't call <code>frac</code> constructors or accept arbitrary <code>frac</code>s from the surface you should report it as a bug</p> <p>If you expect getting calls with malformed <code>frac</code>s in your contract, you should use this function to verify the input.</p>"},{"location":"sophia_stdlib/#list","title":"List","text":"<p>This module contains common operations on lists like constructing, querying, traversing etc.</p>"},{"location":"sophia_stdlib/#is_empty","title":"is_empty","text":"<pre><code>List.is_empty(l : list('a)) : bool\n</code></pre> <p>Returns <code>true</code> iff the list is equal to <code>[]</code>.</p>"},{"location":"sophia_stdlib/#first","title":"first","text":"<pre><code>List.first(l : list('a)) : option('a)\n</code></pre> <p>Returns <code>Some</code> of the first element of a list or <code>None</code> if the list is empty.</p>"},{"location":"sophia_stdlib/#tail","title":"tail","text":"<pre><code>List.tail(l : list('a)) : option(list('a))\n</code></pre> <p>Returns <code>Some</code> of a list without its first element or <code>None</code> if the list is empty.</p>"},{"location":"sophia_stdlib/#last","title":"last","text":"<pre><code>List.last(l : list('a)) : option('a)\n</code></pre> <p>Returns <code>Some</code> of the last element of a list or <code>None</code> if the list is empty.</p>"},{"location":"sophia_stdlib/#contains","title":"contains","text":"<p><pre><code>List.contains(e : 'a, l : list('a)) : bool\n</code></pre> Checks if list <code>l</code> contains element <code>e</code>. Equivalent to <code>List.find(x => x == e, l) != None</code>.</p>"},{"location":"sophia_stdlib/#find","title":"find","text":"<pre><code>List.find(p : 'a => bool, l : list('a)) : option('a)\n</code></pre> <p>Finds first element of <code>l</code> fulfilling predicate <code>p</code> as <code>Some</code> or <code>None</code> if no such element exists.</p>"},{"location":"sophia_stdlib/#find_indices","title":"find_indices","text":"<pre><code>List.find_indices(p : 'a => bool, l : list('a)) : list(int)\n</code></pre> <p>Returns list of all indices of elements from <code>l</code> that fulfill the predicate <code>p</code>.</p>"},{"location":"sophia_stdlib/#nth","title":"nth","text":"<pre><code>List.nth(n : int, l : list('a)) : option('a)\n</code></pre> <p>Gets <code>n</code>th element of <code>l</code> as <code>Some</code> or <code>None</code> if <code>l</code> is shorter than <code>n + 1</code> or <code>n</code> is negative.</p>"},{"location":"sophia_stdlib/#get","title":"get","text":"<pre><code>List.get(n : int, l : list('a)) : 'a\n</code></pre> <p>Gets <code>n</code>th element of <code>l</code> forcefully, throwing and error if <code>l</code> is shorter than <code>n + 1</code> or <code>n</code> is negative.</p>"},{"location":"sophia_stdlib/#length","title":"length","text":"<pre><code>List.length(l : list('a)) : int\n</code></pre> <p>Returns length of a list.</p>"},{"location":"sophia_stdlib/#from_to","title":"from_to","text":"<pre><code>List.from_to(a : int, b : int) : list(int)\n</code></pre> <p>Creates an ascending sequence of all integer numbers between <code>a</code> and <code>b</code> (including <code>a</code> and <code>b</code>).</p>"},{"location":"sophia_stdlib/#from_to_step","title":"from_to_step","text":"<pre><code>List.from_to_step(a : int, b : int, step : int) : list(int)\n</code></pre> <p>Creates an ascending sequence of integer numbers betweeen <code>a</code> and <code>b</code> jumping by given <code>step</code>. Includes <code>a</code> and takes <code>b</code> only if <code>(b - a) mod step == 0</code>. <code>step</code> should be bigger than 0.</p>"},{"location":"sophia_stdlib/#replace_at","title":"replace_at","text":"<pre><code>List.replace_at(n : int, e : 'a, l : list('a)) : list('a)\n</code></pre> <p>Replaces <code>n</code>th element of <code>l</code> with <code>e</code>. Throws an error if <code>n</code> is negative or would cause an overflow.</p>"},{"location":"sophia_stdlib/#insert_at","title":"insert_at","text":"<pre><code>List.insert_at(n : int, e : 'a, l : list('a)) : list('a)\n</code></pre> <p>Inserts <code>e</code> into <code>l</code> to be on position <code>n</code> by shifting following elements further. For instance, <pre><code>insert_at(2, 9, [1,2,3,4])\n</code></pre> will yield <code>[1,2,9,3,4]</code>.</p>"},{"location":"sophia_stdlib/#insert_by","title":"insert_by","text":"<pre><code>List.insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a)\n</code></pre> <p>Assuming that cmp represents <code><</code> comparison, inserts <code>x</code> before the first element in the list <code>l</code> which is greater than it. For instance, <pre><code>insert_by((a, b) => a < b, 4, [1,2,3,5,6,7])\n</code></pre> will yield <code>[1,2,3,4,5,6,7]</code></p>"},{"location":"sophia_stdlib/#foldr","title":"foldr","text":"<pre><code>List.foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b\n</code></pre> <p>Right fold of a list. Assuming <code>l = [x, y, z]</code> will return <code>f(x, f(y, f(z, nil)))</code>. Not tail recursive.</p>"},{"location":"sophia_stdlib/#foldl","title":"foldl","text":"<pre><code>List.foldl(rcons : ('b, 'a) => 'b, acc : 'b, l : list('a)) : 'b\n</code></pre> <p>Left fold of a list. Assuming <code>l = [x, y, z]</code> will return <code>f(f(f(acc, x), y), z)</code>. Tail recursive.</p>"},{"location":"sophia_stdlib/#foreach","title":"foreach","text":"<pre><code>List.foreach(l : list('a), f : 'a => unit) : unit\n</code></pre> <p>Evaluates <code>f</code> on each element of a list.</p>"},{"location":"sophia_stdlib/#reverse","title":"reverse","text":"<pre><code>List.reverse(l : list('a)) : list('a)\n</code></pre> <p>Returns a copy of <code>l</code> with reversed order of elements.</p>"},{"location":"sophia_stdlib/#map_1","title":"map","text":"<pre><code>List.map(f : 'a => 'b, l : list('a)) : list('b)\n</code></pre> <p>Maps function <code>f</code> over a list. For instance <pre><code>map((x) => x == 0, [1, 2, 0, 3, 0])\n</code></pre> will yield <code>[false, false, true, false, true]</code></p>"},{"location":"sophia_stdlib/#flat_map","title":"flat_map","text":"<pre><code>List.flat_map(f : 'a => list('b), l : list('a)) : list('b)\n</code></pre> <p>Maps <code>f</code> over a list and then flattens it. For instance <pre><code>flat_map((x) => [x, x * 10], [1, 2, 3])\n</code></pre> will yield <code>[1, 10, 2, 20, 3, 30]</code></p>"},{"location":"sophia_stdlib/#filter","title":"filter","text":"<pre><code>List.filter(p : 'a => bool, l : list('a)) : list('a)\n</code></pre> <p>Filters out elements of <code>l</code> that fulfill predicate <code>p</code>. For instance <pre><code>filter((x) => x > 0, [-1, 1, -2, 0, 1, 2, -3])\n</code></pre> will yield <code>[1, 1, 2]</code></p>"},{"location":"sophia_stdlib/#take","title":"take","text":"<pre><code>List.take(n : int, l : list('a)) : list('a)\n</code></pre> <p>Takes <code>n</code> first elements of <code>l</code>. Fails if <code>n</code> is negative. If <code>n</code> is greater than length of a list it will return whole list.</p>"},{"location":"sophia_stdlib/#drop","title":"drop","text":"<pre><code>List.drop(n : int, l : list('a)) : list('a)\n</code></pre> <p>Removes <code>n</code> first elements of <code>l</code>. Fails if <code>n</code> is negative. If <code>n</code> is greater than length of a list it will return <code>[]</code>.</p>"},{"location":"sophia_stdlib/#take_while","title":"take_while","text":"<pre><code>List.take_while(p : 'a => bool, l : list('a)) : list('a)\n</code></pre> <p>Returns longest prefix of <code>l</code> in which all elements fulfill <code>p</code>.</p>"},{"location":"sophia_stdlib/#drop_while","title":"drop_while","text":"<pre><code>List.drop_while(p : 'a => bool, l : list('a)) : list('a)\n</code></pre> <p>Removes longest prefix from <code>l</code> in which all elements fulfill <code>p</code>.</p>"},{"location":"sophia_stdlib/#partition","title":"partition","text":"<pre><code>List.partition(p : 'a => bool, l : list('a)) : (list('a) * list('a))\n</code></pre> <p>Separates elements of <code>l</code> that fulfill <code>p</code> and these that do not. Elements fulfilling predicate will be in the right list. For instance <pre><code>partition((x) => x > 0, [-1, 1, -2, 0, 1, 2, -3])\n</code></pre> will yield <code>([1, 1, 2], [-1, -2, 0, -3])</code></p>"},{"location":"sophia_stdlib/#flatten","title":"flatten","text":"<pre><code>List.flatten(ll : list(list('a))) : list('a)\n</code></pre> <p>Flattens a list of lists into a one list.</p>"},{"location":"sophia_stdlib/#all_1","title":"all","text":"<pre><code>List.all(p : 'a => bool, l : list('a)) : bool\n</code></pre> <p>Checks if all elements of a list fulfill predicate <code>p</code>.</p>"},{"location":"sophia_stdlib/#any","title":"any","text":"<pre><code>List.any(p : 'a => bool, l : list('a)) : bool\n</code></pre> <p>Checks if any element of a list fulfills predicate <code>p</code>.</p>"},{"location":"sophia_stdlib/#sum_1","title":"sum","text":"<pre><code>List.sum(l : list(int)) : int\n</code></pre> <p>Sums elements of a list. Returns 0 if the list is empty.</p>"},{"location":"sophia_stdlib/#product","title":"product","text":"<pre><code>List.product(l : list(int)) : int\n</code></pre> <p>Multiplies elements of a list. Returns 1 if the list is empty.</p>"},{"location":"sophia_stdlib/#zip_with","title":"zip_with","text":"<pre><code>List.zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c)\n</code></pre> <p>\"zips\" two lists with a function. n-th element of resulting list will be equal to <code>f(x1, x2)</code> where <code>x1</code> and <code>x2</code> are n-th elements of <code>l1</code> and <code>l2</code> respectively. Will cut off the tail of the longer list. For instance <pre><code>zip_with((a, b) => a + b, [1,2], [1,2,3])\n</code></pre> will yield <code>[2,4]</code></p>"},{"location":"sophia_stdlib/#zip","title":"zip","text":"<pre><code>List.zip(l1 : list('a), l2 : list('b)) : list('a * 'b)\n</code></pre> <p>Special case of zip_with where the zipping function is <code>(a, b) => (a, b)</code>.</p>"},{"location":"sophia_stdlib/#unzip","title":"unzip","text":"<pre><code>List.unzip(l : list('a * 'b)) : list('a) * list('b)\n</code></pre> <p>Opposite to the <code>zip</code> operation. Takes a list of pairs and returns pair of lists with respective elements on same indices.</p>"},{"location":"sophia_stdlib/#merge","title":"merge","text":"<pre><code>List.merge(lesser_cmp : ('a, 'a) => bool, l1 : list('a), l2 : list('a)) : list('a)\n</code></pre> <p>Merges two sorted lists into a single sorted list. O(length(l1) + length(l2))</p>"},{"location":"sophia_stdlib/#sort","title":"sort","text":"<pre><code>List.sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a)\n</code></pre> <p>Sorts a list using given comparator. <code>lesser_cmp(x, y)</code> should return <code>true</code> iff <code>x < y</code>. If <code>lesser_cmp</code> is not transitive or there exists an element <code>x</code> such that <code>lesser_cmp(x, x)</code> or there exists a pair of elements <code>x</code> and <code>y</code> such that <code>lesser_cmp(x, y) && lesser_cmp(y, x)</code> then the result is undefined. O(length(l) * log_2(length(l))).</p>"},{"location":"sophia_stdlib/#intersperse","title":"intersperse","text":"<pre><code>List.intersperse(delim : 'a, l : list('a)) : list('a)\n</code></pre> <p>Intersperses elements of <code>l</code> with <code>delim</code>. Does nothing on empty lists and singletons. For instance <pre><code>intersperse(0, [1, 2, 3, 4])\n</code></pre> will yield <code>[1, 0, 2, 0, 3, 0, 4]</code></p>"},{"location":"sophia_stdlib/#enumerate","title":"enumerate","text":"<pre><code>List.enumerate(l : list('a)) : list(int * 'a)\n</code></pre> <p>Equivalent to zip with <code>[0..length(l)]</code>, but slightly faster.</p>"},{"location":"sophia_stdlib/#option","title":"Option","text":"<p>Common operations on <code>option</code> types and lists of <code>option</code>s.</p>"},{"location":"sophia_stdlib/#is_none","title":"is_none","text":"<pre><code>Option.is_none(o : option('a)) : bool\n</code></pre> <p>Returns true iff <code>o == None</code></p>"},{"location":"sophia_stdlib/#is_some","title":"is_some","text":"<pre><code>Option.is_some(o : option('a)) : bool\n</code></pre> <p>Returns true iff <code>o</code> is not <code>None</code>.</p>"},{"location":"sophia_stdlib/#match","title":"match","text":"<pre><code>Option.match(n : 'b, s : 'a => 'b, o : option('a)) : 'b\n</code></pre> <p>Behaves like pattern matching on <code>option</code> using two case functions.</p>"},{"location":"sophia_stdlib/#default","title":"default","text":"<pre><code>Option.default(def : 'a, o : option('a)) : 'a\n</code></pre> <p>Escapes <code>option</code> wrapping by providing default value for <code>None</code>.</p>"},{"location":"sophia_stdlib/#force","title":"force","text":"<pre><code>Option.force(o : option('a)) : 'a\n</code></pre> <p>Forcefully escapes the <code>option</code> wrapping assuming it is <code>Some</code>. Aborts on <code>None</code>.</p>"},{"location":"sophia_stdlib/#force_msg","title":"force_msg","text":"<pre><code>Option.force_msg(o : option('a), err : string) : 'a\n</code></pre> <p>Forcefully escapes the <code>option</code> wrapping assuming it is <code>Some</code>. Aborts with <code>err</code> error message on <code>None</code>.</p>"},{"location":"sophia_stdlib/#contains_1","title":"contains","text":"<p><pre><code>Option.contains(e : 'a, o : option('a)) : bool\n</code></pre> Returns <code>true</code> if and only if <code>o</code> contains element equal to <code>e</code>. Equivalent to <code>Option.match(false, x => x == e, o)</code>.</p>"},{"location":"sophia_stdlib/#on_elem","title":"on_elem","text":"<pre><code>Option.on_elem(o : option('a), f : 'a => unit) : unit\n</code></pre> <p>Evaluates <code>f</code> on element under <code>Some</code>. Does nothing on <code>None</code>.</p>"},{"location":"sophia_stdlib/#map_2","title":"map","text":"<pre><code>Option.map(f : 'a => 'b, o : option('a)) : option('b)\n</code></pre> <p>Maps element under <code>Some</code>. Leaves <code>None</code> unchanged.</p>"},{"location":"sophia_stdlib/#map2","title":"map2","text":"<pre><code>Option.map2(f : ('a, 'b) => 'c, o1 : option('a), o2 : option('b)) : option('c)\n</code></pre> <p>Applies arity 2 function over two <code>option</code>s' elements. Returns <code>Some</code> iff both of <code>o1</code> and <code>o2</code> were <code>Some</code>, or <code>None</code> otherwise. For instance <pre><code>map2((a, b) => a + b, Some(1), Some(2))\n</code></pre> will yield <code>Some(3)</code> and <pre><code>map2((a, b) => a + b, Some(1), None)\n</code></pre> will yield <code>None</code>.</p>"},{"location":"sophia_stdlib/#map3","title":"map3","text":"<pre><code>Option.map3(f : ('a, 'b, 'c) => 'd, o1 : option('a), o2 : option('b), o3 : option('c)) : option('d)\n</code></pre> <p>Same as map2 but with arity 3 function.</p>"},{"location":"sophia_stdlib/#app_over","title":"app_over","text":"<pre><code>Option.app_over(f : option ('a => 'b), o : option('a)) : option('b)\n</code></pre> <p>Applies function under <code>option</code> over argument under <code>option</code>. If either of them is <code>None</code> the result will be <code>None</code> as well. For instance <pre><code>app_over(Some((x) => x + 1), Some(1))\n</code></pre> will yield <code>Some(2)</code> and <pre><code>app_over(Some((x) => x + 1), None)\n</code></pre> will yield <code>None</code>.</p>"},{"location":"sophia_stdlib/#flat_map_1","title":"flat_map","text":"<pre><code>Option.flat_map(f : 'a => option('b), o : option('a)) : option('b)\n</code></pre> <p>Performs monadic bind on an <code>option</code>. Extracts element from <code>o</code> (if present) and forms new <code>option</code> from it. For instance <pre><code>flat_map((x) => Some(x + 1), Some(1))\n</code></pre> will yield <code>Some(2)</code> and <pre><code>flat_map((x) => Some(x + 1), None)\n</code></pre> will yield <code>None</code>.</p>"},{"location":"sophia_stdlib/#to_list_1","title":"to_list","text":"<pre><code>Option.to_list(o : option('a)) : list('a)\n</code></pre> <p>Turns <code>o</code> into an empty (if <code>None</code>) or singleton (if <code>Some</code>) list.</p>"},{"location":"sophia_stdlib/#filter_options","title":"filter_options","text":"<pre><code>Option.filter_options(l : list(option('a))) : list('a)\n</code></pre> <p>Removes <code>None</code>s from list and unpacks all remaining <code>Some</code>s. For instance <pre><code>filter_options([Some(1), None, Some(2)])\n</code></pre> will yield <code>[1, 2]</code>.</p>"},{"location":"sophia_stdlib/#seq_options","title":"seq_options","text":"<pre><code>Option.seq_options(l : list (option('a))) : option (list('a))\n</code></pre> <p>Tries to unpack all elements of a list from <code>Some</code>s. Returns <code>None</code> if at least element of <code>l</code> is <code>None</code>. For instance <pre><code>seq_options([Some(1), Some(2)])\n</code></pre> will yield <code>Some([1, 2])</code>, but <pre><code>seq_options([Some(1), Some(2), None])\n</code></pre> will yield <code>None</code>.</p>"},{"location":"sophia_stdlib/#choose","title":"choose","text":"<pre><code>Option.choose(o1 : option('a), o2 : option('a)) : option('a)\n</code></pre> <p>Out of two <code>option</code>s choose the one that is <code>Some</code>, or <code>None</code> if both are <code>None</code>s.</p>"},{"location":"sophia_stdlib/#choose_first","title":"choose_first","text":"<pre><code>Option.choose_first(l : list(option('a))) : option('a)\n</code></pre> <p>Same as choose, but chooses from a list insted of two arguments.</p>"},{"location":"sophia_stdlib/#pair","title":"Pair","text":"<p>Common operations on 2-tuples.</p>"},{"location":"sophia_stdlib/#fst","title":"fst","text":"<pre><code>Pair.fst(t : ('a * 'b)) : 'a\n</code></pre> <p>First element projection.</p>"},{"location":"sophia_stdlib/#snd","title":"snd","text":"<pre><code>Pair.snd(t : ('a * 'b)) : 'b\n</code></pre> <p>Second element projection.</p>"},{"location":"sophia_stdlib/#map1","title":"map1","text":"<pre><code>Pair.map1(f : 'a => 'c, t : ('a * 'b)) : ('c * 'b)\n</code></pre> <p>Applies function over first element.</p>"},{"location":"sophia_stdlib/#map2_1","title":"map2","text":"<pre><code>Pair.map2(f : 'b => 'c, t : ('a * 'b)) : ('a * 'c)\n</code></pre> <p>Applies function over second element.</p>"},{"location":"sophia_stdlib/#bimap","title":"bimap","text":"<pre><code>Pair.bimap(f : 'a => 'c, g : 'b => 'd, t : ('a * 'b)) : ('c * 'd)\n</code></pre> <p>Applies functions over respective elements.</p>"},{"location":"sophia_stdlib/#swap","title":"swap","text":"<pre><code>Pair.swap(t : ('a * 'b)) : ('b * 'a)\n</code></pre> <p>Swaps elements.</p>"},{"location":"sophia_stdlib/#set_1","title":"Set","text":""},{"location":"sophia_stdlib/#types_4","title":"Types","text":"<pre><code>record set('a) = { to_map : map('a, unit) }\n</code></pre>"},{"location":"sophia_stdlib/#functions_4","title":"Functions","text":""},{"location":"sophia_stdlib/#new","title":"new","text":"<pre><code>Set.new() : set('a)\n</code></pre> <p>Returns an empty set</p>"},{"location":"sophia_stdlib/#member_1","title":"member","text":"<pre><code>member(e : 'a, s : set('a)) : bool\n</code></pre> <p>Checks if the element <code>e</code> is present in the set <code>s</code></p>"},{"location":"sophia_stdlib/#insert","title":"insert","text":"<pre><code>insert(e : 'a, s : set('a)) : set('a)\n</code></pre> <p>Inserts the element <code>e</code> in the set <code>s</code></p>"},{"location":"sophia_stdlib/#delete_1","title":"delete","text":"<pre><code>Set.delete(e : 'a, s : set('a)) : set('a)\n</code></pre> <p>Removes the element <code>e</code> from the set <code>s</code></p>"},{"location":"sophia_stdlib/#size_1","title":"size","text":"<pre><code>size(s : set('a)) : int\n</code></pre> <p>Returns the number of elements in the set <code>s</code></p>"},{"location":"sophia_stdlib/#to_list_2","title":"to_list","text":"<pre><code>Set.to_list(s : set('a)) : list('a)\n</code></pre> <p>Returns a list containing the elements of the set <code>s</code></p>"},{"location":"sophia_stdlib/#from_list_1","title":"from_list","text":"<pre><code>Set.from_list(l : list('a)) : set('a)\n</code></pre> <p>Turns the list <code>l</code> into a set</p>"},{"location":"sophia_stdlib/#filter_1","title":"filter","text":"<pre><code>Set.filter(p : 'a => bool, s : set('a)) : set('a)\n</code></pre> <p>Filters out elements of <code>s</code> that fulfill predicate <code>p</code></p>"},{"location":"sophia_stdlib/#fold","title":"fold","text":"<pre><code>Set.fold(f : ('a, 'b) => 'b, acc : 'b, s : set('a)) : 'b\n</code></pre> <p>Folds the function <code>f</code> over every element in the set <code>s</code> and returns the final value of the accumulator <code>acc</code>.</p>"},{"location":"sophia_stdlib/#subtract","title":"subtract","text":"<pre><code>Set.subtract(s1 : set('a), s2 : set('a)) : set('a)\n</code></pre> <p>Returns the elements of <code>s1</code> that are not members of <code>s2</code></p>"},{"location":"sophia_stdlib/#intersection_1","title":"intersection","text":"<pre><code>Set.intersection(s1 : set('a), s2 : set('a)) : set('a)\n</code></pre> <p>Returns the intersection of the two sets <code>s1</code> and <code>s2</code></p>"},{"location":"sophia_stdlib/#intersection_list","title":"intersection_list","text":"<pre><code>Set.intersection_list(sets : list(set('a))) : set('a)\n</code></pre> <p>Returns the intersection of all the sets in the given list</p>"},{"location":"sophia_stdlib/#union_1","title":"union","text":"<pre><code>Set.union(s1 : set('a), s2 : set('a)) : set('a)\n</code></pre> <p>Returns the union of the two sets <code>s1</code> and <code>s2</code></p>"},{"location":"sophia_stdlib/#union_list","title":"union_list","text":"<pre><code>Set.union_list(sets : list(set('a))) : set('a)\n</code></pre> <p>Returns the union of all the sets in the given list</p>"},{"location":"sophia_stdlib/#string","title":"String","text":"<p>Operations on the <code>string</code> type. A <code>string</code> is a UTF-8 encoded byte array.</p>"},{"location":"sophia_stdlib/#length_1","title":"length","text":"<p><code>length(s : string) : int</code></p> <p>The length of a string.</p> <p>Note: not equivalent to byte size of the string, rather <code>List.length(String.to_list(s))</code></p>"},{"location":"sophia_stdlib/#concat_1","title":"concat","text":"<pre><code>concat(s1 : string, s2 : string) : string\n</code></pre> <p>Concatenates <code>s1</code> and <code>s2</code>.</p>"},{"location":"sophia_stdlib/#concats","title":"concats","text":"<pre><code>concats(ss : list(string)) : string\n</code></pre> <p>Concatenates a list of strings.</p>"},{"location":"sophia_stdlib/#to_list_3","title":"to_list","text":"<pre><code>to_list(s : string) : list(char)\n</code></pre> <p>Converts a <code>string</code> to a list of <code>char</code> - the code points are normalized, but composite characters are possibly converted to multiple <code>char</code>s. For example the string \"\ud83d\ude1ci\u0307\" is converted to <code>[128540,105,775]</code> - where the smiley is the first code point and the strangely dotted <code>i</code> becomes <code>[105, 775]</code>.</p>"},{"location":"sophia_stdlib/#from_list_2","title":"from_list","text":"<pre><code>from_list(cs : list(char)) : string\n</code></pre> <p>Converts a list of characters into a normalized UTF-8 string.</p>"},{"location":"sophia_stdlib/#to_lower","title":"to_lower","text":"<pre><code>to_lower(s : string) : string\n</code></pre> <p>Converts a string to lowercase.</p>"},{"location":"sophia_stdlib/#to_upper","title":"to_upper","text":"<pre><code>to_upper(s : string) : string\n</code></pre> <p>Converts a string to uppercase.</p>"},{"location":"sophia_stdlib/#at","title":"at","text":"<pre><code>at(ix : int, s : string) : option(char)\n</code></pre> <p>Returns the character/codepoint at (zero-based) index <code>ix</code>. Basically the equivalent to <code>List.nth(ix, String.to_list(s))</code>.</p>"},{"location":"sophia_stdlib/#split_1","title":"split","text":"<pre><code>split(ix : int, s:string) : string * string\n</code></pre> <p>Splits a string at (zero-based) index <code>ix</code>.</p>"},{"location":"sophia_stdlib/#contains_2","title":"contains","text":"<pre><code>contains(str : string, pat : string) : option(int)\n</code></pre> <p>Searches for <code>pat</code> in <code>str</code>, returning <code>Some(ix)</code> if <code>pat</code> is a substring of <code>str</code> starting at position <code>ix</code>, otherwise returns <code>None</code>.</p>"},{"location":"sophia_stdlib/#tokens","title":"tokens","text":"<pre><code>tokens(str : string, pat : string) : list(string)\n</code></pre> <p>Splits <code>str</code> into tokens, <code>pat</code> is the divider of tokens.</p>"},{"location":"sophia_stdlib/#to_int_2","title":"to_int","text":"<pre><code>to_int(s : string) : option(int)\n</code></pre> <p>Converts a decimal (\"123\", \"-253\") or a hexadecimal (\"0xa2f\", \"-0xBBB\") string into an integer. If the string doesn't contain a valid number <code>None</code> is returned.</p>"},{"location":"sophia_stdlib/#sha3_1","title":"sha3","text":"<pre><code>sha3(s : string) : hash\n</code></pre> <p>Computes the SHA3/Keccak hash of the string.</p>"},{"location":"sophia_stdlib/#sha256_1","title":"sha256","text":"<pre><code>sha256(s : string) : hash\n</code></pre> <p>Computes the SHA256 hash of the string.</p>"},{"location":"sophia_stdlib/#blake2b_1","title":"blake2b","text":"<pre><code>blake2b(s : string) : hash\n</code></pre> <p>Computes the Blake2B hash of the string.</p>"},{"location":"sophia_stdlib/#triple","title":"Triple","text":""},{"location":"sophia_stdlib/#fst_1","title":"fst","text":"<pre><code>Triple.fst(t : ('a * 'b * 'c)) : 'a\n</code></pre> <p>First element projection.</p>"},{"location":"sophia_stdlib/#snd_1","title":"snd","text":"<pre><code>Triple.snd(t : ('a * 'b * 'c)) : 'b\n</code></pre> <p>Second element projection.</p>"},{"location":"sophia_stdlib/#thd","title":"thd","text":"<pre><code>Triple.thd(t : ('a * 'b * 'c)) : 'c\n</code></pre> <p>Third element projection.</p>"},{"location":"sophia_stdlib/#map1_1","title":"map1","text":"<pre><code>Triple.map1(f : 'a => 'm, t : ('a * 'b * 'c)) : ('m * 'b * 'c)\n</code></pre> <p>Applies function over first element.</p>"},{"location":"sophia_stdlib/#map2_2","title":"map2","text":"<pre><code>Triple.map2(f : 'b => 'm, t : ('a * 'b * 'c)) : ('a * 'm * 'c)\n</code></pre> <p>Applies function over second element.</p>"},{"location":"sophia_stdlib/#map3_1","title":"map3","text":"<pre><code>Triple.map3(f : 'c => 'm, t : ('a * 'b * 'c)) : ('a * 'b * 'm)\n</code></pre> <p>Applies function over third element.</p>"},{"location":"sophia_stdlib/#trimap","title":"trimap","text":"<pre><code>Triple.trimap(f : 'a => 'x, g : 'b => 'y, h : 'c => 'z, t : ('a * 'b * 'c)) : ('x * 'y * 'z)\n</code></pre> <p>Applies functions over respective elements.</p>"},{"location":"sophia_stdlib/#swap_1","title":"swap","text":"<pre><code>Triple.swap(t : ('a * 'b * 'c)) : ('c * 'b * 'a)\n</code></pre> <p>Swaps first and third element.</p>"},{"location":"sophia_stdlib/#rotr","title":"rotr","text":"<pre><code>Triple.rotr(t : ('a * 'b * 'c)) : ('c * 'a * 'b)\n</code></pre> <p>Cyclic rotation of the elements to the right.</p>"},{"location":"sophia_stdlib/#rotl","title":"rotl","text":"<pre><code>Triple.rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a)\n</code></pre> <p>Cyclic rotation of the elements to the left.</p>"},{"location":"sophia_syntax/","title":"Syntax","text":""},{"location":"sophia_syntax/#lexical-syntax","title":"Lexical syntax","text":""},{"location":"sophia_syntax/#comments","title":"Comments","text":"<p>Single line comments start with <code>//</code> and block comments are enclosed in <code>/*</code> and <code>*/</code> and can be nested.</p>"},{"location":"sophia_syntax/#keywords","title":"Keywords","text":"<pre><code>contract include let switch type record datatype if elif else function\nstateful payable true false mod public entrypoint private indexed namespace\ninterface main using as for hiding\n</code></pre>"},{"location":"sophia_syntax/#tokens","title":"Tokens","text":"<ul> <li><code>Id = [a-z_][A-Za-z0-9_']*</code> identifiers start with a lower case letter.</li> <li><code>Con = [A-Z][A-Za-z0-9_']*</code> constructors start with an upper case letter.</li> <li><code>QId = (Con\\.)+Id</code> qualified identifiers (e.g. <code>Map.member</code>)</li> <li><code>QCon = (Con\\.)+Con</code> qualified constructor</li> <li><code>TVar = 'Id</code> type variable (e.g <code>'a</code>, <code>'b</code>)</li> <li><code>Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*</code> integer literal with optional <code>_</code> separators</li> <li><code>Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*</code> byte array literal with optional <code>_</code> separators</li> <li><code>String</code> string literal enclosed in <code>\"</code> with escape character <code>\\</code></li> <li><code>Char</code> character literal enclosed in <code>'</code> with escape character <code>\\</code></li> <li><code>AccountAddress</code> base58-encoded 32 byte account pubkey with <code>ak_</code> prefix</li> <li><code>ContractAddress</code> base58-encoded 32 byte contract address with <code>ct_</code> prefix</li> <li><code>OracleAddress</code> base58-encoded 32 byte oracle address with <code>ok_</code> prefix</li> <li><code>OracleQueryId</code> base58-encoded 32 byte oracle query id with <code>oq_</code> prefix</li> </ul> <p>Valid string escape codes are</p> Escape ASCII <code>\\b</code> 8 <code>\\t</code> 9 <code>\\n</code> 10 <code>\\v</code> 11 <code>\\f</code> 12 <code>\\r</code> 13 <code>\\e</code> 27 <code>\\xHexDigits</code> HexDigits <p>See the identifier encoding scheme for the details on the base58 literals.</p>"},{"location":"sophia_syntax/#layout-blocks","title":"Layout blocks","text":"<p>Sophia uses Python-style layout rules to group declarations and statements. A layout block with more than one element must start on a separate line and be indented more than the currently enclosing layout block. Blocks with a single element can be written on the same line as the previous token.</p> <p>Each element of the block must share the same indentation and no part of an element may be indented less than the indentation of the block. For instance</p> <pre><code>contract Layout =\nfunction foo() = 0 // no layout\nfunction bar() = // layout block starts on next line\nlet x = foo() // indented more than 2 spaces\nx\n+ 1 // the '+' is indented more than the 'x'\n</code></pre>"},{"location":"sophia_syntax/#notation","title":"Notation","text":"<p>In describing the syntax below, we use the following conventions:</p> <ul> <li>Upper-case identifiers denote non-terminals (like <code>Expr</code>) or terminals with some associated value (like <code>Id</code>).</li> <li>Keywords and symbols are enclosed in single quotes: <code>'let'</code> or <code>'='</code>.</li> <li>Choices are separated by vertical bars: <code>|</code>.</li> <li>Optional elements are enclosed in <code>[</code> square brackets <code>]</code>.</li> <li><code>(</code> Parentheses <code>)</code> are used for grouping.</li> <li>Zero or more repetitions are denoted by a postfix <code>*</code>, and one or more repetitions by a <code>+</code>.</li> <li><code>Block(X)</code> denotes a layout block of <code>X</code>s.</li> <li><code>Sep(X, S)</code> is short for <code>[X (S X)*]</code>, i.e. a possibly empty sequence of <code>X</code>s separated by <code>S</code>s.</li> <li><code>Sep1(X, S)</code> is short for <code>X (S X)*</code>, i.e. same as <code>Sep</code>, but must not be empty.</li> </ul>"},{"location":"sophia_syntax/#declarations","title":"Declarations","text":"<p>A Sophia file consists of a sequence of declarations in a layout block.</p> <pre><code>File ::= Block(TopDecl)\n\nTopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)\n| 'contract' 'interface' Con [Implement] '=' Block(Decl)\n| 'namespace' Con '=' Block(Decl)\n| '@compiler' PragmaOp Version\n| 'include' String\n| Using\n\nImplement ::= ':' Sep1(Con, ',')\n\nDecl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias\n| 'record' Id ['(' TVar* ')'] '=' RecordType\n| 'datatype' Id ['(' TVar* ')'] '=' DataType\n| 'let' Id [':' Type] '=' Expr\n| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)\n| Using\n\nFunDecl ::= Id ':' Type // Type signature\n| Id Args [':' Type] '=' Block(Stmt) // Definition\n| Id Args [':' Type] Block(GuardedDef) // Guarded definitions\n\nGuardedDef ::= '|' Sep1(Expr, ',') '=' Block(Stmt)\n\nUsing ::= 'using' Con ['as' Con] [UsingParts]\nUsingParts ::= 'for' '[' Sep1(Id, ',') ']'\n| 'hiding' '[' Sep1(Id, ',') ']'\n\nPragmaOp ::= '<' | '=<' | '==' | '>=' | '>'\nVersion ::= Sep1(Int, '.')\n\nEModifier ::= 'payable' | 'stateful'\nFModifier ::= 'stateful' | 'private'\n\nArgs ::= '(' Sep(Pattern, ',') ')'\n</code></pre> <p>Contract declarations must appear at the top-level.</p> <p>For example, <pre><code>contract Test =\ntype t = int\nentrypoint add (x : t, y : t) = x + y\n</code></pre></p> <p>There are three forms of type declarations: type aliases (declared with the <code>type</code> keyword), record type definitions (<code>record</code>) and data type definitions (<code>datatype</code>):</p> <pre><code>TypeAlias ::= Type\nRecordType ::= '{' Sep(FieldType, ',') '}'\nDataType ::= Sep1(ConDecl, '|')\n\nFieldType ::= Id ':' Type\nConDecl ::= Con ['(' Sep1(Type, ',') ')']\n</code></pre> <p>For example, <pre><code>record point('a) = {x : 'a, y : 'a}\ndatatype shape('a) = Circle(point('a), 'a) | Rect(point('a), point('a))\ntype int_shape = shape(int)\n</code></pre></p>"},{"location":"sophia_syntax/#types","title":"Types","text":"<pre><code>Type ::= Domain '=>' Type // Function type\n| Type '(' Sep(Type, ',') ')' // Type application\n| '(' Type ')' // Parens\n| 'unit' | Sep(Type, '*') // Tuples\n| Id | QId | TVar\n\nDomain ::= Type // Single argument\n| '(' Sep(Type, ',') ')' // Multiple arguments\n</code></pre> <p>The function type arrow associates to the right.</p> <p>Example, <pre><code>'a => list('a) => (int * list('a))\n</code></pre></p>"},{"location":"sophia_syntax/#statements","title":"Statements","text":"<p>Function bodies are blocks of statements, where a statement is one of the following</p> <pre><code>Stmt ::= 'switch' '(' Expr ')' Block(Case)\n| 'if' '(' Expr ')' Block(Stmt)\n| 'elif' '(' Expr ')' Block(Stmt)\n| 'else' Block(Stmt)\n| 'let' LetDef\n| Using\n| Expr\n\nLetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition\n| Pattern '=' Block(Stmt) // Value definition\n\nCase ::= Pattern '=>' Block(Stmt)\n| Pattern Block(GuardedCase)\n\nGuardedCase ::= '|' Sep1(Expr, ',') '=>' Block(Stmt)\n\nPattern ::= Expr\n</code></pre> <p><code>if</code> statements can be followed by zero or more <code>elif</code> statements and an optional final <code>else</code> statement. For example,</p> <pre><code>let x : int = 4\nswitch(f(x))\nNone => 0\nSome(y) =>\nif(y > 10)\n\"too big\"\nelif(y < 3)\n\"too small\"\nelse\n\"just right\"\n</code></pre>"},{"location":"sophia_syntax/#expressions","title":"Expressions","text":"<pre><code>Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x + 1\n| '(' BinOp ')' // Operator lambda (+)\n| 'if' '(' Expr ')' Expr 'else' Expr // If expression if(x < y) y else x\n| Expr ':' Type // Type annotation 5 : int\n| Expr BinOp Expr // Binary operator x + y\n| UnOp Expr // Unary operator ! b\n| Expr '(' Sep(Expr, ',') ')' // Application f(x, y)\n| Expr '.' Id // Projection state.x\n| Expr '[' Expr ']' // Map lookup map[key]\n| Expr '{' Sep(FieldUpdate, ',') '}' // Record or map update r{ fld[key].x = y }\n| '[' Sep(Expr, ',') ']' // List [1, 2, 3]\n| '[' Expr '|' Sep(Generator, ',') ']'\n// List comprehension [k | x <- [1], if (f(x)), let k = x+1]\n| '[' Expr '..' Expr ']' // List range [1..n]\n| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}\n| '(' Expr ')' // Parens (1 + 2) * 3\n| '(' Expr '=' Expr ')' // Assign pattern (y = x::_)\n| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token\n| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, \"foo\", '%'\n| AccountAddress | ContractAddress // Chain identifiers\n| OracleAddress | OracleQueryId // Chain identifiers\n| '???' // Hole expression 1 + ???\n\nGenerator ::= Pattern '<-' Expr // Generator\n| 'if' '(' Expr ')' // Guard\n| LetDef // Definition\n\nLamArgs ::= '(' Sep(LamArg, ',') ')'\nLamArg ::= Id [':' Type]\n\nFieldUpdate ::= Path '=' Expr\nPath ::= Id // Record field\n| '[' Expr ']' // Map key\n| Path '.' Id // Nested record field\n| Path '[' Expr ']' // Nested map key\n\nBinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='\n| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'\n| '|>'\nUnOp ::= '-' | '!'\n</code></pre>"},{"location":"sophia_syntax/#operators-types","title":"Operators types","text":"Operators Type <code>-</code> <code>+</code> <code>*</code> <code>/</code> <code>mod</code> <code>^</code> arithmetic operators <code>!</code> <code>&&</code> <code>||</code> logical operators <code>==</code> <code>!=</code> <code><</code> <code>></code> <code>=<</code> <code>>=</code> comparison operators <code>::</code> <code>++</code> list operators <code>|></code> functional operators"},{"location":"sophia_syntax/#operator-precedence","title":"Operator precedence","text":"<p>In order of highest to lowest precedence.</p> Operators Associativity <code>!</code> right <code>^</code> left <code>*</code> <code>/</code> <code>mod</code> left <code>-</code> (unary) right <code>+</code> <code>-</code> left <code>::</code> <code>++</code> right <code><</code> <code>></code> <code>=<</code> <code>>=</code> <code>==</code> <code>!=</code> none <code>&&</code> right <code>||</code> right <code>|></code> left"}]} |