diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b3deaa2d9..d9c9a6ad5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,8 +23,8 @@ dont-merge-to-master: only: - master -.build_binary: &build_binary - # To run in sequence and save CPU usage, use stage: build_and_package_binaries +.build_binary: + &build_binary # To run in sequence and save CPU usage, use stage: build_and_package_binaries stage: test script: - $build_binary_script "$target_os_family" "$target_os" "$target_os_version" @@ -35,7 +35,7 @@ dont-merge-to-master: .website_build: &website_build stage: build_and_deploy - image: node:8 + image: node:12 dependencies: - build-and-package-debian-9 - build-and-package-debian-10 @@ -62,19 +62,15 @@ dont-merge-to-master: # copy .deb packages into website - find dist -name \*.deb -exec sh -c 'cp {} gitlab-pages/website/static/deb/ligo_$(basename $(dirname {})).deb' \; - # npm + # yarn - cd gitlab-pages/website - - npm install + - yarn install script: - - npm run version next - - npm run build + - yarn build # move internal odoc documentation to the website folder - - mkdir -p build/ligo/ - - mv ../../_build/default/_doc/_html/ build/ligo/odoc - - pwd # for debug - - ls build/ligo/ # for debug + - mv ../../_build/default/_doc/_html/ build/odoc after_script: - - cp -r gitlab-pages/website/build/ligo public + - cp -r gitlab-pages/website/build public artifacts: paths: - public @@ -84,7 +80,6 @@ dont-merge-to-master: services: - docker:19.03.5-dind - .before_script: &before_script before_script: # Install dependencies @@ -224,9 +219,10 @@ run-webide-unit-tests: - cd tools/webide/packages/server - npm ci - npm run test - only: - changes: + rules: + - changes: - tools/webide/** + when: always build-publish-ide-image: stage: build_and_deploy @@ -236,6 +232,7 @@ build-publish-ide-image: - find dist/ - find dist/package/ -name '*ligo_*deb' - mv $(realpath dist/package/debian-10/*.deb) tools/webide/ligo_deb10.deb + - cp -r src/test/examples tools/webide/packages/client/examples - cd tools/webide - echo "${CI_BUILD_TOKEN}" | docker login -u gitlab-ci-token --password-stdin registry.gitlab.com - > @@ -243,10 +240,12 @@ build-publish-ide-image: -t "${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}" --build-arg GIT_TAG="${CI_COMMIT_SHA}" --build-arg GIT_COMMIT="${CI_COMMIT_SHORT_SHA}" + --build-arg EXAMPLES_DIR_SRC=packages/client/examples . - docker push "${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}" rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - changes: + - tools/webide/** when: always - if: '$CI_COMMIT_REF_NAME == "dev"' when: always @@ -260,7 +259,8 @@ run-webide-e2e-tests: - export WEBIDE_IMAGE="${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}" - docker-compose run e2e rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - changes: + - tools/webide/** when: always - if: '$CI_COMMIT_REF_NAME == "dev"' when: always diff --git a/CHANGELOG.md b/CHANGELOG.md index b40924f28..fe908e334 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +## [Add crypto reference page to docs](https://gitlab.com/ligolang/ligo/-/merge_requests/459) +### Added +- support for `Tezos.create_contract` origination + ## [9164206ef1fcf3e577820442b5afbad92d03ffa4] - 2020-02-09 ### Changed - Mutation of variables inside lambdas passed to list_iter do not have effect anymore. Side-effects used to survive iterations of list_iter via a quirk in the Michelson list_iter. Now, either use a list_fold and explicitly pass through the updated variables (e.g. storage) to the next iteration, or use a `for` loop which automatically detects mutations within the loop body and lifts the affected variables to a record that is passed from iteration to iteration. diff --git a/gitlab-pages/Dockerfile b/gitlab-pages/Dockerfile index d369844d5..93c4179f2 100644 --- a/gitlab-pages/Dockerfile +++ b/gitlab-pages/Dockerfile @@ -1,4 +1,4 @@ -FROM node:8.11.4 +FROM node:12.16 WORKDIR /app/website diff --git a/gitlab-pages/docker-compose.yml b/gitlab-pages/docker-compose.yml index 90c32ea95..af12b7af7 100644 --- a/gitlab-pages/docker-compose.yml +++ b/gitlab-pages/docker-compose.yml @@ -8,15 +8,16 @@ services: - 35729:35729 volumes: - ./docs:/app/docs - - ./website/blog:/app/website/blog + # - ./website/blog:/app/website/blog - ./website/core:/app/website/core - - ./website/i18n:/app/website/i18n - - ./website/pages:/app/website/pages + # - ./website/i18n:/app/website/i18n + - ./website/src:/app/website/src - ./website/static:/app/website/static - - ./website/versioned_sidebars:/app/website/versioned_sidebars - - ./website/versioned_docs:/app/website/versioned_docs + # - ./website/versioned_sidebars:/app/website/versioned_sidebars + # - ./website/versioned_docs:/app/website/versioned_docs - ./website/sidebars.json:/app/website/sidebars.json - - ./website/siteConfig.js:/app/website/siteConfig.js + - ./website/docusaurus.config.js:/app/website/docusaurus.config.js - ./website/versions.json:/app/website/versions.json - - ./website/node_modules/reason-highlightjs:/app/website/node_modules/reason-highlightjs + # - ./website/core/AlgoliaSearch.js:/app/website/core/AlgoliaSearch.js + working_dir: /app/website diff --git a/gitlab-pages/docs/advanced/entrypoints-contracts.md b/gitlab-pages/docs/advanced/entrypoints-contracts.md index 15fa7cb75..499d07c58 100644 --- a/gitlab-pages/docs/advanced/entrypoints-contracts.md +++ b/gitlab-pages/docs/advanced/entrypoints-contracts.md @@ -1,61 +1,68 @@ --- id: entrypoints-contracts -title: Access function and Entrypoints +title: Main function and Entrypoints --- +import Syntax from '@theme/Syntax'; + ## Access Functions A LIGO contract is made of a series of constant and function declarations. Only functions having a special type can be called when -the contract is activated: we called them *access functions*. An -access function takes two parameters, the *contract parameter* and the +the contract is activated: we call them *main functions*. A main +function takes two parameters, the *contract parameter* and the *on-chain storage*, and returns a pair made of a *list of operations* and a (new) storage. When the contract is originated, the initial value of the storage is -provided. When an access function is later called, only the parameter -is provided, but the type of an access function contains both. +provided. When a main function is later called, only the parameter is +provided, but the type of a main function contains both. The type of the contract parameter and the storage are up to the contract designer, but the type for list operations is not. The return -type of an access function is as follows, assuming that the type +type of a main function is as follows, assuming that the type `storage` has been defined elsewhere. (Note that you can use any type with any name for the storage.) - - + + + ```pascaligo skip type storage is ... // Any name, any type type return is list (operation) * storage ``` - + + + ```cameligo skip type storage = ... // Any name, any type type return = operation list * storage ``` - + + + ```reasonligo skip type storage = ...; // Any name, any type type return = (list (operation), storage); ``` - -The contract storage can only be modified by activating an access -function. It is important to understand what that means. What it does -*not* mean is that some global variable holding the storage is -modified by the access function. Instead, what it *does* mean is that, -given the state of the storage *on-chain*, an access function -specifies how to create another state for it, depending on a -parameter. + + + + +The contract storage can only be modified by activating a main +function: given the state of the storage *on-chain*, a main function +specifies how to create another state for it, depending on the +contract's parameter. Here is an example where the storage is a single natural number that is updated by the parameter. - - + + ```pascaligo group=a type parameter is nat @@ -65,8 +72,9 @@ type return is list (operation) * storage function save (const action : parameter; const store : storage) : return is ((nil : list (operation)), store) ``` + + - ```cameligo group=a type parameter = nat type storage = nat @@ -76,7 +84,9 @@ let save (action, store: parameter * storage) : return = (([] : operation list), store) ``` - + + + ```reasonligo group=a type parameter = nat; type storage = nat; @@ -85,21 +95,24 @@ type return = (list (operation), storage); let main = ((action, store): (parameter, storage)) : return => (([] : list (operation)), store); ``` - + + + ## Entrypoints -In LIGO, the design pattern is to have *one* access function that -dispatches the control flow according to its parameter. Those -functions called for those actions are called *entrypoints*. +In LIGO, the design pattern is to have *one* main function called +`main`, that dispatches the control flow according to its +parameter. Those functions called for those actions are called +*entrypoints*. As an analogy, in the C programming language, the `main` function is -the unique access function and any function called from it would be an +the unique main function and any function called from it would be an entrypoint. The parameter of the contract is then a variant type, and, depending on the constructors of that type, different functions in the contract -are called. In other terms, the unique access function dispatches the +are called. In other terms, the unique main function dispatches the control flow depending on a *pattern matching* on the contract parameter. @@ -107,9 +120,10 @@ In the following example, the storage contains a counter of type `nat` and a name of type `string`. Depending on the parameter of the contract, either the counter or the name is updated. - - + + + ```pascaligo group=b type parameter is Action_A of nat @@ -128,14 +142,16 @@ function entry_A (const n : nat; const store : storage) : return is function entry_B (const s : string; const store : storage) : return is ((nil : list (operation)), store with record [name = s]) -function access (const action : parameter; const store : storage): return is +function main (const action : parameter; const store : storage): return is case action of Action_A (n) -> entry_A (n, store) | Action_B (s) -> entry_B (s, store) end ``` - + + + ```cameligo group=b type parameter = Action_A of nat @@ -154,13 +170,15 @@ let entry_A (n, store : nat * storage) : return = let entry_B (s, store : string * storage) : return = ([] : operation list), {store with name = s} -let access (action, store: parameter * storage) : return = +let main (action, store: parameter * storage) : return = match action with Action_A n -> entry_A (n, store) | Action_B s -> entry_B (s, store) ``` - + + + ```reasonligo group=b type parameter = | Action_A (nat) @@ -179,13 +197,15 @@ let entry_A = ((n, store): (nat, storage)) : return => let entry_B = ((s, store): (string, storage)) : return => (([] : list (operation)), {...store, name : s}); -let access = ((action, store): (parameter, storage)) : return => +let main = ((action, store): (parameter, storage)) : return => switch (action) { | Action_A (n) => entry_A ((n, store)) | Action_B (s) => entry_B ((s, store)) }; ``` - + + + ## Tezos-specific Built-ins @@ -196,83 +216,110 @@ how those built-ins can be utilized. ### Accepting or Declining Tokens in a Smart Contract -This example shows how `amount` and `failwith` can be used to decline -any transaction that sends more tez than `0mutez`, that is, no +This example shows how `Tezos.amount` and `failwith` can be used to +decline any transaction that sends more tez than `0tez`, that is, no incoming tokens are accepted. - - + + + ```pascaligo group=c type parameter is unit type storage is unit type return is list (operation) * storage function deny (const action : parameter; const store : storage) : return is - if amount > 0mutez then + if Tezos.amount > 0tez then (failwith ("This contract does not accept tokens.") : return) else ((nil : list (operation)), store) ``` - +> Note that `amount` is *deprecated*. + + + + + ```cameligo group=c type parameter = unit type storage = unit type return = operation list * storage let deny (action, store : parameter * storage) : return = - if amount > 0mutez then - (failwith "This contract does not accept tokens.": return) + if Tezos.amount > 0tez then + (failwith "This contract does not accept tokens." : return) else (([] : operation list), store) ``` - +> Note that `amount` is *deprecated*. + + + + ```reasonligo group=c type parameter = unit; type storage = unit; type return = (list (operation), storage); let deny = ((action, store): (parameter, storage)) : return => { - if (amount > 0mutez) { + if (Tezos.amount > 0tez) { (failwith("This contract does not accept tokens."): return); } else { (([] : list (operation)), store); }; }; ``` - +> Note that `amount` is *deprecated*. + + + ### Access Control -This example shows how `sender` or `source` can be used to deny access to an entrypoint. +This example shows how `Tezos.source` can be used to deny access to an +entrypoint. + + + - - ```pascaligo group=c const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); -function filter (const action : parameter; const store : storage) : return is - if source =/= owner then (failwith ("Access denied.") : return) - else ((nil : list(operation)), store) +function main (const action : parameter; const store : storage) : return is + if Tezos.source =/= owner then (failwith ("Access denied.") : return) + else ((nil : list (operation)), store) ``` - +> Note that `source` is *deprecated*. + + + + ```cameligo group=c let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -let filter (action, store: parameter * storage) : return = - if source <> owner then (failwith "Access denied." : return) +let main (action, store: parameter * storage) : return = + if Tezos.source <> owner then (failwith "Access denied." : return) else (([] : operation list), store) ``` - +> Note that `source` is *deprecated*. + + + + ```reasonligo group=c let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address); -let access = ((action, store): (parameter, storage)) : storage => { - if (source != owner) { (failwith ("Access denied.") : return); } +let main = ((action, store) : (parameter, storage)) : storage => { + if (Tezos.source != owner) { (failwith ("Access denied.") : return); } else { (([] : list (operation)), store); }; }; ``` - + +> Note that `source` is *deprecated*. + + + ### Inter-Contract Invocations @@ -298,9 +345,10 @@ of type `parameter`, and we have a `proxy.ligo` contract that accepts the same parameter type, and forwards the call to the deployed counter contract. - - + + + ```pascaligo skip // counter.ligo type parameter is @@ -329,16 +377,22 @@ const dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address) function proxy (const action : parameter; const store : storage): return is block { - const counter : contract (parameter) = get_contract (dest); + const counter : contract (parameter) = + case (Tezos.get_contract_opt (dest) : option (contract (parameter))) of + Some (contract) -> contract + | None -> (failwith ("Contract not found.") : contract (parameter)) + end; (* Reuse the parameter in the subsequent transaction or use another one, `mock_param`. *) const mock_param : parameter = Increment (5n); - const op : operation = transaction (action, 0mutez, counter); + const op : operation = Tezos.transaction (action, 0tez, counter); const ops : list (operation) = list [op] } with (ops, store) ``` - + + + ```cameligo skip // counter.mligo @@ -365,15 +419,22 @@ type return = operation list * storage let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address) let proxy (action, store : parameter * storage) : return = - let counter : parameter contract = Operation.get_contract dest in + let counter : parameter contract = + match (Tezos.get_contract_opt (dest) : parameter contract option) with + Some contract -> contract + | None -> (failwith "Contract not found." : parameter contract) in (* Reuse the parameter in the subsequent transaction or use another one, `mock_param`. *) let mock_param : parameter = Increment (5n) in - let op : operation = Operation.transaction action 0mutez counter + let op : operation = Tezos.transaction action 0tez counter in [op], store ``` - +> Note that `Operation.get_contract` and `Operation.transaction` are +> *deprecated*. + + + ```reasonligo skip // counter.religo @@ -400,13 +461,21 @@ type return = (list (operation), storage); let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address); let proxy = ((action, store): (parameter, storage)) : return => { - let counter : contract (parameter) = Operation.get_contract (dest); + let counter : contract (parameter) = + switch (Tezos.get_contract_opt (dest) : option (contract (parameter))) { + | Some (contract) => contract; + | None => (failwith ("Contract not found.") : contract (parameter)); + }; (* Reuse the parameter in the subsequent transaction or use another one, `mock_param`. *) let mock_param : parameter = Increment (5n); - let op : operation = Operation.transaction (action, 0mutez, counter); + let op : operation = Tezos.transaction (action, 0tez, counter); ([op], store) }; ``` - +> Note that `Operation.get_contract` and `Operation.transaction` are +> *deprecated*. + + + diff --git a/gitlab-pages/docs/advanced/first-contract.md b/gitlab-pages/docs/advanced/first-contract.md index 7da4e8cf7..a22abd5a2 100644 --- a/gitlab-pages/docs/advanced/first-contract.md +++ b/gitlab-pages/docs/advanced/first-contract.md @@ -3,6 +3,8 @@ id: first-contract title: First contract --- +import Syntax from '@theme/Syntax'; + So far so good, we have learned enough of the LIGO language, we are confident enough to write out first smart contract. @@ -11,99 +13,120 @@ We will be implementing a counter contract. ## Dry-running a Contract Testing a contract can be quite easy if we utilize LIGO's built-in dry -run feature. Dry-run works by simulating the access function -execution, as if it were deployed on a real chain. You need to provide -the following: +run feature. Dry-run works by simulating the main function execution, +as if it were deployed on a real chain. You need to provide the +following: - `file` - contract to run - `entrypoint` - name of the function to execute -- `parameter` - parameter passed to the access function (in a theoretical invocation operation) +- `parameter` - parameter passed to the main function (in a + theoretical invocation operation) - `storage` - a mock storage value, as if it were stored on a real chain Here is a full example: - - -``` + +```shell ligo dry-run src/basic.ligo main Unit Unit // Outputs: // tuple[ list[] // Unit // ] ``` - -Output of the `dry-run` is the return value of our access function, we -can see the operations emited - in our case an empty list, and the new -storage value being returned - which in our case is still `Unit`. + +Output of the `dry-run` is the return value of our main function, we +can see the operations emitted (in our case an empty list, and the new +storage value being returned) which in our case is still `Unit`. ## A Counter Contract Our counter contract will store a single `int` as it's storage, and will accept an `action` variant in order to re-route our single `main` -access function to two entrypoints for `addition` and `subtraction`. +function to two entrypoints for `add` (addition) and `sub` +(subtraction). - - -``` -type action is + + +```pascaligo +type parameter is Increment of int | Decrement of int -function main (const p : action ; const s : int) : (list(operation) * int) is +type storage is int + +type return is list (operation) * storage + +function add (const n : int; const store : storage) : storage is store + n +function sub (const n : int; const store : storage) : storage is store - n + +function main (const action : parameter; const store : storage) : return is ((nil : list(operation)), - (case p of - | Increment (n) -> s + n - | Decrement (n) -> s - n - end)) + case action of + Increment (n) -> add (n, store) + | Decrement (n) -> sub (n, store) + end) ``` - + + + ```cameligo -type action = -| Increment of int +type parameter = + Increment of int | Decrement of int -let main (p, s: action * int) : operation list * int = - let result = - match p with - | Increment n -> s + n - | Decrement n -> s - n - in - (([]: operation list), result) +type storage = int + +type return = (operation) list * storage + +let add (n, store : int * storage) : storage = store + n +let sub (n, store : int * storage) : storage = store - n + +let main (action, store : parameter * storage) : operation list * storage = + (([]: operation list), + (match action with + Increment n -> add (n, store) + | Decrement n -> sub (n, store))) ``` - + + + + ```reasonligo -type action = -| Increment(int) -| Decrement(int); +type parameter = + Increment (int) +| Decrement (int) +; -let main = (p_s: (action, int)) : (list(operation), int) => { - let p, s = p_s; - let result = - switch (p) { - | Increment(n) => s + n - | Decrement(n) => s - n - }; - (([]: list(operation)), result); -}; +type storage = int; + +type return = (list (operation), storage); + +let add = ((n, store) : (int, storage)) : storage => store + n; +let sub = ((n, store) : (int, storage)) : storage => store - n; + +let main = ((action, store) : (parameter, storage)) : return => + (([]: list (operation)), + (switch (action) { + | Increment (n) => add ((n, store)) + | Decrement (n) => sub ((n, store)) + })); ``` - + -To dry-run the counter contract, we will use the `main` entrypoint, provide a variant parameter of `Increment(5)` and an initial storage value of `5`. +To dry-run the counter contract, we will provide the `main` function +with a variant parameter of value `Increment (5)` and an initial +storage value of `5`. - - - -``` +```shell ligo dry-run src/counter.ligo main "Increment(5)" 5 // tuple[ list[] // 10 // ] ``` - Our contract's storage has been successfuly incremented to `10`. @@ -114,86 +137,68 @@ In order to deploy the counter contract to a real Tezos network, we'd have to compile it first, this can be done with the help of the `compile-contract` CLI command: - - -``` +```shell ligo compile-contract src/counter.ligo main ``` - - Command above will output the following Michelson code: - - -``` + +```michelson { parameter (or (int %decrement) (int %increment)) ; storage int ; code { DUP ; - CAR ; - DIP { DUP } ; - SWAP ; CDR ; DIP { DUP } ; SWAP ; + CAR ; IF_LEFT { DUP ; - DIP 2 { DUP } ; - DIG 2 ; - DIP { DUP } ; + DIP { DIP { DUP } ; SWAP } ; + PAIR ; + DUP ; + CDR ; + DIP { DUP ; CAR } ; SUB ; - SWAP ; - DROP ; - SWAP ; - DROP } + DIP { DROP 2 } } { DUP ; - DIP 2 { DUP } ; - DIG 2 ; - DIP { DUP } ; + DIP { DIP { DUP } ; SWAP } ; + PAIR ; + DUP ; + CDR ; + DIP { DUP ; CAR } ; ADD ; - SWAP ; - DROP ; - SWAP ; - DROP } ; + DIP { DROP 2 } } ; NIL operation ; PAIR ; - SWAP ; - DROP ; - SWAP ; - DROP ; - SWAP ; - DROP } } + DIP { DROP 2 } } } ``` - However in order to originate a Michelson contract on Tezos, we also need to provide the initial storage value, we can use `compile-storage` to compile the LIGO representation of the storage to Michelson. - - -``` +```shell ligo compile-storage src/counter.ligo main 5 // Outputs: 5 ``` - - -In our case the LIGO storage value maps 1:1 to its Michelson representation, however this will not be the case once the parameter is of a more complex data type, like a record. +In our case the LIGO storage value maps 1:1 to its Michelson +representation, however this will not be the case once the parameter +is of a more complex data type, like a record. ## Invoking a LIGO contract -Same rules apply for parameters, as apply for translating LIGO storage values to Michelson. We will need to use `compile-parameter` to compile our `action` variant into Michelson, here's how: +Same rules apply for parameters, as apply for translating LIGO storage +values to Michelson. We will need to use `compile-parameter` to +compile our `action` variant into Michelson, here's how: - - -``` +```shell ligo compile-parameter src/counter.ligo main 'Increment(5)' // Outputs: (Right 5) ``` - Now we can use `(Right 5)` which is a Michelson value, to invoke our -contract - e.g. via `tezos-client` +contract - e.g., via `tezos-client` diff --git a/gitlab-pages/docs/advanced/include.md b/gitlab-pages/docs/advanced/include.md index 21094102e..6b82a89df 100644 --- a/gitlab-pages/docs/advanced/include.md +++ b/gitlab-pages/docs/advanced/include.md @@ -3,6 +3,8 @@ id: include title: Including Other Contracts --- +import Syntax from '@theme/Syntax'; + Let us say that we have a contract that is getting a too large. If it has a modular structure, you might find it useful to use the `#include` statement to split the contract up over multiple files. @@ -10,9 +12,10 @@ has a modular structure, you might find it useful to use the You take the code that you want to include and put it in a separate file, for example `included.ligo`: - - + + + ```pascaligo // Demonstrate PascaLIGO inclusion statements, see includer.ligo @@ -20,46 +23,57 @@ file, for example `included.ligo`: const foo : int = 144 ``` - + + + ```cameligo // Demonstrate CameLIGO inclusion statements, see includer.mligo let foo : int = 144 ``` - + + + ```reasonligo // Demonstrate ReasonLIGO inclusion statements, see includer.religo let foo : int = 144; ``` - + + And then you can include this code using the `#include` statement like so: - - + + + ```pascaligo #include "included.ligo" const bar : int = foo ``` - + + + ```cameligo #include "included.mligo" let bar : int = foo ``` - + + + ```reasonligo #include "included.religo" let bar : int = foo; ``` - + + diff --git a/gitlab-pages/docs/advanced/src/counter.ligo b/gitlab-pages/docs/advanced/src/counter.ligo new file mode 100644 index 000000000..301b662c4 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/counter.ligo @@ -0,0 +1,17 @@ +type parameter is + Increment of int +| Decrement of int + +type storage is int + +type return is list (operation) * storage + +function add (const n : int; const store : storage) : storage is store + n +function sub (const n : int; const store : storage) : storage is store - n + +function main (const action : parameter; const store : storage) : return is + ((nil : list(operation)), + case action of + Increment (n) -> add (n, store) + | Decrement (n) -> sub (n, store) + end) diff --git a/gitlab-pages/docs/advanced/src/functions.ligo b/gitlab-pages/docs/advanced/src/functions.ligo new file mode 100644 index 000000000..6e0f83bf5 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/functions.ligo @@ -0,0 +1,9 @@ +function multiply (const a : int; const b : int) : int is + block { + const result : int = a * b + } with result + +function add (const a : int; const b : int) : int is a + b + +function main (const p : unit; const s : unit) : list (operation) * unit is + ((nil : list (operation)), s) diff --git a/gitlab-pages/docs/advanced/src/multiple-entrypoints.ligo b/gitlab-pages/docs/advanced/src/multiple-entrypoints.ligo new file mode 100644 index 000000000..301b662c4 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/multiple-entrypoints.ligo @@ -0,0 +1,17 @@ +type parameter is + Increment of int +| Decrement of int + +type storage is int + +type return is list (operation) * storage + +function add (const n : int; const store : storage) : storage is store + n +function sub (const n : int; const store : storage) : storage is store - n + +function main (const action : parameter; const store : storage) : return is + ((nil : list(operation)), + case action of + Increment (n) -> add (n, store) + | Decrement (n) -> sub (n, store) + end) diff --git a/gitlab-pages/docs/advanced/src/variables.ligo b/gitlab-pages/docs/advanced/src/variables.ligo new file mode 100644 index 000000000..e8e40f1f7 --- /dev/null +++ b/gitlab-pages/docs/advanced/src/variables.ligo @@ -0,0 +1,5 @@ +const four : int = 4 +const name : string = "John Doe" + +function main (const p : unit; const s : unit) : list (operation) * unit is + ((nil : list (operation)), s) diff --git a/gitlab-pages/docs/advanced/timestamps-addresses.md b/gitlab-pages/docs/advanced/timestamps-addresses.md index fb2154bc8..30f0b55b0 100644 --- a/gitlab-pages/docs/advanced/timestamps-addresses.md +++ b/gitlab-pages/docs/advanced/timestamps-addresses.md @@ -3,6 +3,8 @@ id: timestamps-addresses title: Timestamps, Addresses --- +import Syntax from '@theme/Syntax'; + ## Timestamps LIGO features timestamps, as Michelson does, while bakers baking the @@ -15,26 +17,38 @@ You can obtain the current time using the built-in syntax specific expression, please be aware that it is up to the baker to set the current timestamp value. - - + + + ```pascaligo group=a -const today : timestamp = now +const today : timestamp = Tezos.now ``` - +> Note that `now` is *deprecated*. + + + + ```cameligo group=a -let today : timestamp = Current.time +let today : timestamp = Tezos.now ``` - +> Note that `Current.time` is *deprecated*. + + + + ```reasonligo group=a -let today : timestamp = Current.time; +let today : timestamp = Tezos.now; ``` - +> Note that `Current.time` is *deprecated*. -> When running code, the LIGO CLI option -> `--predecessor-timestamp` allows you to control what `now` returns. + + + +> When running code, the LIGO CLI option `--predecessor-timestamp` +> allows you to control what `Tezos.now` returns. ### Timestamp Arithmetics @@ -43,84 +57,120 @@ constraints on your smart contracts. Consider the following scenarios. #### In 24 hours - - + + + ```pascaligo group=b -const today : timestamp = now +const today : timestamp = Tezos.now const one_day : int = 86400 const in_24_hrs : timestamp = today + one_day const some_date : timestamp = ("2000-01-01T10:10:10Z" : timestamp) const one_day_later : timestamp = some_date + one_day ``` - +> Note that `now` is *deprecated*. + + + + ```cameligo group=b -let today : timestamp = Current.time +let today : timestamp = Tezos.now let one_day : int = 86400 let in_24_hrs : timestamp = today + one_day let some_date : timestamp = ("2000-01-01t10:10:10Z" : timestamp) let one_day_later : timestamp = some_date + one_day ``` - +> Note that `Current.time` is *deprecated*. + + + + ```reasonligo group=b -let today : timestamp = Current.time; +let today : timestamp = Tezos.now; let one_day : int = 86400; let in_24_hrs : timestamp = today + one_day; let some_date : timestamp = ("2000-01-01t10:10:10Z" : timestamp); let one_day_later : timestamp = some_date + one_day; ``` - +> Note that `Current.time` is *deprecated*. + + + #### 24 hours Ago - - + + + ```pascaligo group=c -const today : timestamp = now +const today : timestamp = Tezos.now const one_day : int = 86400 const in_24_hrs : timestamp = today - one_day ``` - +> Note that `now` is *deprecated*. + + + + ```cameligo group=c -let today : timestamp = Current.time +let today : timestamp = Tezos.now let one_day : int = 86400 let in_24_hrs : timestamp = today - one_day ``` - +> Note that `Current.time` is *deprecated*. + + + + ```reasonligo group=c -let today : timestamp = Current.time; +let today : timestamp = Tezos.now; let one_day : int = 86400; let in_24_hrs : timestamp = today - one_day; ``` - +> Note that `Current.time` is *deprecated*. + + + ### Comparing Timestamps You can compare timestamps using the same comparison operators applying to numbers. - - + + + ```pascaligo group=c -const not_tommorow : bool = (now = in_24_hrs) +const not_tommorow : bool = (Tezos.now = in_24_hrs) ``` - +> Note that `now` is *deprecated*. + + + + ```cameligo group=c -let not_tomorrow : bool = (Current.time = in_24_hrs) +let not_tomorrow : bool = (Tezos.now = in_24_hrs) ``` - +> Note that `Current.time` is *deprecated*. + + + + ```reasonligo group=c -let not_tomorrow : bool = (Current.time == in_24_hrs); +let not_tomorrow : bool = (Tezos.now == in_24_hrs); ``` - +> Note that `Current.time` is *deprecated*. + + + ## Addresses @@ -129,26 +179,32 @@ KT1, ...). Currently, addresses are created by casting a string to the `address` type. Beware of failures if the address is invalid. Consider the following examples. - - + + + ```pascaligo group=d const my_account : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) ``` - + + + ```cameligo group=d let my_account : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) ``` - + + + ```reasonligo group=d let my_account : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); ``` - + + ## Signatures @@ -158,26 +214,35 @@ failure if the signature is invalid. Here is how you can define a signature: - - + + + ```pascaligo group=e const my_sig : signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" : signature) ``` - + + + + ```cameligo group=e let my_sig : signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" : signature) ``` - + + + + ```reasonligo group=e let my_sig : signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" : signature); ``` - + + + ## Keys @@ -187,20 +252,29 @@ failure if the key is invalid. Here is how you can define a key. - - + + + ```pascaligo group=f const my_key : key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key) ``` - + + + + ```cameligo group=f let my_key : key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key) ``` - + + + + ```reasonligo group=f let my_key : key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key); ``` - + + + diff --git a/gitlab-pages/docs/api/cheat-sheet.md b/gitlab-pages/docs/api/cheat-sheet.md index 8f8baebdb..412ba5bd5 100644 --- a/gitlab-pages/docs/api/cheat-sheet.md +++ b/gitlab-pages/docs/api/cheat-sheet.md @@ -2,14 +2,17 @@ id: cheat-sheet title: Cheat Sheet --- -
- - - + + |Primitive |Example| |--- |---| @@ -30,10 +33,10 @@ Note that this table is not compiled before production and currently needs to be |Includes|```#include "library.ligo"```| |Functions (short form)|
function add (const a : int ; const b : int) : int is
  block { skip } with a + b
| |Functions (long form)|
function add (const a : int ; const b : int) : int is
  block {
    const result: int = a + b;
  } with result
| -| If Statement |
if age < 16 
then fail("Too young to drive.");
else const new_id: int = prev_id + 1;
| +| If Statement |
if age < 16 
then failwith ("Too young to drive.");
else const new_id: int = prev_id + 1;
| |Options|
type middleName is option(string);
const middleName : middleName = Some("Foo");
const middleName : middleName = None;
| |Assignment| ```const age: int = 5;```| -|Assignment on an existing variable

*⚠️ This feature is not supported at the top-level scope, you can use it e.g. within functions. Works for Records and Maps as well.*| ```age := 18;```, ```p.age := 21``` | +|Assignment on an existing variable
*⚠️ This feature is not supported at the top-level scope, you can use it e.g. within functions. Works for Records and Maps as well.*| ```age := 18;```, ```p.age := 21``` | |Type Annotations| ```("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)```| |Variants|
type action is
| Increment of int
| Decrement of int
| |Variant *(pattern)* matching|
const a: action = Increment(5);
case a of
| Increment(n) -> n + 1
| Decrement(n) -> n - 1
end
| @@ -41,9 +44,10 @@ Note that this table is not compiled before production and currently needs to be |Maps|
type prices is map(nat, tez);

const prices : prices = map
  10n -> 60mutez;
  50n -> 30mutez;
  100n -> 10mutez;
end

const price: option(tez) = prices[50n];

prices[200n] := 5mutez;
| |Contracts & Accounts|
const destinationAddress : address = "tz1...";
const contract : contract(unit) = get_contract(destinationAddress);
| |Transactions|
const payment : operation = transaction(unit, amount, receiver);
| -|Exception/Failure|`failwith("Your descriptive error message for the user goes here.")`| +|Exception/Failure|`failwith ("Your descriptive error message for the user goes here.")`| - +
+ |Primitive |Example| |--- |---| @@ -63,7 +67,7 @@ Note that this table is not compiled before production and currently needs to be |Types|`type age = int`, `type name = string` | |Includes|```#include "library.mligo"```| |Functions |
let add (a : int) (b : int) : int = a + b 
| -| If Statement |
let new_id: int = if age < 16 
then failwith("Too young to drive.")
else prev_id + 1
| +| If Statement |
let new_id: int = if age < 16 
then failwith ("Too young to drive.")
else prev_id + 1
| |Options|
type middle_name = string option
let middle_name : middle_name = Some "Foo"
let middle_name : middle_name = None
| |Variable Binding | ```let age: int = 5```| |Type Annotations| ```("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)```| @@ -71,11 +75,12 @@ Note that this table is not compiled before production and currently needs to be |Variant *(pattern)* matching|
let a: action = Increment 5
match a with
| Increment n -> n + 1
| Decrement n -> n - 1
| |Records|
type person = {
  age: int ;
  name: string ;
}

let john : person = {
  age = 18;
  name = "John Doe";
}

let name: string = john.name
| |Maps|
type prices = (nat, tez) map

let prices : prices = Map.literal [
  (10n, 60mutez);
  (50n, 30mutez);
  (100n, 10mutez)
]

let price: tez option = Map.find_opt 50n prices

let prices: prices = Map.update 200n (Some 5mutez) prices
| -|Contracts & Accounts|
let destination_address : address = "tz1..."
let contract : unit contract =
Operation.get_contract destination_address
| -|Transactions|
let payment : operation = 
Operation.transaction unit amount receiver
| -|Exception/Failure|`failwith("Your descriptive error message for the user goes here.")`| +|Contracts & Accounts|
let destination_address : address = "tz1..."
let contract : unit contract =
Tezos.get_contract destination_address
| +|Transactions|
let payment : operation = 
Tezos.transaction unit amount receiver
| +|Exception/Failure|`failwith ("Your descriptive error message for the user goes here.")`| - +
+ |Primitive |Example| |--- |---| @@ -95,7 +100,7 @@ Note that this table is not compiled before production and currently needs to be |Types|`type age = int;`, `type name = string;` | |Includes|```#include "library.mligo"```| |Functions |
let add = (a: int, b: int) : int => a + b; 
| -| If Statement |
let new_id: int = if (age < 16) {
failwith("Too young to drive.");
} else { prev_id + 1; }
| +| If Statement |
let new_id: int = if (age < 16) {
failwith ("Too young to drive.");
} else { prev_id + 1; }
| |Options|
type middle_name = option(string);
let middle_name : middle_name = Some("Foo");
let middle_name : middle_name = None;
| |Variable Binding | ```let age: int = 5;```| |Type Annotations| ```("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)```| @@ -103,11 +108,12 @@ Note that this table is not compiled before production and currently needs to be |Variant *(pattern)* matching|
let a: action = Increment(5);
switch(a) {
| Increment(n) => n + 1
| Decrement(n) => n - 1;
}
| |Records|
type person = {
  age: int,
  name: string
}

let john : person = {
  age: 18,
  name: "John Doe"
};

let name: string = john.name;
| |Maps|
type prices = map(nat, tez);

let prices : prices = Map.literal([
  (10n, 60mutez),
  (50n, 30mutez),
  (100n, 10mutez)
]);

let price: option(tez) = Map.find_opt(50n, prices);

let prices: prices = Map.update(200n, Some (5mutez), prices);
| -|Contracts & Accounts|
let destination_address : address = "tz1...";
let contract : contract(unit) =
Operation.get_contract(destination_address);
| -|Transactions|
let payment : operation = 
Operation.transaction (unit, amount, receiver);
| -|Exception/Failure|`failwith("Your descriptive error message for the user goes here.");`| +|Contracts & Accounts|
let destination_address : address = "tz1...";
let contract : contract(unit) =
Tezos.get_contract(destination_address);
| +|Transactions|
let payment : operation = 
Tezos.transaction (unit, amount, receiver);
| +|Exception/Failure|`failwith ("Your descriptive error message for the user goes here.");`| + +
-
diff --git a/gitlab-pages/docs/intro/ligo-intro.md b/gitlab-pages/docs/intro/ligo-intro.md index 09da92f2a..b2dda419f 100644 --- a/gitlab-pages/docs/intro/ligo-intro.md +++ b/gitlab-pages/docs/intro/ligo-intro.md @@ -3,6 +3,9 @@ id: introduction title: Introduction To LIGO --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + LIGO is a programming language for writing [Tezos](https://tezos.com/) smart contracts. Smart contracts are a unique domain with extreme resource constraints and even more extreme security risks. Unlike desktop, mobile, or web @@ -49,8 +52,17 @@ Let's define some LIGO contract in the three flavours above. Do not worry if it is a little confusing at first; we will explain all the syntax in the upcoming sections of the documentation. - - + + + + ```pascaligo group=a type storage is int @@ -70,7 +82,9 @@ function main (const action : parameter; const store : storage) : return is end) ``` - + + + ```cameligo group=a type storage = int @@ -89,7 +103,9 @@ let main (action, store : parameter * storage) : return = | Reset -> 0) ``` - + + + ```reasonligo group=a type storage = int; @@ -108,7 +124,9 @@ let main = ((action, store): (parameter, storage)) : return => { | Reset => 0})); }; ``` - + + + This LIGO contract accepts the following LIGO expressions: `Increment(n)`, `Decrement(n)` and `Reset`. Those serve as diff --git a/gitlab-pages/docs/language-basics/boolean-if-else.md b/gitlab-pages/docs/language-basics/boolean-if-else.md index 8a872d777..8e35bc2f2 100644 --- a/gitlab-pages/docs/language-basics/boolean-if-else.md +++ b/gitlab-pages/docs/language-basics/boolean-if-else.md @@ -3,28 +3,39 @@ id: boolean-if-else title: Booleans and Conditionals --- +import Syntax from '@theme/Syntax'; + ## Booleans The type of a boolean value is `bool`. Here is how to define a boolean value: - - + + + ```pascaligo group=a -const a : bool = True // Notice the capital letter -const b : bool = False // Same. +const a : bool = True // Also: true +const b : bool = False // Also: false ``` - + + + + ```cameligo group=a let a : bool = true let b : bool = false ``` - + + + + ```reasonligo group=a let a : bool = true; let b : bool = false; ``` - + + + ## Comparing Values @@ -39,31 +50,41 @@ function. ### Comparing Strings - - + + + ```pascaligo group=b const a : string = "Alice" const b : string = "Alice" const c : bool = (a = b) // True ``` - + + + + ```cameligo group=b let a : string = "Alice" let b : string = "Alice" let c : bool = (a = b) // true ``` - + + + + ```reasonligo group=b let a : string = "Alice"; let b : string = "Alice"; let c : bool = (a == b); // true ``` - + + + ### Comparing numbers - - + + + ```pascaligo group=c const a : int = 5 const b : int = 4 @@ -74,7 +95,10 @@ const f : bool = (a <= b) const g : bool = (a >= b) const h : bool = (a =/= b) ``` - + + + + ```cameligo group=c let a : int = 5 let b : int = 4 @@ -86,7 +110,9 @@ let g : bool = (a >= b) let h : bool = (a <> b) ``` - + + + ```reasonligo group=c let a : int = 5; let b : int = 4; @@ -97,33 +123,43 @@ let f : bool = (a <= b); let g : bool = (a >= b); let h : bool = (a != b); ``` - + + + ### Comparing tez > 💡 Comparing `tez` values is especially useful when dealing with an > amount sent in a transaction. - - + + + ```pascaligo group=d const a : tez = 5mutez const b : tez = 10mutez const c : bool = (a = b) // False ``` - + + + + ```cameligo group=d let a : tez = 5mutez let b : tez = 10mutez let c : bool = (a = b) // false ``` - + + + ```reasonligo group=d let a : tez = 5mutez; let b : tez = 10mutez; let c : bool = (a == b); // false ``` - + + + ## Conditionals @@ -131,13 +167,14 @@ let c : bool = (a == b); // false Conditional logic enables forking the control flow depending on the state. - - + + + ```pascaligo group=e type magnitude is Small | Large // See variant types. function compare (const n : nat) : magnitude is - if n < 10n then Small (Unit) else Large (Unit) // Unit is needed for now. + if n < 10n then Small else Large ``` You can run the `compare` function defined above using the LIGO compiler @@ -145,7 +182,7 @@ like this: ```shell ligo run-function gitlab-pages/docs/language-basics/boolean-if-else/cond.ligo compare 21n' -# Outputs: Large (Unit) +# Outputs: Large(Unit) ``` When the branches of the conditional are not a single expression, as @@ -161,7 +198,7 @@ else skip; ``` As an exception to the rule, the blocks in a conditional branch do not -need to be introduced by the keywor `block`, so, we could have written +need to be introduced by the keyword `block`, so we could have written instead: ```pascaligo skip if x < y then { @@ -171,7 +208,9 @@ if x < y then { else skip; ``` - + + + ```cameligo group=e type magnitude = Small | Large // See variant types. @@ -187,9 +226,16 @@ gitlab-pages/docs/language-basics/boolean-if-else/cond.mligo compare 21n' # Outputs: Large ``` - +> Notice that, as in OCaml, in CameLIGO, if a conditional has a branch +> `else ()`, that branch can be omitted. The resulting so-called +> *dangling else* problem is parsed by associating any `else` to the +> closest previous `then`. + + + + ```reasonligo group=e -type magnitude = | Small | Large; // See variant types. +type magnitude = Small | Large; // See variant types. let compare = (n : nat) : magnitude => if (n < 10n) { Small; } else { Large; }; @@ -202,4 +248,6 @@ ligo run-function gitlab-pages/docs/language-basics/boolean-if-else/cond.religo compare 21n' # Outputs: Large ``` - + + + diff --git a/gitlab-pages/docs/language-basics/functions.md b/gitlab-pages/docs/language-basics/functions.md index d0344a754..eadc1c31e 100644 --- a/gitlab-pages/docs/language-basics/functions.md +++ b/gitlab-pages/docs/language-basics/functions.md @@ -3,13 +3,23 @@ id: functions title: Functions --- +import Syntax from '@theme/Syntax'; + LIGO functions are the basic building block of contracts. For example, -entrypoints are functions. +entrypoints are functions and each smart contract needs a main +function that dispatches control to the entrypoints (it is not already +the default entrypoint). + +The semantics of function calls in LIGO is that of a *copy of the +arguments but also of the environment*. In the case of PascaLIGO, this +means that any mutation (assignment) on variables outside the scope of +the function will be lost when the function returns, just as the +mutations inside the functions will be. ## Declaring Functions - - + + There are two ways in PascaLIGO to define functions: with or without a *block*. @@ -83,7 +93,8 @@ ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.ligo # Outputs: 3 ``` - + + Functions in CameLIGO are defined using the `let` keyword, like other values. The difference is that a succession of parameters is provided @@ -125,7 +136,7 @@ returns an integer as well: ```cameligo group=b let add (a, b : int * int) : int = a + b // Uncurried let add_curry (a : int) (b : int) : int = add (a, b) // Curried -let increment (b : int) : int = add_curry 1 // Partial application +let increment : int -> int = add_curry 1 // Partial application ``` You can run the `increment` function defined above using the LIGO @@ -137,7 +148,8 @@ ligo run-function gitlab-pages/docs/language-basics/src/functions/curry.mligo in The function body is a single expression, whose value is returned. - + + Functions in ReasonLIGO are defined using the `let` keyword, like other values. The difference is that a tuple of parameters is provided @@ -168,7 +180,8 @@ let myFun = ((x, y) : (int, int)) : int => { }; ``` - + + ## Anonymous functions (a.k.a. lambdas) @@ -178,8 +191,9 @@ a key in a record or a map. Here is how to define an anonymous function: - - + + + ```pascaligo group=c function increment (const b : int) : int is (function (const a : int) : int is a + 1) (b) @@ -193,7 +207,9 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.ligo a # Outputs: 2 ``` - + + + ```cameligo group=c let increment (b : int) : int = (fun (a : int) -> a + 1) b let a : int = increment 1 // a = 2 @@ -206,7 +222,9 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.mligo a # Outputs: 2 ``` - + + + ```reasonligo group=c let increment = (b : int) : int => ((a : int) : int => a + 1) (b); let a : int = increment (1); // a == 2 @@ -219,21 +237,27 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.religo # Outputs: 2 ``` - + + If the example above seems contrived, here is a more common design pattern for lambdas: to be used as parameters to functions. Consider the use case of having a list of integers and mapping the increment function to all its elements. - - + + + ```pascaligo group=c function incr_map (const l : list (int)) : list (int) is - list_map (function (const i : int) : int is i + 1, l) + List.map (function (const i : int) : int is i + 1, l) ``` -You can call the function `incr_map` defined above using the LIGO compiler -like so: + +> Note that `list_map` is *deprecated*. + +You can call the function `incr_map` defined above using the LIGO +compiler like so: + ```shell ligo run-function gitlab-pages/docs/language-basics/src/functions/incr_map.ligo incr_map @@ -241,7 +265,9 @@ gitlab-pages/docs/language-basics/src/functions/incr_map.ligo incr_map # Outputs: [ 2 ; 3 ; 4 ] ``` - + + + ```cameligo group=c let incr_map (l : int list) : int list = List.map (fun (i : int) -> i + 1) l @@ -255,7 +281,9 @@ gitlab-pages/docs/language-basics/src/functions/incr_map.mligo incr_map # Outputs: [ 2 ; 3 ; 4 ] ``` - + + + ```reasonligo group=c let incr_map = (l : list (int)) : list (int) => List.map ((i : int) => i + 1, l); @@ -269,4 +297,5 @@ gitlab-pages/docs/language-basics/src/functions/incr_map.religo incr_map # Outputs: [ 2 ; 3 ; 4 ] ``` - + + diff --git a/gitlab-pages/docs/language-basics/loops.md b/gitlab-pages/docs/language-basics/loops.md index bb31e124f..8f81c93ae 100644 --- a/gitlab-pages/docs/language-basics/loops.md +++ b/gitlab-pages/docs/language-basics/loops.md @@ -3,11 +3,13 @@ id: loops title: Loops --- +import Syntax from '@theme/Syntax'; + ## General Iteration - - + + General iteration in PascaLIGO takes the shape of general loops, which should be familiar to programmers of imperative languages as "while @@ -47,7 +49,8 @@ gitlab-pages/docs/language-basics/src/loops/gcd.ligo gcd '(2n*2n*3n*11n, 2n*2n*2 # Outputs: +12 ``` - + + CameLIGO is a functional language where user-defined values are constant, therefore it makes no sense in CameLIGO to feature loops, @@ -79,17 +82,22 @@ let gcd (x,y : nat * nat) : nat = ``` To ease the writing and reading of the iterated functions (here, -`iter`), two predefined functions are provided: `continue` and `stop`: +`iter`), two predefined functions are provided: `Loop.resume` and +`Loop.stop`: ```cameligo group=a let iter (x,y : nat * nat) : bool * (nat * nat) = - if y = 0n then stop (x,y) else continue (y, x mod y) + if y = 0n then Loop.stop (x,y) else Loop.resume (y, x mod y) let gcd (x,y : nat * nat) : nat = let x,y = if x < y then y,x else x,y in let x,y = Loop.fold_while iter (x,y) in x ``` + +> Note that `stop` and `continue` (now `Loop.resume`) are +> *deprecated*. + You can call the function `gcd` defined above using the LIGO compiler like so: ```shell @@ -98,7 +106,8 @@ gitlab-pages/docs/language-basics/src/loops/gcd.mligo gcd (2n*2n*3n*11n, 2n*2n*2 # Outputs: +12 ``` - + + ReasonLIGO is a functional language where user-defined values are constant, therefore it makes no sense in ReasonLIGO to feature loops, @@ -131,11 +140,12 @@ let gcd = ((x,y) : (nat, nat)) : nat => { ``` To ease the writing and reading of the iterated functions (here, -`iter`), two predefined functions are provided: `continue` and `stop`: +`iter`), two predefined functions are provided: `Loop.resume` and +`Loop.stop`: ```reasonligo group=b let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) => - if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); }; + if (y == 0n) { Loop.stop ((x,y)); } else { Loop.resume ((y, x mod y)); }; let gcd = ((x,y) : (nat, nat)) : nat => { let (x,y) = if (x < y) { (y,x); } else { (x,y); }; @@ -143,7 +153,12 @@ let gcd = ((x,y) : (nat, nat)) : nat => { x }; ``` - + +> Note that `stop` and `continue` (now `Loop.resume`) are +> *deprecated*. + + + ## Bounded Loops @@ -175,8 +190,8 @@ gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n PascaLIGO "for" loops can also iterate through the contents of a collection, that is, a list, a set or a map. This is done with a loop -of the form `for in -`, where `` is any of the following keywords: +of the form `for in `, +where `` is any of the following keywords: `list`, `set` or `map`. Here is an example where the integers in a list are summed up. diff --git a/gitlab-pages/docs/language-basics/maps-records.md b/gitlab-pages/docs/language-basics/maps-records.md index d691d588e..837290d39 100644 --- a/gitlab-pages/docs/language-basics/maps-records.md +++ b/gitlab-pages/docs/language-basics/maps-records.md @@ -3,6 +3,8 @@ id: maps-records title: Records and Maps --- +import Syntax from '@theme/Syntax'; + So far we have seen pretty basic data types. LIGO also offers more complex built-in constructs, such as *records* and *maps*. @@ -16,10 +18,11 @@ special operator (`.`). Let us first consider and example of record type declaration. - - -```pascaligo group=a + + + +```pascaligo group=records1 type user is record [ id : nat; @@ -28,8 +31,10 @@ type user is ] ``` - -```cameligo group=a + + + +```cameligo group=records1 type user = { id : nat; is_admin : bool; @@ -37,21 +42,26 @@ type user = { } ``` - -```reasonligo group=a + + + +```reasonligo group=records1 type user = { id : nat, is_admin : bool, name : string }; ``` - + + + And here is how a record value is defined: - - -```pascaligo group=a + + + +```pascaligo group=records1 const alice : user = record [ id = 1n; @@ -60,8 +70,10 @@ const alice : user = ] ``` - -```cameligo group=a + + + +```cameligo group=records1 let alice : user = { id = 1n; is_admin = true; @@ -69,37 +81,48 @@ let alice : user = { } ``` - -```reasonligo group=a + + + +```reasonligo group=records1 let alice : user = { id : 1n, is_admin : true, name : "Alice" }; ``` - + + + ### Accessing Record Fields If we want the contents of a given field, we use the (`.`) infix operator, like so: - - -```pascaligo group=a + + + +```pascaligo group=records1 const alice_admin : bool = alice.is_admin ``` - -```cameligo group=a + + + +```cameligo group=records1 let alice_admin : bool = alice.is_admin ``` - -```reasonligo group=a + + + +```reasonligo group=records1 let alice_admin : bool = alice.is_admin; ``` - + + + ### Functional Updates @@ -115,15 +138,16 @@ updated record. Let us consider defining a function that translates three-dimensional points on a plane. - - -In PascaLIGO, the shape of that expression is ` with -`. The record variable is the record to update and the + + +In PascaLIGO, the shape of that expression is +` with `. +The record variable is the record to update and the record value is the update itself. -```pascaligo group=b +```pascaligo group=records2 type point is record [x : int; y : int; z : int] type vector is record [dx : int; dy : int] @@ -146,12 +170,13 @@ You have to understand that `p` has not been changed by the functional update: a namless new version of it has been created and returned by the blockless function. - + + The syntax for the functional updates of record in CameLIGO follows that of OCaml: -```cameligo group=b +```cameligo group=records2 type point = {x : int; y : int; z : int} type vector = {dx : int; dy : int} @@ -174,12 +199,13 @@ xy_translate "({x=2;y=3;z=1}, {dx=3;dy=4})" > functional update: a nameless new version of it has been created and > returned. - + + The syntax for the functional updates of record in ReasonLIGO follows that of ReasonML: -```reasonligo group=b +```reasonligo group=records2 type point = {x : int, y : int, z : int}; type vector = {dx : int, dy : int}; @@ -188,7 +214,9 @@ let origin : point = {x : 0, y : 0, z : 0}; let xy_translate = ((p, vec) : (point, vector)) : point => {...p, x : p.x + vec.dx, y : p.y + vec.dy}; ``` - + + + You can call the function `xy_translate` defined above by running the following command of the shell: @@ -216,7 +244,7 @@ name "patch"). Let us consider defining a function that translates three-dimensional points on a plane. -```pascaligo group=c +```pascaligo group=records3 type point is record [x : int; y : int; z : int] type vector is record [dx : int; dy : int] @@ -242,7 +270,7 @@ Of course, we can actually translate the point with only one `patch`, as the previous example was meant to show that, after the first patch, the value of `p` indeed changed. So, a shorter version would be -```pascaligo group=d +```pascaligo group=records4 type point is record [x : int; y : int; z : int] type vector is record [dx : int; dy : int] @@ -267,7 +295,7 @@ Record patches can actually be simulated with functional updates. All we have to do is *declare a new record value with the same name as the one we want to update* and use a functional update, like so: -```pascaligo group=e +```pascaligo group=records5 type point is record [x : int; y : int; z : int] type vector is record [dx : int; dy : int] @@ -298,109 +326,162 @@ values of the same type. The former are called *key* and the latter is that the type of the keys must be *comparable*, in the Michelson sense. +### Declaring a Map + Here is how a custom map from addresses to a pair of integers is defined. - - -```pascaligo group=f + + + + +```pascaligo group=maps type move is int * int type register is map (address, move) ``` - -```cameligo group=f + + + +```cameligo group=maps type move = int * int type register = (address, move) map ``` - -```reasonligo group=f + + + +```reasonligo group=maps type move = (int, int); type register = map (address, move); ``` - -And here is how a map value is defined: + - - -```pascaligo group=f +### Creating an Empty Map + +Here is how to create an empty map. + + + + + +```pascaligo group=maps +const empty : register = map [] +``` + + + + +```cameligo group=maps +let empty : register = Map.empty +``` + + + + +```reasonligo group=maps +let empty : register = Map.empty +``` + + + + +### Creating a Non-empty Map + +And here is how to create a non-empty map value: + + + + +```pascaligo group=maps const moves : register = map [ ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2); ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (0,3)] ``` -> Notice the `->` between the key and its value and `;` to separate -> individual map entries. The annotated value `("" : -> address)` means that we cast a string into an address. Also, `map` -> is a keyword. +Notice the `->` between the key and its value and `;` to separate +individual map entries. The annotated value `("" : +address)` means that we cast a string into an address. Also, `map` is +a keyword. - -```cameligo group=f + + + +```cameligo group=maps let moves : register = Map.literal [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] ``` -> The `Map.literal` predefined function builds a map from a list of -> key-value pair tuples, `(, )`. Note also the `;` to -> separate individual map entries. `("": address)` -> means that we type-cast a string into an address. +The `Map.literal` predefined function builds a map from a list of +key-value pair tuples, `(, )`. Note also the `;` to +separate individual map entries. `("": address)` means +that we type-cast a string into an address. --> - -```reasonligo group=f + + + +```reasonligo group=maps let moves : register = Map.literal ([ ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); ``` -> The `Map.literal` predefined function builds a map from a list of -> key-value pair tuples, `(, )`. Note also the `;` to -> separate individual map entries. `("": address)` -> means that we type-cast a string into an address. +The `Map.literal` predefined function builds a map from a list of +key-value pair tuples, `(, )`. Note also the `;` to +separate individual map entries. `("": address)` means +that we type-cast a string into an address. --> + + - ### Accessing Map Bindings - - + + In PascaLIGO, we can use the postfix `[]` operator to read the `move` value associated to a given key (`address` here) in the register. Here is an example: -```pascaligo group=f +```pascaligo group=maps const my_balance : option (move) = moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` - -```cameligo group=f + + + +```cameligo group=maps let my_balance : move option = Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves ``` - -```reasonligo group=f + + + +```reasonligo group=maps let my_balance : option (move) = Map.find_opt (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), moves); ``` - + + + Notice how the value we read is an optional value: this is to force the reader to account for a missing key in the map. This requires *pattern matching*. - - -```pascaligo group=f + + + +```pascaligo group=maps function force_access (const key : address; const moves : register) : move is case moves[key] of Some (move) -> move @@ -408,16 +489,20 @@ function force_access (const key : address; const moves : register) : move is end ``` - -```cameligo group=f + + + +```cameligo group=maps let force_access (key, moves : address * register) : move = match Map.find_opt key moves with Some move -> move | None -> (failwith "No move." : move) ``` - -```reasonligo group=f + + + +```reasonligo group=maps let force_access = ((key, moves) : (address, register)) : move => { switch (Map.find_opt (key, moves)) { | Some (move) => move @@ -425,25 +510,26 @@ let force_access = ((key, moves) : (address, register)) : move => { } }; ``` - + + + ### Updating a Map Given a map, we may want to add a new binding, remove one, or modify -one by changing the value associated to an already existing key. We -may even want to retain the key but not the associated value. All +one by changing the value associated to an already existing key. All those operations are called *updates*. - - + + The values of a PascaLIGO map can be updated using the usual assignment syntax `[] := `. Let us consider an example. -```pascaligo group=f +```pascaligo group=maps function assign (var m : register) : register is block { m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9) @@ -453,7 +539,7 @@ function assign (var m : register) : register is If multiple bindings need to be updated, PascaLIGO offers a *patch instruction* for maps, similar to that for records. -```pascaligo group=f +```pascaligo group=maps function assignments (var m : register) : register is block { patch m with map [ @@ -463,72 +549,94 @@ function assignments (var m : register) : register is } with m ``` - +See further for the removal of bindings. + + + We can update a binding in a map in CameLIGO by means of the `Map.update` built-in function: -```cameligo group=f +```cameligo group=maps let assign (m : register) : register = Map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) m ``` -> Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had -> use `None` instead, that would have meant that the binding is -> removed. +Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had +use `None` instead, that would have meant that the binding is removed. - +As a particular case, we can only add a key and its associated value. + +```cameligo group=maps +let add (m : register) : register = + Map.add + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (4,9) m +``` + + + We can update a binding in a map in ReasonLIGO by means of the `Map.update` built-in function: -```reasonligo group=f +```reasonligo group=maps let assign = (m : register) : register => Map.update (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), Some ((4,9)), m); ``` -> Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had -> use `None` instead, that would have meant that the binding is -> removed. +Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had +use `None` instead, that would have meant that the binding is removed. + +As a particular case, we can only add a key and its associated value. + +```reasonligo group=maps +let add = (m : register) : register => + Map.add + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (4,9), m); +``` + + - To remove a binding from a map, we need its key. - - + + In PascaLIGO, there is a special instruction to remove a binding from a map. -```pascaligo group=f +```pascaligo group=maps function delete (const key : address; var moves : register) : register is block { remove key from map moves } with moves ``` - + + In CameLIGO, we use the predefined function `Map.remove` as follows: -```cameligo group=f +```cameligo group=maps let delete (key, moves : address * register) : register = Map.remove key moves ``` - + + In ReasonLIGO, we use the predefined function `Map.remove` as follows: -```reasonligo group=f +```reasonligo group=maps let delete = ((key, moves) : (address, register)) : register => Map.remove (key, moves); ``` - + + ### Functional Iteration over Maps @@ -549,101 +657,91 @@ no return value: its only use is to produce side-effects. This can be useful if for example you would like to check that each value inside of a map is within a certain range, and fail with an error otherwise. - +The predefined functional iterator implementing the iterated operation +over maps is called `Map.iter`. In the following example, the register +of moves is iterated to check that the start of each move is above +`3`. - -In PascaLIGO, the predefined functional iterator implementing the -iterated operation over maps is called `map_iter`. In the following -example, the register of moves is iterated to check that the start of -each move is above `3`. -```pascaligo group=f + + +```pascaligo group=maps function iter_op (const m : register) : unit is block { function iterated (const i : address; const j : move) : unit is if j.1 > 3 then Unit else (failwith ("Below range.") : unit) - } with map_iter (iterated, m) + } with Map.iter (iterated, m) ``` -> The iterated function must be pure, that is, it cannot mutate -> variables. +> Note that `map_iter` is *deprecated*. - + + -In CameLIGO, the predefinded functional iterator implementing the -iterated operation over maps is called `Map.iter`. In the following -example, the register of moves is iterated to check that the start of -each move is above `3`. - -```cameligo group=f +```cameligo group=maps let iter_op (m : register) : unit = let predicate = fun (i,j : address * move) -> assert (j.0 > 3) in Map.iter predicate m ``` - + + -In ReasonLIGO, the predefined functional iterator implementing the -iterated operation over maps is called `Map.iter`. In the following -example, the register of moves is iterated to check that the start of -each move is above `3`. - -```reasonligo group=f +```reasonligo group=maps let iter_op = (m : register) : unit => { let predicate = ((i,j) : (address, move)) => assert (j[0] > 3); Map.iter (predicate, m); }; ``` - + + + #### Map Operations over Maps We may want to change all the bindings of a map by applying to them a function. This is called a *map operation*, not to be confused with -the map data structure. +the map data structure. The predefined functional iterator +implementing the map operation over maps is called `Map.map`. In the +following example, we add `1` to the ordinate of the moves in the +register. - - -In PascaLIGO, the predefined functional iterator implementing the map -operation over maps is called `map_map` and is used as follows: + -```pascaligo group=f +```pascaligo group=maps function map_op (const m : register) : register is block { function increment (const i : address; const j : move) : move is - (j.0, j.1 + 1); - } with map_map (increment, m) + (j.0, j.1 + 1) + } with Map.map (increment, m) ``` -> The mapped function must be pure, that is, it cannot mutate -> variables. +> Note that `map_map` is *deprecated*. - + + -In CameLIGO, the predefined functional iterator implementing the map -operation over maps is called `Map.map` and is used as follows: - -```cameligo group=f +```cameligo group=maps let map_op (m : register) : register = let increment = fun (i,j : address * move) -> j.0, j.1 + 1 in Map.map increment m ``` - + + -In ReasonLIGO, the predefined functional iteratir implementing the map -operation over maps is called `Map.map` and is used as follows: - -```reasonligo group=f +```reasonligo group=maps let map_op = (m : register) : register => { let increment = ((i,j): (address, move)) => (j[0], j[1] + 1); Map.map (increment, m); }; ``` - + + + #### Folded Operations over Maps @@ -653,50 +751,44 @@ function takes two arguments: an *accumulator* and the structure enables having a partial result that becomes complete when the traversal of the data structure is over. - +The predefined functional iterator implementing the folded operation +over maps is called `Map.fold` and is used as follows. - -In PascaLIGO, the predefined functional iterator implementing the -folded operation over maps is called `map_fold` and is used as -follows: -```pascaligo group=f -function fold_op (const m : register) : int is block { - function folded (const j : int; const cur : address * move) : int is - j + cur.1.1 - } with map_fold (folded, m, 5) + + +```pascaligo group=maps +function fold_op (const m : register) : int is + block { + function folded (const i : int; const j : address * move) : int is + i + j.1.1 + } with Map.fold (folded, m, 5) ``` -> The folded function must be pure, that is, it cannot mutate -> variables. +> Note that `map_fold` is *deprecated*. - + + -In CameLIGO, the predefined functional iterator implementing the -folded operation over maps is called `Map.fold` and is used as -follows: - -```cameligo group=f -let fold_op (m : register) : register = +```cameligo group=maps +let fold_op (m : register) : int = let folded = fun (i,j : int * (address * move)) -> i + j.1.1 in Map.fold folded m 5 ``` - + + -In ReasonLIGO, the predefined functional iterator implementing the -folded operation over maps is called `Map.fold` and is used as -follows: - -```reasonligo group=f -let fold_op = (m : register) : register => { +```reasonligo group=maps +let fold_op = (m : register) : int => { let folded = ((i,j): (int, (address, move))) => i + j[1][1]; Map.fold (folded, m, 5); }; ``` - + + ## Big Maps @@ -709,78 +801,118 @@ were it not for *big maps*. Big maps are a data structure offered by Michelson which handles the scaling concerns for us. In LIGO, the interface for big maps is analogous to the one used for ordinary maps. +### Declaring a Map + Here is how we define a big map: - - -```pascaligo group=g -type move is int * int + + +```pascaligo group=big_maps +type move is int * int type register is big_map (address, move) ``` - -```cameligo group=g -type move = int * int + + +```cameligo group=big_maps +type move = int * int type register = (address, move) big_map ``` - -```reasonligo group=g -type move = (int, int); + + +```reasonligo group=big_maps +type move = (int, int); type register = big_map (address, move); ``` - -And here is how a map value is created: + - - -```pascaligo group=g +### Creating an Empty Big Map + +Here is how to create an empty big map. + + + + + + +```pascaligo group=big_maps +const empty : register = big_map [] +``` + + + + +```cameligo group=big_maps +let empty : register = Big_map.empty +``` + + + + +```reasonligo group=big_maps +let empty : register = Big_map.empty +``` + + + + +### Creating a Non-empty Map + +And here is how to create a non-empty map value: + + + + +```pascaligo group=big_maps const moves : register = big_map [ ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2); ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (0,3)] ``` -> Notice the right arrow `->` between the key and its value and the -> semicolon separating individual map entries. The value annotation -> `("" : address)` means that we cast a string into an -> address. +Notice the right arrow `->` between the key and its value and the +semicolon separating individual map entries. The value annotation +`("" : address)` means that we cast a string into an +address. --> - + + -```cameligo group=g +```cameligo group=big_maps let moves : register = Big_map.literal [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] ``` -> The predefind function `Big_map.literal` constructs a big map from a -> list of key-value pairs `(, )`. Note also the semicolon -> separating individual map entries. The annotated value `(" value>" : address)` means that we cast a string into an address. +The predefind function `Big_map.literal` constructs a big map from a +list of key-value pairs `(, )`. Note also the semicolon +separating individual map entries. The annotated value `(" +value>" : address)` means that we cast a string into an address. - + + -```reasonligo group=g +```reasonligo group=big_maps let moves : register = Big_map.literal ([ ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); ``` -> The predefind function `Big_map.literal` constructs a big map from a -> list of key-value pairs `(, )`. Note also the semicolon -> separating individual map entries. The annotated value `(" value>" : address)` means that we cast a string into an address. +The predefind function `Big_map.literal` constructs a big map from a +list of key-value pairs `(, )`. Note also the semicolon +separating individual map entries. The annotated value `(" +value>" : address)` means that we cast a string into an address. + - ### Accessing Values @@ -789,40 +921,44 @@ postfix `[]` operator to read the associated `move` value. However, the value we read is an optional value (in our case, of type `option (move)`), to account for a missing key. Here is an example: - - -```pascaligo group=g + + +```pascaligo group=big_maps const my_balance : option (move) = moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` - + + -```cameligo group=g +```cameligo group=big_maps let my_balance : move option = Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves ``` - + + -```reasonligo group=g +```reasonligo group=big_maps let my_balance : option (move) = Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, moves); ``` - + + + ### Updating Big Maps - - + + The values of a PascaLIGO big map can be updated using the assignment syntax for ordinary maps -```pascaligo group=g +```pascaligo group=big_maps function add (var m : register) : register is block { m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9) @@ -831,43 +967,46 @@ function add (var m : register) : register is const updated_map : register = add (moves) ``` - + + We can update a big map in CameLIGO using the `Big_map.update` built-in: -```cameligo group=g +```cameligo group=big_maps let updated_map : register = Big_map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves ``` - + + We can update a big map in ReasonLIGO using the `Big_map.update` built-in: -```reasonligo group=g +```reasonligo group=big_maps let updated_map : register = Big_map.update - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some ((4,9)), moves); ``` - + + ### Removing Bindings Removing a binding in a map is done differently according to the LIGO syntax. - - + + PascaLIGO features a special syntactic construct to remove bindings from maps, of the form `remove from map `. For example, -```pascaligo group=g +```pascaligo group=big_maps function rem (var m : register) : register is block { remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) from map moves @@ -876,24 +1015,27 @@ function rem (var m : register) : register is const updated_map : register = rem (moves) ``` - + + In CameLIGO, the predefined function which removes a binding in a map is called `Map.remove` and is used as follows: -```cameligo group=g +```cameligo group=big_maps let updated_map : register = Map.remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves ``` - + + In ReasonLIGO, the predefined function which removes a binding in a map is called `Map.remove` and is used as follows: -```reasonligo group=g +```reasonligo group=big_maps let updated_map : register = Map.remove (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves) ``` - + + diff --git a/gitlab-pages/docs/language-basics/math-numbers-tez.md b/gitlab-pages/docs/language-basics/math-numbers-tez.md index 41d05ed5d..c56119c64 100644 --- a/gitlab-pages/docs/language-basics/math-numbers-tez.md +++ b/gitlab-pages/docs/language-basics/math-numbers-tez.md @@ -3,7 +3,30 @@ id: math-numbers-tez title: Math, Numbers & Tez --- -LIGO offers three built-in numerical types: `int`, `nat` and `tez`. +import Syntax from '@theme/Syntax'; + +LIGO offers three built-in numerical types: `int`, `nat` and +`tez`. Values of type `int` are integers; values of type `nat` are +natural numbers (integral numbers greater than or equal to zero); +values of type `tez` are units of measure of Tezos tokens. + + * Integer literals are the same found in mainstream programming + languages, for example, `10`, `-6` and `0`, but there is only one + canonical zero: `0` (so, for instance, `-0` and `00` are invalid). + + * Natural numbers are written as digits follwed by the suffix `n`, + like so: `12n`, `0n`, and the same restriction on zero as integers + applies: `0n` is the only way to specify the natural zero. + + * Tezos tokens can be specified using literals of three kinds: + * units of millionth of `tez`, using the suffix `mutez` after a + natural literal, like `10000mutez` or `0mutez`; + * units of `tez`, using the suffix `tz` or `tez`, like `3tz` or + `3tez`; + * decimal amounts of `tz` or `tez`, like `12.3tz` or `12.4tez`. + +Note that large integral values can be expressed using underscores to +separate groups of digits, like `1_000mutez` or `0.000_004tez`. ## Addition @@ -17,8 +40,9 @@ remain in comments as they would otherwise not compile, for example, adding a value of type `int` to a value of type `tez` is invalid. Note that adding an integer to a natural number produces an integer. - - + + + ```pascaligo group=a // int + int yields int const a : int = 5 + 10 @@ -27,7 +51,7 @@ const a : int = 5 + 10 const b : int = 5n + 10 // tez + tez yields tez -const c : tez = 5mutez + 10mutez +const c : tez = 5mutez + 0.000_010tez //tez + int or tez + nat is invalid // const d : tez = 5mutez + 10n @@ -48,7 +72,9 @@ const g : int = 1_000_000 > const sum : tez = 100_000mutez >``` - + + + ```cameligo group=a // int + int yields int let a : int = 5 + 10 @@ -57,7 +83,7 @@ let a : int = 5 + 10 let b : int = 5n + 10 // tez + tez yields tez -let c : tez = 5mutez + 10mutez +let c : tez = 5mutez + 0.000_010tez // tez + int or tez + nat is invalid // let d : tez = 5mutez + 10n @@ -78,7 +104,9 @@ let g : int = 1_000_000 >let sum : tez = 100_000mutez >``` - + + + ```reasonligo group=a // int + int yields int let a : int = 5 + 10; @@ -87,7 +115,7 @@ let a : int = 5 + 10; let b : int = 5n + 10; // tez + tez yields tez -let c : tez = 5mutez + 10mutez; +let c : tez = 5mutez + 0.000_010tez; // tez + int or tez + nat is invalid: // let d : tez = 5mutez + 10n; @@ -106,7 +134,9 @@ let g : int = 1_000_000; >```reasonligo >let sum : tex = 100_000mutez; >``` - + + + ## Subtraction @@ -114,8 +144,9 @@ Subtraction looks as follows. > ⚠️ Even when subtracting two `nats`, the result is an `int` - - + + + ```pascaligo group=b const a : int = 5 - 10 @@ -128,7 +159,9 @@ const b : int = 5n - 2n const d : tez = 5mutez - 1mutez ``` - + + + ```cameligo group=b let a : int = 5 - 10 @@ -141,7 +174,9 @@ let b : int = 5n - 2n let d : tez = 5mutez - 1mutez ``` - + + + ```reasonligo group=b let a : int = 5 - 10; @@ -154,15 +189,16 @@ let b : int = 5n - 2n; let d : tez = 5mutez - 1mutez; ``` - + + ## Multiplication You can multiply values of the same type, such as: - - + + ```pascaligo group=c const a : int = 5 * 5 @@ -172,7 +208,9 @@ const b : nat = 5n * 5n const c : tez = 5n * 5mutez ``` - + + + ```cameligo group=c let a : int = 5 * 5 let b : nat = 5n * 5n @@ -181,7 +219,9 @@ let b : nat = 5n * 5n let c : tez = 5n * 5mutez ``` - + + + ```reasonligo group=c let a : int = 5 * 5; let b : nat = 5n * 5n; @@ -190,62 +230,123 @@ let b : nat = 5n * 5n; let c : tez = 5n * 5mutez; ``` - + -## Division + +## Euclidean Division In LIGO you can divide `int`, `nat`, and `tez`. Here is how: > ⚠️ Division of two `tez` values results into a `nat` - - + + + ```pascaligo group=d const a : int = 10 / 3 const b : nat = 10n / 3n const c : nat = 10mutez / 3mutez ``` - + + + ```cameligo group=d let a : int = 10 / 3 let b : nat = 10n / 3n let c : nat = 10mutez / 3mutez ``` - + + + ```reasonligo group=d let a : int = 10 / 3; let b : nat = 10n / 3n; let c : nat = 10mutez / 3mutez; ``` - + + + +LIGO also allows you to compute the remainder of the Euclidean +division. In LIGO, it is a natural number. + + + + +```pascaligo group=d +const a : int = 120 +const b : int = 9 +const rem1 : nat = a mod b // 3 +const c : nat = 120n +const rem2 : nat = c mod b // 3 +const d : nat = 9n +const rem3 : nat = c mod d // 3 +const rem4 : nat = a mod d // 3 +``` + + + + +```cameligo group=d +let a : int = 120 +let b : int = 9 +let rem1 : nat = a mod b // 3 +let c : nat = 120n +let rem2 : nat = c mod b // 3 +let d : nat = 9n +let rem3 : nat = c mod d // 3 +let rem4 : nat = a mod d // 3 +``` + + + + +```reasonligo group=d +let a : int = 120; +let b : int = 9; +let rem1 : nat = a mod b; // 3 +let c : nat = 120n; +let rem2 : nat = c mod b; // 3 +let d : nat = 9n; +let rem3 : nat = c mod d; // 3 +let rem4 : nat = a mod d; // 3 +``` + + + ## From `int` to `nat` and back You can *cast* an `int` to a `nat` and vice versa. Here is how: - - + + + ```pascaligo group=e const a : int = int (1n) const b : nat = abs (1) ``` - + + + ```cameligo group=e let a : int = int (1n) let b : nat = abs (1) ``` - + + + ```reasonligo group=e let a : int = int (1n); let b : nat = abs (1); ``` - + + ## Checking a `nat` @@ -254,20 +355,26 @@ function which accepts an `int` and returns an optional `nat`: if the result is not `None`, then the provided integer was indeed a natural number, and not otherwise. - - + + + ```pascaligo group=e const is_a_nat : option (nat) = is_nat (1) ``` - + + + ```cameligo group=e let is_a_nat : nat option = Michelson.is_nat (1) ``` - + + + ```reasonligo group=e let is_a_nat : option (nat) = Michelson.is_nat (1); ``` - + + diff --git a/gitlab-pages/docs/language-basics/operators.md b/gitlab-pages/docs/language-basics/operators.md index fc988c658..07dddbafc 100644 --- a/gitlab-pages/docs/language-basics/operators.md +++ b/gitlab-pages/docs/language-basics/operators.md @@ -3,13 +3,14 @@ id: operators title: Operators --- -## Available operators +## Available Operators -> This list is non-exhaustive. More operators will be added in upcoming LIGO releases. +> This list is non-exhaustive. More operators will be added in +> upcoming LIGO releases. |Michelson |Pascaligo |Description | |--- |--- |--- | -| `SENDER` | `sender` | Address that initiated the current transaction -| `SOURCE` | `source` | Address that initiated the transaction, which triggered the current transaction. (useful e.g. when there's a transaction sent by another contract) -| `AMOUNT` | `amount` | Amount of tez sent by the transaction that invoked the contract -| `NOW` | `now` | Timestamp of the block whose validation triggered execution of the contract, i.e. current time when the contract is run. \ No newline at end of file +| `SENDER` | `Tezos.sender` | Address that initiated the current transaction +| `SOURCE` | `Tezos.source` | Address that initiated the transaction, which triggered the current transaction. (useful e.g. when there's a transaction sent by another contract) +| `AMOUNT` | `Tezos.amount` | Amount of tez sent by the transaction that invoked the contract +| `NOW` | `Tezos.now` | Timestamp of the block whose validation triggered execution of the contract, i.e. current time when the contract is run. diff --git a/gitlab-pages/docs/language-basics/sets-lists-tuples.md b/gitlab-pages/docs/language-basics/sets-lists-tuples.md index a3db92b6d..31963eb26 100644 --- a/gitlab-pages/docs/language-basics/sets-lists-tuples.md +++ b/gitlab-pages/docs/language-basics/sets-lists-tuples.md @@ -3,6 +3,8 @@ id: sets-lists-tuples title: Tuples, Lists, Sets --- +import Syntax from '@theme/Syntax'; + Apart from complex data types such as `maps` and `records`, LIGO also features `tuples`, `lists` and `sets`. @@ -27,9 +29,9 @@ Unlike [a record](language-basics/maps-records.md), tuple types do not have to be defined before they can be used. However below we will give them names by *type aliasing*. - - + + ```pascaligo group=tuple type full_name is string * string // Alias @@ -37,7 +39,8 @@ type full_name is string * string // Alias const full_name : full_name = ("Alice", "Johnson") ``` - + + ```cameligo group=tuple type full_name = string * string // Alias @@ -45,7 +48,8 @@ type full_name = string * string // Alias let full_name : full_name = ("Alice", "Johnson") // Optional parentheses ``` - + + ```reasonligo group=tuple type full_name = (string, string); // Alias @@ -53,7 +57,8 @@ type full_name = (string, string); // Alias let full_name : full_name = ("Alice", "Johnson"); ``` - + + ### Accessing Components @@ -62,35 +67,34 @@ Accessing the components of a tuple in OCaml is achieved by [pattern matching](language-basics/unit-option-pattern-matching.md). LIGO currently supports tuple patterns only in the parameters of functions, not in pattern matching. However, we can access components by their -position in their tuple, which cannot be done in OCaml. +position in their tuple, which cannot be done in OCaml. *Tuple +components are zero-indexed*, that is, the first component has index +`0`. - - -Tuple components are one-indexed and accessed like so: + ```pascaligo group=tuple -const first_name : string = full_name.1 +const first_name : string = full_name.0 ``` - - -Tuple elements are zero-indexed and accessed like so: + + ```cameligo group=tuple let first_name : string = full_name.0 ``` - - -Tuple components are one-indexed and accessed like so: + + ```reasonligo group=tuple -let first_name : string = full_name[1]; +let first_name : string = full_name[0]; ``` - + + ## Lists @@ -103,30 +107,35 @@ called the *head*, and the sub-list after the head is called the think of a list a *stack*, where the top is written on the left. > 💡 Lists are needed when returning operations from a smart -> contract's access function. +> contract's main function. ### Defining Lists - - + + + ```pascaligo group=lists const empty_list : list (int) = nil // Or list [] const my_list : list (int) = list [1; 2; 2] // The head is 1 ``` - + + + ```cameligo group=lists let empty_list : int list = [] let my_list : int list = [1; 2; 2] // The head is 1 ``` - + + + ```reasonligo group=lists let empty_list : list (int) = []; let my_list : list (int) = [1, 2, 2]; // The head is 1 ``` - + ### Adding to Lists @@ -135,9 +144,9 @@ Lists can be augmented by adding an element before the head (or, in terms of stack, by *pushing an element on top*). This operation is usually called *consing* in functional languages. - - + + In PascaLIGO, the *cons operator* is infix and noted `#`. It is not symmetric: on the left lies the element to cons, and, on the right, a @@ -148,7 +157,8 @@ you of that.) const larger_list : list (int) = 5 # my_list // [5;1;2;2] ``` - + + In CameLIGO, the *cons operator* is infix and noted `::`. It is not symmetric: on the left lies the element to cons, and, on the right, a @@ -158,7 +168,8 @@ list on which to cons. let larger_list : int list = 5 :: my_list // [5;1;2;2] ``` - + + In ReasonLIGO, the *cons operator* is infix and noted `, ...`. It is not symmetric: on the left lies the element to cons, and, on the @@ -167,7 +178,9 @@ right, a list on which to cons. ```reasonligo group=lists let larger_list : list (int) = [5, ...my_list]; // [5,1,2,2] ``` - + + + ### Functional Iteration over Lists @@ -180,48 +193,36 @@ There are three kinds of functional iterations over LIGO lists: the *iterated operation*, the *map operation* (not to be confused with the *map data structure*) and the *fold operation*. -> 💡 Lists can be iterated, folded or mapped to different values. You -> can find additional examples -> [here](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts) -> and other built-in operators -> [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L59) - #### Iterated Operation over Lists The first, the *iterated operation*, is an iteration over the list with a unit return value. It is useful to enforce certain invariants -on the element of a list, or fail. For example you might want to check -that each value inside of a list is within a certain range, and fail -otherwise. +on the element of a list, or fail. - - - - -In PascaLIGO, the predefined functional iterator implementing the -iterated operation over lists is called `list_iter`. +For example you might want to check that each value inside of a list +is within a certain range, and fail otherwise. The predefined +functional iterator implementing the iterated operation over lists is +called `List.iter`. In the following example, a list is iterated to check that all its -elements (integers) are greater than `3`: +elements (integers) are strictly greater than `3`. + + + + ```pascaligo group=lists function iter_op (const l : list (int)) : unit is block { function iterated (const i : int) : unit is - if i > 2 then Unit else (failwith ("Below range.") : unit) - } with list_iter (iterated, l) + if i > 3 then Unit else (failwith ("Below range.") : unit) + } with List.iter (iterated, l) ``` -> The iterated function must be pure, that is, it cannot mutate -> variables. +> Note that `list_iter` is *deprecated*. - - -In CameLIGO, the predefined functional iterator implementing the -iterated operation over lists is called `List.iter`. - -In the following example, a list is iterated to check that all its -elements (integers) are greater than `3`: + + ```cameligo group=lists let iter_op (l : int list) : unit = @@ -229,13 +230,8 @@ let iter_op (l : int list) : unit = in List.iter predicate l ``` - - -In ReasonLIGO, the predefined functional iterator implementing the -iterated operation over lists is called `List.iter`. - -In the following example, a list is iterated to check that all its -elements (integers) are greater than `3`: + + ```reasonligo group=lists let iter_op = (l : list (int)) : unit => { @@ -244,38 +240,33 @@ let iter_op = (l : list (int)) : unit => { }; ``` - + + #### Mapped Operation over Lists We may want to change all the elements of a given list by applying to them a function. This is called a *map operation*, not to be confused -with the map data structure. +with the map data structure. The predefined functional iterator +implementing the mapped operation over lists is called `List.map` and +is used as follows. - - -In PascaLIGO, the predefined functional iterator implementing the -mapped operation over lists is called `list_map` and is used as -follows: + ```pascaligo group=lists function increment (const i : int): int is i + 1 // Creates a new list with all elements incremented by 1 -const plus_one : list (int) = list_map (increment, larger_list) +const plus_one : list (int) = List.map (increment, larger_list) ``` -> The mapped function must be pure, that is, it cannot mutate -> variables. +> Note that `list_map` is *deprecated*. - - -In CameLIGO, the predefined functional iterator implementing the -mapped operation over lists is called `List.map` and is used as -follows: + + ```cameligo group=lists let increment (i : int) : int = i + 1 @@ -284,11 +275,8 @@ let increment (i : int) : int = i + 1 let plus_one : int list = List.map increment larger_list ``` - - -In ReasonLIGO, the predefined functional iterator implementing the -mapped operation over lists is called `List.map` and is used as -follows: + + ```reasonligo group=lists let increment = (i : int) : int => i + 1; @@ -296,7 +284,9 @@ let increment = (i : int) : int => i + 1; // Creates a new list with all elements incremented by 1 let plus_one : list (int) = List.map (increment, larger_list); ``` - + + + #### Folded Operation over Lists @@ -305,58 +295,52 @@ A *folded operation* is the most general of iterations. The folded function takes two arguments: an *accumulator* and the structure *element* at hand, with which it then produces a new accumulator. This enables having a partial result that becomes complete when the -traversal of the data structure is over. +traversal of the data structure is over. The predefined functional +iterator implementing the folded operation over lists is called +`List.fold` and is used as follows. - - -In PascaLIGO, the predefined functional iterator implementing the -folded operation over lists is called `list_fold` and is used as -follows: + ```pascaligo group=lists function sum (const acc : int; const i : int): int is acc + i -const sum_of_elements : int = list_fold (sum, my_list, 0) +const sum_of_elements : int = List.fold (sum, my_list, 0) ``` -> The folded function must be pure, that is, it cannot mutate -> variables. +> Note that `list_fold` is *deprecated*. - - -In CameLIGO, the predefined functional iterator implementing the folded -operation over lists is called `List.fold` and is used as follows: + + ```cameligo group=lists let sum (acc, i: int * int) : int = acc + i let sum_of_elements : int = List.fold sum my_list 0 ``` - - -In ReasonLIGO, the predefined functional iterator implementing the -folded operation over lists is called `List.fold` and is used as -follows: + + ```reasonligo group=lists let sum = ((result, i): (int, int)): int => result + i; let sum_of_elements : int = List.fold (sum, my_list, 0); ``` - + + + ## Sets Sets are unordered collections of values of the same type, like lists are ordered collections. Like the mathematical sets and lists, sets can be empty and, if not, elements of sets in LIGO are *unique*, -whereas they can be repeated in a list. +whereas they can be repeated in a *list*. ### Empty Sets - - + + In PascaLIGO, the notation for sets is similar to that for lists, except the keyword `set` is used before: @@ -364,7 +348,9 @@ except the keyword `set` is used before: ```pascaligo group=sets const my_set : set (int) = set [] ``` - + + + In CameLIGO, the empty set is denoted by the predefined value `Set.empty`. @@ -373,7 +359,8 @@ In CameLIGO, the empty set is denoted by the predefined value let my_set : int set = Set.empty ``` - + + In ReasonLIGO, the empty set is denoted by the predefined value `Set.empty`. @@ -381,13 +368,15 @@ In ReasonLIGO, the empty set is denoted by the predefined value ```reasonligo group=sets let my_set : set (int) = Set.empty; ``` - + + + ### Non-empty Sets - - + + In PascaLIGO, the notation for sets is similar to that for lists, except the keyword `set` is used before: @@ -404,7 +393,8 @@ gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo my_set # Outputs: { 3 ; 2 ; 1 } ``` - + + In CameLIGO, there is no predefined syntactic construct for sets: you must build your set by adding to the empty set. (This is the way in @@ -424,7 +414,8 @@ gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.mligo my_set # Outputs: { 3 ; 2 ; 1 } ``` - + + In ReasonLIGO, there is no predefined syntactic construct for sets: you must build your set by adding to the empty set. (This is the way @@ -444,13 +435,15 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.religo my_set # Outputs: { 3 ; 2 ; 1 } ``` - + + + ### Set Membership - - + + PascaLIGO features a special keyword `contains` that operates like an infix operator checking membership in a set. @@ -459,7 +452,8 @@ infix operator checking membership in a set. const contains_3 : bool = my_set contains 3 ``` - + + In CameLIGO, the predefined predicate `Set.mem` tests for membership in a set as follows: @@ -468,7 +462,8 @@ in a set as follows: let contains_3 : bool = Set.mem 3 my_set ``` - + + In ReasonLIGO, the predefined predicate `Set.mem` tests for membership in a set as follows: @@ -476,57 +471,63 @@ in a set as follows: ```reasonligo group=sets let contains_3 : bool = Set.mem (3, my_set); ``` - + + + ### Cardinal of Sets - +The predefined function `Set.size` returns the number of +elements in a given set as follows. - -In PascaLIGO, the predefined function `size` returns the number of -elements in a given set as follows: + + ```pascaligo group=sets -const set_size : nat = size (my_set) +const cardinal : nat = Set.size (my_set) ``` - -In CameLIGO, the predefined function `Set.size` returns the number of -elements in a given set as follows: +> Note that `size` is *deprecated*. + + + ```cameligo group=sets -let set_size : nat = Set.size my_set +let cardinal : nat = Set.size my_set ``` - -In ReasonLIGO, the predefined function `Set.size` returns the number -of elements in a given set as follows: + + ```reasonligo group=sets -let set_size : nat = Set.size (my_set); +let cardinal : nat = Set.size (my_set); ``` - + + ### Updating Sets - +There are two ways to update a set, that is to add or remove from +it. - -In PascaLIGO, there are two ways to update a set, that is to add or -remove from it. Either we create a new set from the given one, or we + + + +In PascaLIGO, either we create a new set from the given one, or we modify it in-place. First, let us consider the former way: ```pascaligo group=sets -const larger_set : set (int) = set_add (4, my_set) - -const smaller_set : set (int) = set_remove (3, my_set) +const larger_set : set (int) = Set.add (4, my_set) +const smaller_set : set (int) = Set.remove (3, my_set) ``` +> Note that `set_add` and `set_remove` are *deprecated*. + If we are in a block, we can use an instruction to modify the set bound to a given variable. This is called a *patch*. It is only possible to add elements by means of a patch, not remove any: it is @@ -546,28 +547,32 @@ function update (var s : set (int)) : set (int) is block { const new_set : set (int) = update (my_set) ``` - + + -In CameLIGO, we update a given set by creating another one, with or +In CameLIGO, we can use the predefined functions `Set.add` and +`Set.remove`. We update a given set by creating another one, with or without some elements. ```cameligo group=sets let larger_set : int set = Set.add 4 my_set - let smaller_set : int set = Set.remove 3 my_set ``` - + + -In ReasonLIGO, we update a given set by creating another one, with or +In ReasonLIGO, we can use the predefined functions `Set.add` and +`Set.remove`. We update a given set by creating another one, with or without some elements. ```reasonligo group=sets let larger_set : set (int) = Set.add (4, my_set); - let smaller_set : set (int) = Set.remove (3, my_set); ``` - + + + ### Functional Iteration over Sets @@ -588,34 +593,27 @@ no return value: its only use is to produce side-effects. This can be useful if for example you would like to check that each value inside of a map is within a certain range, and fail with an error otherwise. - +The predefined functional iterator implementing the iterated operation +over sets is called `Set.iter`. In the following example, a set is +iterated to check that all its elements (integers) are greater than +`3`. - -In PascaLIGO, the predefined functional iterator implementing the -iterated operation over sets is called `set_iter`. -In the following example, a set is iterated to check that all its -elements (integers) are greater than `3`: + ```pascaligo group=sets function iter_op (const s : set (int)) : unit is block { function iterated (const i : int) : unit is if i > 2 then Unit else (failwith ("Below range.") : unit) - } with set_iter (iterated, s) + } with Set.iter (iterated, s) ``` -> The iterated function must be pure, that is, it cannot mutate -> variables. +> Note that `set_iter` is *deprecated*. - - -In CameLIGO, the predefined functional iterator implementing the -iterated operation over sets is called `Set.iter`. - -In the following example, a set is iterated to check that all its -elements (integers) are greater than `3`: + + ```cameligo group=sets let iter_op (s : int set) : unit = @@ -623,13 +621,8 @@ let iter_op (s : int set) : unit = in Set.iter predicate s ``` - - -In ReasonLIGO, the predefined functional iterator implementing the -iterated operation over sets is called `Set.iter`. - -In the following example, a set is iterated to check that all its -elements (integers) are greater than `3`: + + ```reasonligo group=sets let iter_op = (s : set (int)) : unit => { @@ -638,54 +631,55 @@ let iter_op = (s : set (int)) : unit => { }; ``` - + -#### Mapped Operation (NOT IMPLEMENTED YET) -We may want to change all the elements of a given set by applying to -them a function. This is called a *mapped operation*, not to be -confused with the map data structure. + - + + + - + -In PascaLIGO, the predefined functional iterator implementing the -mapped operation over sets is called `set_map` and is used as follows: + -```pascaligo skip -function increment (const i : int): int is i + 1 + + -// Creates a new set with all elements incremented by 1 -const plus_one : set (int) = set_map (increment, larger_set) -``` + + - + + + -In CameLIGO, the predefined functional iterator implementing the -mapped operation over sets is called `Set.map` and is used as follows: + -```cameligo skip -let increment (i : int) : int = i + 1 + + -// Creates a new set with all elements incremented by 1 -let plus_one : int set = Set.map increment larger_set -``` + + - + + + -In ReasonLIGO, the predefined functional iterator implementing the -mapped operation over sets is called `Set.map` and is used as follows: + -```reasonligo skip -let increment = (i : int) : int => i + 1; + + -// Creates a new set with all elements incremented by 1 -let plus_one : set (int) = Set.map (increment, larger_set); -``` + + - + + + + + #### Folded Operation @@ -693,24 +687,18 @@ A *folded operation* is the most general of iterations. The folded function takes two arguments: an *accumulator* and the structure *element* at hand, with which it then produces a new accumulator. This enables having a partial result that becomes complete when the -traversal of the data structure is over. +traversal of the data structure is over. The predefined fold over sets +is called `Set.fold`. - - - -In PascaLIGO, the predefined functional iterator implementing the -folded operation over sets is called `set_fold` and is used as -follows: + ```pascaligo group=sets function sum (const acc : int; const i : int): int is acc + i - -const sum_of_elements : int = set_fold (sum, my_set, 0) +const sum_of_elements : int = Set.fold (sum, my_set, 0) ``` -> The folded function must be pure, that is, it cannot mutate -> variables. +> Note that `set_fold` is *deprecated*. It is possible to use a *loop* over a set as well. @@ -723,23 +711,21 @@ function loop (const s : set (int)) : int is block { } with sum ``` - - -In CameLIGO, the predefined fold over sets is called `Set.fold`. + + ```cameligo group=sets let sum (acc, i : int * int) : int = acc + i - let sum_of_elements : int = Set.fold sum my_set 0 ``` - - -In ReasonLIGO, the predefined fold over sets is called `Set.fold`. + + ```reasonligo group=sets let sum = ((acc, i) : (int, int)) : int => acc + i; - let sum_of_elements : int = Set.fold (sum, my_set, 0); ``` - + + + diff --git a/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo index 68f01d71b..9722fbac3 100644 --- a/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo +++ b/gitlab-pages/docs/language-basics/src/boolean-if-else/cond.ligo @@ -1,4 +1,4 @@ type magnitude is Small | Large // See variant types function compare (const n : nat) : magnitude is - if n < 10n then Small (Unit) else Large (Unit) + if n < 10n then Small else Large diff --git a/gitlab-pages/docs/language-basics/src/functions/incr_map.ligo b/gitlab-pages/docs/language-basics/src/functions/incr_map.ligo index e5f915847..75bbb9c3a 100644 --- a/gitlab-pages/docs/language-basics/src/functions/incr_map.ligo +++ b/gitlab-pages/docs/language-basics/src/functions/incr_map.ligo @@ -2,4 +2,4 @@ function increment (const b : int) : int is (function (const a : int) : int is a + 1) (b) function incr_map (const l : list (int)) : list (int) is - list_map (function (const i : int) : int is i + 1, l) +List.map (function (const i : int) : int is i + 1, l) diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo index 7d2d49fbc..70e2b0655 100644 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo +++ b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/lists.ligo @@ -4,9 +4,8 @@ const larger_list : int_list = 5 # my_list function increment (const i : int): int is i + 1 -// Creates a new list with all elements incremented by 1 -const plus_one : list (int) = list_map (increment, larger_list); +const plus_one : list (int) = List.map (increment, larger_list); function sum (const acc : int; const i : int): int is acc + i -const sum_of_elements : int = list_fold (sum, my_list, 0) +const sum_of_elements : int = List.fold (sum, my_list, 0) diff --git a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo index b6ede8212..885d21e02 100644 --- a/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo +++ b/gitlab-pages/docs/language-basics/src/sets-lists-tuples/sets.ligo @@ -6,9 +6,9 @@ const contains_3 : bool = my_set contains 3 const set_size : nat = size (my_set) -const larger_set : int_set = set_add (4, my_set) +const larger_set : int_set = Set.add (4, my_set) -const smaller_set : int_set = set_remove (3, my_set) +const smaller_set : int_set = Set.remove (3, my_set) function update (var s : set (int)) : set (int) is block { patch s with set [4; 7] @@ -18,7 +18,7 @@ const new_set : set (int) = update (my_set) function sum (const acc : int; const i : int): int is acc + i -const sum_of_elements : int = set_fold (sum, my_set, 0) +const sum_of_elements : int = Set.fold (sum, my_set, 0) function loop (const s : set (int)) : int is block { var sum : int := 0; diff --git a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo index 5f54bc61e..f50bc7757 100644 --- a/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo +++ b/gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo @@ -2,6 +2,6 @@ type coin is Head | Tail function flip (const c : coin) : coin is case c of - Head -> Tail (Unit) // Unit needed because of a bug - | Tail -> Head (Unit) // Unit needed because of a bug + Head -> Tail + | Tail -> Head end diff --git a/gitlab-pages/docs/language-basics/strings.md b/gitlab-pages/docs/language-basics/strings.md index 461d0e38d..be1e201c9 100644 --- a/gitlab-pages/docs/language-basics/strings.md +++ b/gitlab-pages/docs/language-basics/strings.md @@ -3,28 +3,40 @@ id: strings title: Strings --- +import Syntax from '@theme/Syntax'; + Strings are defined using the built-in `string` type like this: - - + + + ``` const a : string = "Hello Alice" ``` - + + + + ``` let a : string = "Hello Alice" ``` - + + + + ```reasonligo let a : string = "Hello Alice"; ``` - + + + ## Concatenating Strings - - + + + Strings can be concatenated using the `^` operator. ```pascaligo group=a @@ -32,7 +44,10 @@ const name : string = "Alice" const greeting : string = "Hello" const full_greeting : string = greeting ^ " " ^ name ``` - + + + + Strings can be concatenated using the `^` operator. ```cameligo group=a @@ -40,7 +55,10 @@ let name : string = "Alice" let greeting : string = "Hello" let full_greeting : string = greeting ^ " " ^ name ``` - + + + + Strings can be concatenated using the `++` operator. ```reasonligo group=a @@ -48,52 +66,76 @@ let name : string = "Alice"; let greeting : string = "Hello"; let full_greeting : string = greeting ++ " " ++ name; ``` - + + + ## Slicing Strings Strings can be sliced using a built-in function: - - + + + ```pascaligo group=b const name : string = "Alice" -const slice : string = string_slice (0n, 1n, name) +const slice : string = String.slice (0n, 1n, name) ``` - + +> Note that `string_slide` is *deprecated*. + + + + ```cameligo group=b let name : string = "Alice" let slice : string = String.slice 0n 1n name ``` - + + + + ```reasonligo group=b let name : string = "Alice"; let slice : string = String.slice (0n, 1n, name); ``` - -> ⚠️ Notice that the offset and length of the slice are natural numbers. + + + +> ⚠️ Notice that the offset and length of the slice are natural +> numbers. ## Length of Strings The length of a string can be found using a built-in function: - - + + + ```pascaligo group=c const name : string = "Alice" -const length : nat = size (name) // length = 5 +const length : nat = String.length (name) // length = 5 ``` - + +> Note that `size` is *deprecated*. + + + + ```cameligo group=c let name : string = "Alice" let length : nat = String.size name // length = 5 ``` - + + + ```reasonligo group=c let name : string = "Alice"; let length : nat = String.size (name); // length == 5 ``` - + + + diff --git a/gitlab-pages/docs/language-basics/tezos-specific.md b/gitlab-pages/docs/language-basics/tezos-specific.md index e63764b66..a73698fd6 100644 --- a/gitlab-pages/docs/language-basics/tezos-specific.md +++ b/gitlab-pages/docs/language-basics/tezos-specific.md @@ -3,6 +3,8 @@ id: tezos-specific title: Tezos Domain-Specific Operations --- +import Syntax from '@theme/Syntax'; + LIGO is a programming language for writing Tezos smart contracts. It would be a little odd if it did not have any Tezos specific functions. This page will tell you about them. @@ -20,23 +22,30 @@ functionality can be accessed from within LIGO. > untrusted source or casting the result to the wrong type. Do not use > the corresponding LIGO functions without doing your homework first. - - + + + ```pascaligo group=a function id_string (const p : string) : option (string) is block { const packed : bytes = bytes_pack (p) -} with (bytes_unpack (packed) : option (string)) +} with (Bytes.unpack (packed) : option (string)) ``` - +> Note that `bytes_unpack` is *deprecated*. + + + + ```cameligo group=a let id_string (p : string) : string option = let packed: bytes = Bytes.pack p in (Bytes.unpack packed : string option) ``` - + + + ```reasonligo group=a let id_string = (p : string) : option (string) => { let packed : bytes = Bytes.pack (p); @@ -44,7 +53,8 @@ let id_string = (p : string) : option (string) => { }; ``` - + + ## Hashing Keys @@ -54,9 +64,10 @@ if this were not the case, hashes are much smaller than keys, and storage on blockchains comes at a cost premium. You can hash keys with a predefined functions returning a value of type `key_hash`. - - + + + ```pascaligo group=b function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block { @@ -66,14 +77,18 @@ function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash } with (ret, kh2) ``` - + + + ```cameligo group=b let check_hash_key (kh1, k2 : key_hash * key) : bool * key_hash = let kh2 : key_hash = Crypto.hash_key k2 in if kh1 = kh2 then true, kh2 else false, kh2 ``` - + + + ```reasonligo group=b let check_hash_key = ((kh1, k2) : (key_hash, key)) : (bool, key_hash) => { let kh2 : key_hash = Crypto.hash_key (k2); @@ -81,7 +96,8 @@ let check_hash_key = ((kh1, k2) : (key_hash, key)) : (bool, key_hash) => { }; ``` - + + ## Checking Signatures @@ -95,56 +111,114 @@ asynchronously. You can do this in LIGO using the `key` and > because that would require storing a private key on chain, at which > point it is not... private anymore. - - + + + ```pascaligo group=c function check_signature (const pk : key; const signed : signature; const msg : bytes) : bool - is crypto_check (pk, signed, msg) + is Crypto.check (pk, signed, msg) ``` - +> Note that `crypto_check` is *deprecated*. + + + + ```cameligo group=c let check_signature (pk, signed, msg : key * signature * bytes) : bool = Crypto.check pk signed msg ``` - + + + ```reasonligo group=c let check_signature = ((pk, signed, msg) : (key, signature, bytes)) : bool => Crypto.check (pk, signed, msg); ``` - + + ## Contract's Own Address Often you want to get the address of the contract being executed. You -can do it with `self_address`. +can do it with `Tezos.self_address`. -> ⚠️ Due to limitations in Michelson, `self_address` in a contract is -> only allowed at the top-level. Using it in an embedded function will -> cause an error. +> Note that `self_address` is *deprecated*. - +> ⚠️ Due to limitations in Michelson, `Tezos.self_address` in a +> contract is only allowed at the top-level. Using it in an embedded +> function will cause an error. + + + + - ```pascaligo group=d -const current_addr : address = self_address +const current_addr : address = Tezos.self_address ``` - + + + ```cameligo group=d -let current_addr : address = Current.self_address +let current_addr : address = Tezos.self_address ``` - + + + ```reasonligo group=d -let current_addr : address = Current.self_address; +let current_addr : address = Tezos.self_address; ``` - + + +## Origination of a contract + +`Tezos.create_contract` allows you to originate a contract given its code, delegate (if any), initial balance and initial storage. +The return value is a pair of type `(operation * address)`. + +> ⚠️ Due to limitations in Michelson, `Tezos.create_contract` first argument +> must be inlined and must not contain references to free variables + + + +```pascaligo group=e +const origination : operation * address = Tezos.create_contract ( + function (const p : nat; const s : string): list(operation) * string is ((nil : list(operation)), s), + (None : option(key_hash)), + 3tz, + "initial_storage") +``` + + + + +```cameligo group=e +let origination : operation * address = Tezos.create_contract + (fun (p, s : nat * string) -> (([] : operation list), s)) + (None: key_hash option) + 3tz + "initial_storage" +``` + + + + +```reasonligo group=e +let origination : (operation, address) = Tezos.create_contract ( + ((p, s) : (nat,string)) : (list(operation),string) => (([] : list(operation)), s), + None: option(key_hash), + 3tz, + "initial_storage") +``` + + + diff --git a/gitlab-pages/docs/language-basics/types.md b/gitlab-pages/docs/language-basics/types.md index 1ea5eeba0..5a58aa0cb 100644 --- a/gitlab-pages/docs/language-basics/types.md +++ b/gitlab-pages/docs/language-basics/types.md @@ -3,6 +3,8 @@ id: types title: Types --- +import Syntax from '@theme/Syntax'; + *LIGO is strongly and statically typed.* This means that the compiler checks how your contract processes data. If it passes the test, your contract will not fail at run-time due to inconsistent assumptions on @@ -22,36 +24,41 @@ maintainability of your smart contracts. For example we can choose to alias a string type as an animal breed - this will allow us to comunicate our intent with added clarity. - - + + + ```pascaligo group=a type breed is string const dog_breed : breed = "Saluki" ``` - + + ```cameligo group=a type breed = string let dog_breed : breed = "Saluki" ``` - + + ```reasonligo group=a type breed = string; let dog_breed : breed = "Saluki"; ``` - + + > The above type definitions are aliases, which means that `breed` and > `string` are interchangable in all contexts. ## Simple types - - + + + ```pascaligo group=b // The type account_balances denotes maps from addresses to tez @@ -61,7 +68,9 @@ const ledger : account_balances = map [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez] ``` - + + + ```cameligo group=b // The type account_balances denotes maps from addresses to tez @@ -72,7 +81,9 @@ let ledger : account_balances = [(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), 10mutez)] ``` - + + + ```reasonligo group=b // The type account_balances denotes maps from addresses to tez @@ -83,7 +94,8 @@ let ledger: account_balances = ([("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, 10mutez)]); ``` - + + ## Structured types @@ -96,8 +108,9 @@ types as *fields* and index them with a *field name*. In the example below you can see the definition of data types for a ledger that keeps the balance and number of previous transactions for a given account. - - + + + ```pascaligo group=c // Type aliasing @@ -124,7 +137,9 @@ const my_ledger : ledger = map [ ] ``` - + + + ```cameligo group=c // Type aliasing @@ -147,7 +162,9 @@ let my_ledger : ledger = Map.literal {balance = 10mutez; transactions = 5n})] ``` - + + + ```reasonligo group=c // Type aliasing @@ -177,7 +194,8 @@ are dual because records are a product of types (types are bundled into a record), whereas variant types are a sum of types (they are exclusive to each other). - + + ## Annotations @@ -185,9 +203,10 @@ In certain cases, the type of an expression cannot be properly inferred by the compiler. In order to help the type checker, you can annotate an expression with its desired type. Here is an example: - - + + + ```pascaligo group=d type parameter is Back | Claim | Withdraw @@ -213,7 +232,9 @@ function back (var action : unit; var store : storage) : return is end with ((nil : list (operation)), store) // Annotation ``` - + + + ```cameligo group=d type parameter = Back | Claim | Withdraw @@ -229,7 +250,7 @@ type return = operation list * storage let back (param, store : unit * storage) : return = let no_op : operation list = [] in - if Current.time > store.deadline then + if Tezos.now > store.deadline then (failwith "Deadline passed." : return) // Annotation else match Map.find_opt sender store.backers with @@ -239,7 +260,9 @@ let back (param, store : unit * storage) : return = | Some (x) -> no_op, store ``` - + + + ```reasonligo group=d type parameter = | Back | Claim | Withdraw; @@ -255,7 +278,7 @@ type return = (list (operation), storage); let back = ((param, store) : (unit, storage)) : return => { let no_op : list (operation) = []; - if (Current.time > store.deadline) { + if (Tezos.now > store.deadline) { (failwith ("Deadline passed.") : return); // Annotation } else { @@ -269,4 +292,5 @@ let back = ((param, store) : (unit, storage)) : return => { }; ``` - + + diff --git a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md b/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md index ff497a6b3..9ae8b34f3 100644 --- a/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md +++ b/gitlab-pages/docs/language-basics/unit-option-pattern-matching.md @@ -3,6 +3,9 @@ id: unit-option-pattern-matching title: Unit, Option, Pattern matching --- +import Syntax from '@theme/Syntax'; + + Optionals are a pervasive programing pattern in OCaml. Since Michelson and LIGO are both inspired by OCaml, *optional types* are available in LIGO as well. Similarly, OCaml features a *unit* type, and LIGO @@ -16,15 +19,16 @@ The `unit` type in Michelson or LIGO is a predefined type that contains only one value that carries no information. It is used when no relevant information is required or produced. Here is how it used. - - + + In PascaLIGO, the unique value of the `unit` type is `Unit`. ```pascaligo group=a const n : unit = Unit // Note the capital letter ``` - + + In CameLIGO, the unique value of the `unit` type is `()`, following the OCaml convention. @@ -32,7 +36,8 @@ the OCaml convention. let n : unit = () ``` - + + In ReasonLIGO, the unique value of the `unit` type is `()`, following the OCaml convention. @@ -40,7 +45,8 @@ the OCaml convention. let n : unit = (); ``` - + + ## Variant types @@ -52,39 +58,49 @@ the enumerated types found in Java, C++, JavaScript etc. Here is how we define a coin as being either head or tail (and nothing else): - - + + + ```pascaligo group=b type coin is Head | Tail -const head : coin = Head (Unit) // Unit needed for now. -const tail : coin = Tail (Unit) // Unit needed for now. +const head : coin = Head +const tail : coin = Tail ``` - + + + ```cameligo group=b type coin = Head | Tail let head : coin = Head let tail : coin = Tail ``` - + + + ```reasonligo group=b -type coin = | Head | Tail; +type coin = Head | Tail; let head : coin = Head; let tail : coin = Tail; ``` - + + + The names `Head` and `Tail` in the definition of the type `coin` are -called *data constructors*, or *variants*. +called *data constructors*, or *variants*. In this particular, they +carry no information beyond their names, so they are called *constant +constructors*. In general, it is interesting for variants to carry some information, and thus go beyond enumerated types. In the following, we show how to define different kinds of users of a system. - - + + + ```pascaligo group=c type id is nat @@ -94,10 +110,12 @@ type user is | Guest const u : user = Admin (1000n) -const g : user = Guest (Unit) // Unit needed because of a bug +const g : user = Guest ``` - + + + ```cameligo group=c type id = nat @@ -110,7 +128,9 @@ let u : user = Admin 1000n let g : user = Guest ``` - + + + ```reasonligo group=c type id = nat; @@ -123,9 +143,13 @@ let u : user = Admin (1000n); let g : user = Guest; ``` - + +In LIGO, a constant constructor is equivalent to the same constructor +taking an argument of type `unit`, so, for example, `Guest` is the +same value as `Guest (unit)`. + ## Optional values The `option` type is a predefined variant type that is used to express @@ -136,26 +160,32 @@ type would be `None`, otherwise `Some (v)`, where `v` is some meaningful value *of any type*. An example in arithmetic is the division operation: - - + + + ```pascaligo group=d function div (const a : nat; const b : nat) : option (nat) is if b = 0n then (None: option (nat)) else Some (a/b) ``` - + + + ```cameligo group=d let div (a, b : nat * nat) : nat option = if b = 0n then (None: nat option) else Some (a/b) ``` - + + + ```reasonligo group=d let div = ((a, b) : (nat, nat)) : option (nat) => if (b == 0n) { (None: option (nat)); } else { Some (a/b); }; ``` - + + ## Pattern matching @@ -165,15 +195,16 @@ Javascript, and can be used to route the program's control flow based on the value of a variant. Consider for example the definition of a function `flip` that flips a coin. - - + + + ```pascaligo group=e type coin is Head | Tail function flip (const c : coin) : coin is case c of - Head -> Tail (Unit) // Unit needed because of a bug - | Tail -> Head (Unit) // Unit needed because of a bug + Head -> Tail + | Tail -> Head end ``` @@ -181,11 +212,13 @@ You can call the function `flip` by using the LIGO compiler like so: ```shell ligo run-function gitlab-pages/docs/language-basics/src/unit-option-pattern-matching/flip.ligo -flip "(Head (Unit))" +flip "Head" # Outputs: Tail(Unit) ``` - + + + ```cameligo group=e type coin = Head | Tail @@ -203,7 +236,9 @@ flip Head # Outputs: Tail(Unit) ``` - + + + ```reasonligo group=e type coin = | Head | Tail; @@ -222,4 +257,5 @@ flip Head # Outputs: Tail(Unit) ``` - + + diff --git a/gitlab-pages/docs/language-basics/variables-and-constants.md b/gitlab-pages/docs/language-basics/variables-and-constants.md index 3fe31ff18..1194d6c23 100644 --- a/gitlab-pages/docs/language-basics/variables-and-constants.md +++ b/gitlab-pages/docs/language-basics/variables-and-constants.md @@ -3,6 +3,9 @@ id: constants-and-variables title: Constants & Variables --- +import Syntax from '@theme/Syntax'; + + The next building block after types are *constants* and *variables*. ## Constants @@ -12,8 +15,9 @@ reassigned. Put in another way, they can be assigned once, at their declaration. When defining a constant you need to provide a `name`, `type` and a `value`: - - + + + ```pascaligo group=a const age : int = 25 ``` @@ -24,7 +28,10 @@ command: ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.ligo age # Outputs: 25 ``` - + + + + ```cameligo group=a let age : int = 25 ``` @@ -36,7 +43,9 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constant # Outputs: 25 ``` - + + + ```reasonligo group=a let age : int = 25; ``` @@ -48,19 +57,23 @@ ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constant # Outputs: 25 ``` - + + ## Variables - - + + Variables, unlike constants, are *mutable*. They cannot be declared in a *global scope*, but they can be declared and used within functions, or as function parameters. > ⚠️ Please be wary that mutation only works within the function scope -> itself, values outside of the function scope will not be affected. +> itself, values outside of the function scope will not be +> affected. In other words, when a function is called, its arguments +> are copied, *as well as the environment*. Any side-effect to that +> environment is therefore lost when the function returns. ```pascaligo group=b @@ -85,7 +98,8 @@ ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/ # Outputs: 2 ``` - + + As expected in the pure subset of a functional language, CameLIGO only features *constant values*: once they are declared, the value cannot @@ -102,7 +116,9 @@ like this: ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo add '(1,1)' # Outputs: 2 ``` - + + + As expected in the pure subset of a functional language, ReasonLIGO only features *constant values*: once they are declared, the value @@ -122,4 +138,5 @@ ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/ # Outputs: 2 ``` - + + diff --git a/gitlab-pages/docs/reference/big_map.md b/gitlab-pages/docs/reference/big_map.md index cb733d61c..952275c1c 100644 --- a/gitlab-pages/docs/reference/big_map.md +++ b/gitlab-pages/docs/reference/big_map.md @@ -1,268 +1,207 @@ --- id: big-map-reference -title: Big Map — Scalable hashmap primitive +title: Big Maps — Scalable Maps --- -## Defining A Big Map Type +import Syntax from '@theme/Syntax'; - - -```pascaligo -type move is (int * int) -type moveset is big_map (address, move) -type foo is big_map (int, int) +Ordinary maps are fine for contracts with a finite lifespan or a +bounded number of users. For many contracts however, the intention is +to have a map holding *many* entries, potentially millions of +them. The cost of loading those entries into the environment each time +a user executes the contract would eventually become too expensive +were it not for *big maps*. Big maps are a data structure offered by +Michelson which handles the scaling concerns for us. In LIGO, the +interface for big maps is analogous to the one used for ordinary maps. + +# Declaring a Map + + + + +```pascaligo group=big_maps +type move is int * int +type register is big_map (address, move) ``` - -```cameligo + + + +```cameligo group=big_maps type move = int * int -type moveset = (address, move) big_map -type foo = (int, int) big_map +type register = (address, move) big_map ``` - -```reasonligo + + + +```reasonligo group=big_maps type move = (int, int); -type moveset = big_map(address, move); -type foo = big_map(int, int); +type register = big_map (address, move); ``` - + -## Creating A Map - - -```pascaligo -const moves: moveset = - big_map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1,2); - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0,3); - end +# Creating an Empty Big Map + + + + + +```pascaligo group=big_maps +const empty : register = big_map [] ``` - + + -```cameligo -let moves: moveset = +```cameligo group=big_maps +let empty : register = Big_map.empty +``` + + + + +```reasonligo group=big_maps +let empty : register = Big_map.empty +``` + + + + +# Creating a Non-empty Map + + + + + +```pascaligo group=big_maps +const moves : register = + big_map [ + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2); + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (0,3)] +``` + + + + +```cameligo group=big_maps +let moves : register = Big_map.literal [ - (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1,2)); - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0,3)); - ] + (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] ``` - + + -```reasonligo -let moves: moveset = +```reasonligo group=big_maps +let moves : register = Big_map.literal ([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1,2)), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0,3)), - ]); + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); ``` - + -## Big_map.find_opt(k: a', m: (a',b') big_map) : b' option -Retrieve the value associated with a particular key. This version returns an option -which can either shift logic in response to a missing value or throw an error. +# Accessing Values - - -```pascaligo -const my_balance : option(move) = - moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] + + + +```pascaligo group=big_maps +const my_balance : option (move) = + moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` - + + -```cameligo +```cameligo group=big_maps let my_balance : move option = - Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves + Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves ``` - + + -```reasonligo -let my_balance : option(move) = - Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); -``` - - -## Big_map.find(k: a', m: (a', b') big_map) : b' - -Forcefully retrieve the value associated with a particular key. If that value -doesn't exist, this function throws an error. - - - -```pascaligo -const my_balance : move = - get_force (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); +```reasonligo group=big_maps +let my_balance : option (move) = + Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, moves); ``` - + -```cameligo -let my_balance : move = - Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves -``` - -```reasonligo -let my_balance : move = - Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); -``` +# Updating Big Maps - -## Big_map.update(k: a', v: b', m: (a', b') big_map) : (a', b') big_map + -Change the value associated with a particular key, if that value doesn't already -exist add it. - - - - - -The values of a PascaLIGO big map can be updated using the ordinary -assignment syntax: - -```pascaligo - -function set_ (var m : moveset) : moveset is +```pascaligo group=big_maps +function add (var m : register) : register is block { - m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9); + m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9) } with m + +const updated_map : register = add (moves) ``` - + + -```cameligo -let updated_map : moveset = - Big_map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) (Some (4,9)) moves +```cameligo group=big_maps +let updated_map : register = + Big_map.update + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) moves ``` - + + -```reasonligo -let updated_map : moveset = - Big_map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); +```reasonligo group=big_maps +let updated_map : register = + Big_map.update + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some ((4,9)), moves); ``` - + -## Big_map.add(k: a', v: b', m: (a', b') big_map) : (a', b') big_map -Add a key and its associated value to the big map. +# Removing Bindings - - -```pascaligo -function set_ (var n : int ; var m : foo) : foo is block { - m[23] := n ; -} with m + + + +```pascaligo group=big_maps +function rem (var m : register) : register is + block { + remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) from map moves + } with m + +const updated_map : register = rem (moves) ``` - -```cameligo -let add (n,m : int * foo) : foo = Big_map.add 23 n m + + + +```cameligo group=big_maps +let updated_map : register = + Map.remove ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves ``` - -```reasonligo -let add = ((n,m): (int, foo)): foo => Big_map.add(23, n, m); + + + +```reasonligo group=big_maps +let updated_map : register = + Map.remove (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves) ``` - - -## Big_map.remove(k: a', m: (a', b') big_map) : (a', b') big_map - -Remove a key and its associated value from the big map. - - - - -```pascaligo -function rm (var m : foo) : foo is block { - remove 42 from map m; -} with m -``` - - -```cameligo -let rm (m : foo) : foo = Big_map.remove 42 m -``` - - -```reasonligo -let rm = (m: foo): foo => Big_map.remove(42, m); -``` - - - -## Big_map.literal(key_value_pair_list: (a', b') list) : (a', b') big_map - -Constructs a big map from a list of key-value pair tuples. - - - - -```pascaligo -const moves: moveset = - big_map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1,2); - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0,3); - end -``` - - - -```cameligo -let moves: moveset = - Big_map.literal [ - (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1,2)); - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0,3)); - ] -``` - - - -```reasonligo -let moves: moveset = - Big_map.literal ([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1,2)), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0,3)), - ]); -``` - - - - -## Big_map.empty() : (a', b') big_map - -Create an empty big map. - - - - -```pascaligo -const empty_big_map : big_map(int,int) = big_map end -``` - - -```cameligo -let empty_map : foo = Big_map.empty -``` - - -```reasonligo -let empty_map: foo = Big_map.empty; -``` - - + diff --git a/gitlab-pages/docs/reference/bytes.md b/gitlab-pages/docs/reference/bytes.md index c585ffeed..dc8dc1aec 100644 --- a/gitlab-pages/docs/reference/bytes.md +++ b/gitlab-pages/docs/reference/bytes.md @@ -3,62 +3,67 @@ id: bytes-reference title: Bytes — Manipulate bytes data --- +import Syntax from '@theme/Syntax'; + ## Bytes.concat(b1: bytes, b2: bytes) : bytes Concatenate together two `bytes` arguments and return the result. - - - + ```pascaligo function concat_op (const s : bytes) : bytes is begin skip end with bytes_concat(s , 0x7070) ``` - + + ```cameligo let concat_op (s : bytes) : bytes = Bytes.concat s 0x7070 ``` - + + ```reasonligo let concat_op = (s: bytes): bytes => Bytes.concat(s, 0x7070); ``` - + ## Bytes.slice(pos1: nat, pos2: nat, data: bytes) : bytes Extract the bytes between `pos1` and `pos2`. **Positions are zero indexed and inclusive**. For example if you gave the input "ff7a7aff" to the following: - - + + ```pascaligo function slice_op (const s : bytes) : bytes is begin skip end with bytes_slice(1n , 2n , s) ``` - + + ```cameligo let slice_op (s : bytes) : bytes = Bytes.slice 1n 2n s ``` - + + ``` let slice_op = (s: bytes): bytes => Bytes.slice(1n, 2n, s); ``` - + + It would return "7a7a" rather than "ff7a" or "ff" or "7a". @@ -68,23 +73,28 @@ Converts Michelson data structures to a binary format for serialization. > ⚠️ `PACK` and `UNPACK` are features of Michelson that are intended to be used by people that really know what they're doing. There are several failure cases (such as `UNPACK`ing a lambda from an untrusted source), most of which are beyond the scope of this document. Don't use these functions without doing your homework first. - - + + + ```pascaligo function id_string (const p : string) : option(string) is block { const packed : bytes = bytes_pack(p) ; } with (bytes_unpack(packed): option(string)) ``` - + + + ```cameligo let id_string (p: string) : string option = let packed: bytes = Bytes.pack p in ((Bytes.unpack packed): string option) ``` - + + + ```reasonligo let id_string = (p: string) : option(string) => { let packed : bytes = Bytes.pack(p); @@ -92,7 +102,8 @@ let id_string = (p: string) : option(string) => { }; ``` - + + ## Bytes.unpack(packed: bytes) : a' @@ -101,23 +112,28 @@ serialization format to the `option` type annotated on the call. > ⚠️ `PACK` and `UNPACK` are features of Michelson that are intended to be used by people that really know what they're doing. There are several failure cases (such as `UNPACK`ing a lambda from an untrusted source), most of which are beyond the scope of this document. Don't use these functions without doing your homework first. - - + + + ```pascaligo function id_string (const p : string) : option(string) is block { const packed : bytes = bytes_pack(p) ; } with (bytes_unpack(packed): option(string)) ``` - + + + ```cameligo let id_string (p: string) : string option = let packed: bytes = Bytes.pack p in ((Bytes.unpack packed): string option) ``` - + + + ```reasonligo let id_string = (p: string) : option(string) => { let packed : bytes = Bytes.pack(p); @@ -125,4 +141,5 @@ let id_string = (p: string) : option(string) => { }; ``` - + + diff --git a/gitlab-pages/docs/reference/crypto.md b/gitlab-pages/docs/reference/crypto.md index e2b0511d2..49f5a9f02 100644 --- a/gitlab-pages/docs/reference/crypto.md +++ b/gitlab-pages/docs/reference/crypto.md @@ -3,93 +3,108 @@ id: crypto-reference title: Crypto — Cryptographic functions --- +import Syntax from '@theme/Syntax'; + ## Crypto.blake2b(data: bytes): bytes Runs the [blake2b hash algorithm](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2) over the given `bytes` data and returns a `bytes` representing the hash. - - + + ```pascaligo function hasherman_blake (const s: bytes) : bytes is blake2b(s) ``` - + + ```cameligo let hasherman_blake (s: bytes) : bytes = Crypto.blake2b s ``` - + + ```reasonligo let hasherman_blake = (s: bytes) => Crypto.blake2b(s); ``` - + + ## Crypto.sha256(data: bytes) : bytes Runs the [sha256 hash algorithm](https://en.wikipedia.org/wiki/SHA-2) over the given `bytes` data and returns a `bytes` representing the hash. - - + + + ```pascaligo function hasherman (const s : bytes) : bytes is begin skip end with sha_256(s) ``` - + + + ```cameligo let hasherman (s : bytes) : bytes = Crypto.sha256 s ``` - + + + ```reasonligo let hasherman = (s: bytes): bytes => Crypto.sha256(s); ``` - + + ## Crypto.sha512(data: bytes) : bytes Runs the [sha512 hash algorithm](https://en.wikipedia.org/wiki/SHA-2) over the given `bytes` data and returns a `bytes` representing the hash. - - + + ```pascaligo function hasherman512 (const s: bytes) : bytes is sha_512(s) ``` - + + ```cameligo let hasherman512 (s: bytes) : bytes = Crypto.sha512 s ``` - + + ```reasonligo let hasherman512 = (s: bytes) => Crypto.sha512(s); ``` - + + ## Crypto.hash_key(k: key) : key_hash Hashes a key for easy comparison and storage. - - + + + ```pascaligo function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block { var ret : bool := False ; @@ -98,7 +113,9 @@ function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash } with (ret, kh2) ``` - + + + ```cameligo let check_hash_key (kh1, k2: key_hash * key) : bool * key_hash = let kh2 : key_hash = Crypto.hash_key k2 in @@ -107,7 +124,9 @@ let check_hash_key (kh1, k2: key_hash * key) : bool * key_hash = else (false, kh2) ``` - + + + ```reasonligo let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => { let kh2 : key_hash = Crypto.hash_key(k2); @@ -120,7 +139,8 @@ let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => { }; ``` - + + ## Crypto.check(pk: key, signed: signature, data: bytes) : bool @@ -128,9 +148,10 @@ Check that a message has been signed by a particular key. > ⚠️ There is no way to *generate* a signed message in LIGO. This is because that would require storing a private key on chain, at which point it isn't very private anymore. - - + + + ```pascaligo function check_signature (const pk: key; @@ -139,17 +160,22 @@ function check_signature is crypto_check(pk, signed, msg) ``` - + + + ```cameligo let check_signature (pk, signed, msg: key * signature * bytes) : bool = Crypto.check pk signed msg ``` - + + + ```reasonligo let check_signature = ((pk, signed, msg): (key, signature, bytes)) : bool => { Crypto.check(pk, signed, msg); }; ``` - + + diff --git a/gitlab-pages/docs/reference/current.md b/gitlab-pages/docs/reference/current.md index c9f1945d8..0b5cd7265 100644 --- a/gitlab-pages/docs/reference/current.md +++ b/gitlab-pages/docs/reference/current.md @@ -1,319 +1,456 @@ --- id: current-reference -title: Current - Things relating to the current execution context +title: Tezos - Things relating to the current execution context --- -## Current.balance() : tez +import Syntax from '@theme/Syntax'; + +# Tezos.balance Get the balance for the contract. - - + + + ```pascaligo -function main (const p : unit; const s: tez) : list(operation) * tez is - ((nil : list(operation)), balance) +function main (const p : unit; const s: tez) : list (operation) * tez is + ((nil : list (operation)), Tezos.balance) ``` - + +> Note that `balance` and `Current.balance` are *deprecated*. + + + + ```cameligo -let main (p, s : unit * tez) = - ([] : operation list), balance +let main (p,s : unit * tez) = ([] : operation list), Tezos.balance ``` - + +> Note that `balance` and `Current.balance` are *deprecated*. + + + + ```reasonligo -let main = ((p,s): (unit, tez)) => ([]: list(operation), balance); +let main = ((p,s) : (unit, tez)) => + ([]: list (operation), Tezos.balance); ``` - +> Note that `balance` and `Current.balance` are *deprecated*. -## Current.time() : timestamp + -Returns the current time as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time). -In LIGO, timestamps are type compatible in operations with `int`(s). This lets you set e.g. time constraints for your smart contracts like this: +## Tezos.now + +Returns the current time as a [unix timestamp](https://en.wikipedia.org/wiki/Unix_time). + +In LIGO, timestamps are type compatible in operations with +integers. This lets you set for instance time constraints for your +smart contracts like this: ### Examples #### 24 hours from now - - + + + ```pascaligo group=b -const today: timestamp = now; -const one_day: int = 86400; +const today: timestamp = Tezos.now; +const one_day: int = 86_400; const in_24_hrs: timestamp = today + one_day; const some_date: timestamp = ("2000-01-01T10:10:10Z" : timestamp); const one_day_later: timestamp = some_date + one_day; ``` - +> Note that `now` is *deprecated*. + + + + ```cameligo group=b -let today: timestamp = Current.time -let one_day: int = 86400 +let today: timestamp = Tezos.now +let one_day: int = 86_400 let in_24_hrs: timestamp = today + one_day let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp) let one_day_later: timestamp = some_date + one_day ``` - +> Note that `Current.time` is *deprecated*. + + + + ```reasonligo group=b -let today: timestamp = Current.time; -let one_day: int = 86400; +let today: timestamp = Tezos.now; +let one_day: int = 86_400; let in_24_hrs: timestamp = today + one_day; let some_date: timestamp = ("2000-01-01t10:10:10Z" : timestamp); let one_day_later: timestamp = some_date + one_day; ``` - +> Note that `Current.time` is *deprecated*. + + + #### 24 hours ago - - + + + + ```pascaligo group=c -const today: timestamp = now; -const one_day: int = 86400; +const today: timestamp = Tezos.now; +const one_day: int = 86_400; const in_24_hrs: timestamp = today - one_day; ``` - +> Note that `now` is *deprecated*. + + + + ```cameligo group=c -let today: timestamp = Current.time -let one_day: int = 86400 +let today: timestamp = Tezos.now +let one_day: int = 86_400 let in_24_hrs: timestamp = today - one_day ``` - +> Note that `Current.time` is *deprecated*. + + + + ```reasonligo group=c -let today: timestamp = Current.time; -let one_day: int = 86400; +let today: timestamp = Tezos.now; +let one_day: int = 86_400; let in_24_hrs: timestamp = today - one_day; ``` - +> Note that `Current.time` is *deprecated*. -#### Comparing timestamps + -You can also compare timestamps using the same comparison operators as for numbers: - - +#### Comparing Timestamps + +You can also compare timestamps using the same comparison operators as +for numbers + + + + ```pascaligo group=c -const not_tommorow: bool = (now = in_24_hrs) +const not_tommorow: bool = (Tezos.now = in_24_hrs) ``` - +> Note that `now` is *deprecated*. + + + + ```cameligo group=c -let not_tomorrow: bool = (Current.time = in_24_hrs) +let not_tomorrow: bool = (Tezos.now = in_24_hrs) ``` - +> Note that `Current.time` is *deprecated*. + + + + ```reasonligo group=c -let not_tomorrow: bool = (Current.time == in_24_hrs); +let not_tomorrow: bool = (Tezos.now == in_24_hrs); ``` - +> Note that `Current.time` is *deprecated*. + + -## Current.amount() : tez -Get the amount of tez provided by the sender to complete this transaction. +## Amount - +Get the amount of tez provided by the sender to complete this +transaction. + + + + - ```pascaligo -function check (const p: unit) : int is - begin - var result : int := 0; - if amount = 100tz then - result := 42 - else - result := 0 - end with result +function threshold (const p : unit) : int is + if Tezos.amount = 100tz then 42 else 0 ``` - +> Note that `amount` is *deprecated*. + + + + ```cameligo -let check_ (p: unit) : int = if Current.amount = 100tz then 42 else 0 +let threshold (p : unit) : int = if Tezos.amount = 100tz then 42 else 0 ``` - +> Note that `Current.amount` is *deprecated*. + + + + ```reasonligo -let check_ = (p: unit) : int => - if (Current.amount == 100tz) { - 42; - } - else { - 0; - }; +let threshold = (p : unit) : int => + if (Tezos.amount == 100tz) { 42; } else { 0; }; ``` - +> Note that `Current.amount` is *deprecated*. -## Current.sender() : address + + + +## Sender Get the address that initiated the current transaction. - - + + + ```pascaligo -function main (const p: unit) : address is sender +function main (const p : unit) : address is Tezos.sender ``` - +> Note that `sender` is *deprecated*. + + + + ```cameligo -let main (p: unit) : address = Current.sender +let main (p: unit) : address = Tezos.sender ``` - +> Note that `Current.sender` is *deprecated*. + + + + ```reasonligo -let main = (p: unit) : address => Current.sender; +let main = (p : unit) : address => Tezos.sender; ``` - +> Note that `Current.sender` is *deprecated*. -## Current.address(c: a' contract) : address + -Get the address associated with a `contract`. - - +## Address + +Get the address associated with a value of type `contract`. + + + + + ```pascaligo function main (const p : key_hash) : address is block { - const c : contract(unit) = implicit_account(p) ; -} with address(c) + const c : contract (unit) = Tezos.implicit_account (p) +} with Tezos.address(c) ``` - +> Note that `implicit_account` and `address` are *deprecated*. + + + + ```cameligo let main (p : key_hash) = - let c : unit contract = Current.implicit_account p in - Current.address c + let c : unit contract = Tezos.implicit_account p + in Tezos.address c ``` - +> Note that `Current.implicit_account` and `Current.address` are +> *deprecated*. + + + + ```reasonligo let main = (p : key_hash) : address => { - let c : contract(unit) = Current.implicit_account(p) ; - Current.address(c) ; + let c : contract (unit) = Tezos.implicit_account (p); + Tezos.address (c); }; ``` - +> Note that `Current.implicit_account` and `Current.address` are +> *deprecated*. -## Current.self_address() : address + + + +## Self Address Get the address of the currently running contract. - - + + + ```pascaligo -function main (const p: unit) : address is self_address +function main (const p : unit) : address is Tezos.self_address ``` - +> Note that `self_address` is *deprecated*. + + + + ```cameligo -let main (p: unit) : address = Current.self_address +let main (p : unit) : address = Tezos.self_address ``` - +> Note that `Current.self_address` is *deprecated*. + + + + ```reasonligo -let main = (p: unit): address => Current.self_address; +let main = (p : unit) : address => Tezos.self_address; ``` - +> Note that `Current.self_address` is *deprecated*. -## Current.implicit_account(p: key_hash) : a' contract + -Get the default contract associated with an on-chain keypair. This contract -doesn't execute code, instead it exists to receive money on behalf of a keys -owner. - +## Implicit Account + +Get the default contract associated with an on-chain key-pair. This +contract does not execute code, instead it exists to receive tokens on +behalf of a key's owner. + + + + - ```pascaligo -function main (const kh: key_hash) : contract(unit) is implicit_account(kh) +function main (const kh: key_hash) : contract (unit) is + Tezos.implicit_account (kh) ``` - +> Note that `implicit_account` is *deprecated*. + + + + ```cameligo -let main (kh: key_hash) : unit contract = Current.implicit_account kh +let main (kh : key_hash) : unit contract = Tezos.implicit_account kh ``` - +> Note that `Current.implicit_account` is *deprecated*. + + + + ```reasonligo -let main = (kh: key_hash): contract(unit) => Current.implicit_account(kh); +let main = (kh : key_hash): contract (unit) => + Tezos.implicit_account (kh); ``` - +> Note that `Current.implicit_account` is *deprecated*. -## Current.source() : address + -Get the _originator_ of the current transaction. That is, if a chain of transactions -led to the current execution get the address that began the chain. Not to be confused -with `Current.sender`, which gives the address of the contract or user which directly -caused the current transaction. -> ⚠️ -> There are a few caveats you should keep in mind before using `SOURCE` over `SENDER`: +## Source + +Get the _originator_ (address) of the current transaction. That is, if +a chain of transactions led to the current execution get the address +that began the chain. Not to be confused with `Tezos.sender`, which +gives the address of the contract or user which directly caused the +current transaction. + +> ⚠️ There are a few caveats you should keep in mind before using +> `Tezos.source` over `Tezos.sender`: > -> 1. SOURCE will never be a contract, so if you want to allow contracts (multisigs etc) to operate your contract, you need to use SENDER -> 2. https://vessenes.com/tx-origin-and-ethereum-oh-my/ -- in general it is somewhat unsafe to assume that SOURCE understands everything that's going to happen in a transaction. If SOURCE transfers to a malicious (or sufficiently attackable) contract, that contract might potentially transfer to yours, without SOURCE's consent. So if you are using SOURCE for authentication, you risk being confused. A good historical example of this is bakers paying out delegation rewards. Naive bakers did (and probably still do) just use tezos-client to transfer to whatever KT1 delegates they had, even if those KT1 were malicious scripts. +> 1. `Tezos.source` will never be a contract, so if you want to allow +> contracts (multisigs etc) to operate your contract, you need to +> use `Tezos.sender` +> 2. https://vessenes.com/tx-origin-and-ethereum-oh-my/ -- in general +> it is somewhat unsafe to assume that `Tezos.source` understands +> everything that is going to happen in a transaction. If +> `Tezos.source` transfers to a malicious (or sufficiently +> attackable) contract, that contract might potentially transfer to +> yours, without `Tezos.source`'s consent. So if you are using +> `Tezos.source` for authentication, you risk being confused. A +> good historical example of this is bakers paying out delegation +> rewards. Naive bakers did (and probably still do) just use +> tezos-client to transfer to whatever KT1 delegates they had, even +> if those KT1 were malicious scripts. - - + + + ```pascaligo -function main (const p: unit) : address is source +function main (const p: unit) : address is Tezos.source ``` - +> Note that `source` is *deprecated*. + + + + ```cameligo -let main (p: unit) : address = Current.source +let main (p : unit) : address = Tezos.source ``` - +> Note that `Current.source` is *deprecated*. + + + + ```reasonligo -let main = (p: unit) : address => Current.source; +let main = (p : unit) : address => Tezos.source; ``` - +> Note that `Current.source` is *deprecated*. -## Current.failwith(error_message: string) : a' + + + +## Failwith Cause the contract to fail with an error message. -> ⚠ Using this currently requires a type annotation on the failwith to unify it -> with the type of whatever other code branch it's on. +> ⚠ Using this currently requires in general a type annotation on the +> `failwith` call. - - + + + ```pascaligo -function main (const p : int; const s : unit) : list(operation) * unit is +function main (const p : int; const s : unit) : list (operation) * unit is block { - if p > 10 then failwith("fail") else skip; + if p > 10 then failwith ("Failure.") else skip } - with ((nil : list(operation)), s) + with ((nil : list (operation)), s) ``` - + + + ```cameligo -let main (p,s: unit * unit) = - if true then failwith "This contract always fails" else () +let main (p,s : int * unit) = if p > 10 then failwith "Failure." ``` - + + + ```reasonligo -let main = ((p,s): (unit, unit)) => - if (true) { - failwith("This contract always fails"); - } else { - (); - }; +let main = ((p,s) : (int, unit)) => + if (p > 10) { failwith ("Failure."); }; ``` - + + diff --git a/gitlab-pages/docs/reference/list.md b/gitlab-pages/docs/reference/list.md index 63efcf66a..cbded72e6 100644 --- a/gitlab-pages/docs/reference/list.md +++ b/gitlab-pages/docs/reference/list.md @@ -1,140 +1,239 @@ --- id: list-reference -title: List — Ordered collection of a type +title: Lists — Linear Collections --- -## List.size(lst: a' list) : nat +import Syntax from '@theme/Syntax'; -Get the number of elements in a list. +Lists are linear collections of elements of the same type. Linear +means that, in order to reach an element in a list, we must visit all +the elements before (sequential access). Elements can be repeated, as +only their order in the collection matters. The first element is +called the *head*, and the sub-list after the head is called the +*tail*. For those familiar with algorithmic data structure, you can +think of a list a *stack*, where the top is written on the left. - +# Defining Lists - -```pascaligo -function size_ (const m : list(int)) : nat is size(m) + + + +```pascaligo group=lists +const empty_list : list (int) = nil // Or list [] +const my_list : list (int) = list [1; 2; 2] // The head is 1 ``` - -```cameligo -let size_ (s: int list) : nat = List.size s + + + +```cameligo group=lists +let empty_list : int list = [] +let my_list : int list = [1; 2; 2] // The head is 1 ``` - -```reasonligo -let size_ = (s: list(int)): nat => List.size(s); + + + +```reasonligo group=lists +let empty_list : list (int) = []; +let my_list : list (int) = [1, 2, 2]; // The head is 1 ``` - + -## List.length(lst: a' list) : nat -Alias of `List.size`. +# Adding to Lists -## List.map(map_function: a' -> b', lst: a' list) : 'b list +Lists can be augmented by adding an element before the head (or, in +terms of stack, by *pushing an element on top*). -Apply an operation defined by `map_function` to each element of a list and return -a list of the modified elements. - - -```pascaligo group=b -function increment(const i: int): int is i + 1; -// Creates a new list with elements incremented by 1 -const incremented_list: list(int) = list_map(increment, list 1; 2; 3; end ); + + + +```pascaligo group=lists +const larger_list : list (int) = 5 # my_list // [5;1;2;2] ``` - + + -```cameligo group=b -let increment (i: int) : int = i + 1 -(* Creates a new list with elements incremented by 1 *) -let incremented_list: int list = List.map increment [1; 2; 3] +```cameligo group=lists +let larger_list : int list = 5 :: my_list // [5;1;2;2] ``` + + - - -```reasonligo group=b -let increment = (i: int): int => i + 1; -(* Creates a new list with elements incremented by 1 *) -let incremented_list: list(int) = List.map(increment, [1, 2, 3]); +```reasonligo group=lists +let larger_list : list (int) = [5, ...my_list]; // [5,1,2,2] ``` - + -## List.iter(iter_function: a' -> unit, lst: a' list) : unit -Apply a side effecting function `iter_function` to each element of a list with no -return value. This is useful for asserting that each element of a list satisfies -a particular property. - +# Functional Iteration over Lists - -```pascaligo -function iter_op (const s : list(int)) : int is - begin - var r : int := 0 ; - function aggregate (const i : int) : unit is - begin - r := r + i ; - end with unit ; - list_iter(aggregate, s) ; - end with r +A *functional iterator* is a function that traverses a data structure +and calls in turn a given function over the elements of that structure +to compute some value. Another approach is possible in PascaLIGO: +*loops* (see the relevant section). + +There are three kinds of functional iterations over LIGO lists: the +*iterated operation*, the *map operation* (not to be confused with the +*map data structure*) and the *fold operation*. + +## Iterated Operation over Lists + +The first, the *iterated operation*, is an iteration over the list +with a unit return value. It is useful to enforce certain invariants +on the element of a list, or fail. + + + + + +```pascaligo group=lists +function iter_op (const l : list (int)) : unit is + block { + function iterated (const i : int) : unit is + if i > 3 then Unit else (failwith ("Below range.") : unit) + } with List.iter (iterated, l) ``` - -```cameligo -let iter_op (s : int list) : unit = - let do_nothing = fun (_: int) -> unit - in List.iter do_nothing s +> Note that `list_iter` is *deprecated*. + + + + +```cameligo group=lists +let iter_op (l : int list) : unit = + let predicate = fun (i : int) -> assert (i > 3) + in List.iter predicate l ``` - -```reasonligo -let iter_op = (s: list(int)): unit => { - let do_nothing = (z: int) => unit; - List.iter(do_nothing, s); + + + +```reasonligo group=lists +let iter_op = (l : list (int)) : unit => { + let predicate = (i : int) => assert (i > 3); + List.iter (predicate, l); }; ``` - + -## List.fold(fold_function: (a' * a') -> a', lst: a' list, acc: a') : 'a -Combine the elements of a list into one value using the operation defined by -`fold_function'. For example, you could define summation by folding a list of -integers. Starting with some initial accumulator value `acc`, the fold: +## Mapped Operation over Lists -1. Consumes an element of the list. -2. Passes the accumulator value to `fold_function` along with the element to produce -a new accumulated value. -3. The new accumulated value replaces the previous one. -4. IF there are still elements in the list go back to 1, ELSE return the accumulator +We may want to change all the elements of a given list by applying to +them a function. This is called a *map operation*, not to be confused +with the map data structure. -Summation would be defined then by using a `fold_function` that takes two integers and -adds them together. Each step of the fold would consume an element from the list -and add it to the total until you've summed over the list. - - -```pascaligo group=b -function sum(const result: int; const i: int): int is result + i; -const sum_of_a_list: int = list_fold(sum, list 1; 2; 3; end, 0); + + + +```pascaligo group=lists +function increment (const i : int): int is i + 1 + +// Creates a new list with all elements incremented by 1 +const plus_one : list (int) = List.map (increment, larger_list) ``` - +> Note that `list_map` is *deprecated*. -```cameligo group=b -let sum (result, i: int * int) : int = result + i -let sum_of_a_list: int = List.fold sum [1; 2; 3] 0 + + + +```cameligo group=lists +let increment (i : int) : int = i + 1 + +// Creates a new list with all elements incremented by 1 +let plus_one : int list = List.map increment larger_list ``` - + + -```reasonligo group=b +```reasonligo group=lists +let increment = (i : int) : int => i + 1; + +// Creates a new list with all elements incremented by 1 +let plus_one : list (int) = List.map (increment, larger_list); +``` + + + + + +## Folded Operation over Lists + +A *folded operation* is the most general of iterations. The folded +function takes two arguments: an *accumulator* and the structure +*element* at hand, with which it then produces a new accumulator. This +enables having a partial result that becomes complete when the +traversal of the data structure is over. + + + + + +```pascaligo group=lists +function sum (const acc : int; const i : int): int is acc + i +const sum_of_elements : int = List.fold (sum, my_list, 0) +``` + +> Note that `list_fold` is *deprecated*. + + + + +```cameligo group=lists +let sum (acc, i: int * int) : int = acc + i +let sum_of_elements : int = List.fold sum my_list 0 +``` + + + + +```reasonligo group=lists let sum = ((result, i): (int, int)): int => result + i; -let sum_of_a_list: int = List.fold(sum, [1, 2, 3], 0); +let sum_of_elements : int = List.fold (sum, my_list, 0); ``` - + + + +# List Length + +Get the number of elements in a list. + + + + + +```pascaligo +function size_of (const l : list (int)) : nat is List.length (l) +``` + +> Note that `size` is *deprecated*. + + + + +```cameligo +let size_of (l : int list) : nat = List.length l +``` + + + + +```reasonligo +let size_of = (l : list (int)) : nat => List.length (l); +``` + + + diff --git a/gitlab-pages/docs/reference/map.md b/gitlab-pages/docs/reference/map.md index 70710e8b7..b359380eb 100644 --- a/gitlab-pages/docs/reference/map.md +++ b/gitlab-pages/docs/reference/map.md @@ -1,392 +1,419 @@ --- id: map-reference -title: Map — Hashmaps that it makes sense to iterate over +title: Maps --- -## Defining A Map Type +import Syntax from '@theme/Syntax'; - - -```pascaligo +*Maps* are a data structure which associate values of the same type to +values of the same type. The former are called *key* and the latter +*values*. Together they make up a *binding*. An additional requirement +is that the type of the keys must be *comparable*, in the Michelson +sense. + +# Declaring a Map + + + + + +```pascaligo group=maps type move is int * int -type moveset is map(address, move) +type register is map (address, move) ``` - -```cameligo + + + +```cameligo group=maps type move = int * int -type moveset = (address, move) map +type register = (address, move) map ``` - -```reasonligo + + + +```reasonligo group=maps type move = (int, int); -type moveset = map(address, move); +type register = map (address, move); ``` - + -## Creating A Map - - +# Creating an Empty Map -```pascaligo -const moves: moveset = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2); - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3); -end + + + + +```pascaligo group=maps +const empty : register = map [] ``` - + + -```cameligo -let moves: moveset = Map.literal - [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ; - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ; - ] +```cameligo group=maps +let empty : register = Map.empty ``` - + + -```reasonligo -let moves : moveset = - Map.literal([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)), - ]); -``` - - -## Map.find_opt(k: a', m: (a',b') map) : b' option - -Retrieve the value associated with a particular key. This version returns an option -which can either shift logic in response to a missing value or throw an error. - - - -```pascaligo -const my_balance : option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]; +```reasonligo group=maps +let empty : register = Map.empty ``` - + -```cameligo -let my_balance : move option = Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves + +# Creating a Non-empty Map + + + + +```pascaligo group=maps +const moves : register = + map [ + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2); + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (0,3)] ``` - + + -```reasonligo -let my_balance : option(move) = - Map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); -``` - - -## Map.find(k: a', m: (a', b') map) : b' - -Forcefully retrieve the value associated with a particular key. If that value -doesn't exist, this function throws an error. - - - -```pascaligo -const my_balance : move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves); +```cameligo group=maps +let moves : register = + Map.literal [ + (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), (1,2)); + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (0,3))] ``` - + + -```cameligo -let my_balance : move = Map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves +```reasonligo group=maps +let moves : register = + Map.literal ([ + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, (1,2)), + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address, (0,3))]); ``` - + -```reasonligo -let my_balance : move = - Map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves); + +# Accessing Map Bindings + + + + +```pascaligo group=maps +const my_balance : option (move) = + moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address)] ``` - + + -## Map.update(k: a', v: b', m: (a', b') map) : (a', b') map +```cameligo group=maps +let my_balance : move option = + Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) moves +``` -Change the value associated with a particular key, if that value doesn't already -exist add it. + + - +```reasonligo group=maps +let my_balance : option (move) = + Map.find_opt (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), moves); +``` - + -The values of a PascaLIGO map can be updated using the ordinary assignment syntax: -```pascaligo +Notice how the value we read is an optional value: this is to force +the reader to account for a missing key in the map. This requires +*pattern matching*. -function set_ (var m: moveset) : moveset is + + + + +```pascaligo group=maps +function force_access (const key : address; const moves : register) : move is + case moves[key] of + Some (move) -> move + | None -> (failwith ("No move.") : move) + end +``` + + + + +```cameligo group=maps +let force_access (key, moves : address * register) : move = + match Map.find_opt key moves with + Some move -> move + | None -> (failwith "No move." : move) +``` + + + + +```reasonligo group=maps +let force_access = ((key, moves) : (address, register)) : move => { + switch (Map.find_opt (key, moves)) { + | Some (move) => move + | None => failwith ("No move.") : move + } +}; +``` + + + + +# Updating a Map + +Given a map, we may want to add a new binding, remove one, or modify +one by changing the value associated to an already existing key. All +those operations are called *updates*. + + + + +```pascaligo group=maps +function assign (var m : register) : register is block { - m[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9); + m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9) } with m ``` - +If multiple bindings need to be updated, PascaLIGO offers a *patch +instruction* for maps, similar to that for records. -We can update a map in CameLIGO using the `Map.update` built-in: - -```cameligo - -let updated_map: moveset = Map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) (Some (4,9)) moves -``` - - - -We can update a map in ReasonLIGO using the `Map.update` built-in: - -```reasonligo - -let updated_map: moveset = Map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves); -``` - - - -## Map.add(k: a', v: b', m: (a', b') map) : (a', b') map - - - - -```pascaligo -function set_ (var n : int ; var m : map(int, int)) : map(int, int) is block { - m[23] := n ; -} with m -``` - - -```cameligo -let add (n,m: int * (int, int) map) : foobar = Map.add 23 n m -``` - - -```reasonligo -let add = (n: int, m: map(int, int)) : foobar => Map.add(23, n, m); -``` - - - -## Map.remove(k: a', m: (a', b') map) : (a', b') map - -Remove a key and its associated value from the map. - - - - -```pascaligo -function rm (var m : map(int, int)) : map(int, int) is block { - remove 42 from map m -} with m -``` - - -```cameligo -let rm (m: (int, int) map) : (int, int) map = Map.remove 42 m -``` - - -```reasonligo -let rm = (m: map(int, int)): map(int, int) => Map.remove(42, m); -``` - - - -## Map.iter(iterator_function: (a', b') -> unit, m: (a', b') map) : unit - -Run a function returning unit over the contents of a map's key-value pairs. -For example an assertion. - - - -```pascaligo -function iter_op (const m : moveset) : unit is +```pascaligo group=maps +function assignments (var m : register) : register is block { - function aggregate (const i : address ; const j : move) : unit is block - { if j.1 > 1 then skip else failwith("fail") } with unit - } with map_iter(aggregate, m); + patch m with map [ + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) -> (4,9); + ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> (1,2) + ] + } with m ``` - -```cameligo -let iter_op (m : moveset) : unit = - let assert_eq = fun (i,j: address * move) -> assert (j.0 > 1) - in Map.iter assert_eq m + + + +```cameligo group=maps +let assign (m : register) : register = + Map.update + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (Some (4,9)) m +``` +Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had +use `None` instead, that would have meant that the binding is removed. + +As a particular case, we can only add a key and its associated value. + +```cameligo group=maps +let add (m : register) : register = + Map.add + ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address) (4,9) m ``` - -```reasonligo -let iter_op = (m: moveset): unit => { - let assert_eq = ((i,j): (address, move)) => assert (j[0] > 1); - Map.iter(assert_eq, m); + + + +```reasonligo group=maps +let assign = (m : register) : register => + Map.update + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), Some ((4,9)), m); +``` + +Notice the optional value `Some (4,9)` instead of `(4,9)`. If we had +use `None` instead, that would have meant that the binding is removed. + +As a particular case, we can only add a key and its associated value. + +```reasonligo group=maps +let add = (m : register) : register => + Map.add + (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" : address), (4,9), m); +``` + + + + +To remove a binding from a map, we need its key. + + + + + +```pascaligo group=maps +function delete (const key : address; var moves : register) : register is + block { + remove key from map moves + } with moves +``` + + + + +```cameligo group=maps +let delete (key, moves : address * register) : register = + Map.remove key moves +``` + + + + +```reasonligo group=maps +let delete = ((key, moves) : (address, register)) : register => + Map.remove (key, moves); +``` + + + + + +# Functional Iteration over Maps + +A *functional iterator* is a function that traverses a data structure +and calls in turn a given function over the elements of that structure +to compute some value. Another approach is possible in PascaLIGO: +*loops* (see the relevant section). + +There are three kinds of functional iterations over LIGO maps: the +*iterated operation*, the *map operation* (not to be confused with the +*map data structure*) and the *fold operation*. + +## Iterated Operation over Maps + +The first, the *iterated operation*, is an iteration over the map with +no return value: its only use is to produce side-effects. This can be +useful if for example you would like to check that each value inside +of a map is within a certain range, and fail with an error otherwise. + + + + + +```pascaligo group=maps +function iter_op (const m : register) : unit is + block { + function iterated (const i : address; const j : move) : unit is + if j.1 > 3 then Unit else (failwith ("Below range.") : unit) + } with Map.iter (iterated, m) +``` + +> Note that `map_iter` is *deprecated*. + + + + +```cameligo group=maps +let iter_op (m : register) : unit = + let predicate = fun (i,j : address * move) -> assert (j.0 > 3) + in Map.iter predicate m +``` + + + + +```reasonligo group=maps +let iter_op = (m : register) : unit => { + let predicate = ((i,j) : (address, move)) => assert (j[0] > 3); + Map.iter (predicate, m); }; ``` - + + -## Map.map(mapping_function: (a', b') -> b', m: (a', b') map) : (a', b') map +## Map Operations over Maps -Update the values associated with every key in the map according to some update -rule `mapping_function`. +We may want to change all the bindings of a map by applying to them a +function. This is called a *map operation*, not to be confused with +the map data structure. The predefined functional iterator +implementing the map operation over maps is called `Map.map`. - - -```pascaligo -function map_op (const m : moveset) : moveset is + + + + +```pascaligo group=maps +function map_op (const m : register) : register is block { - function increment (const i : address ; const j : move) : move is (j.0, j.1 + 1); - } with map_map (increment, m); + function increment (const i : address; const j : move) : move is + (j.0, j.1 + 1) + } with Map.map (increment, m) ``` - -```cameligo -let map_op (m : moveset) : moveset = - let increment = fun (i,j: address * move) -> (j.0, j.1 + 1) +> Note that `map_map` is *deprecated*. + + + + +```cameligo group=maps +let map_op (m : register) : register = + let increment = fun (i,j : address * move) -> j.0, j.1 + 1 in Map.map increment m ``` - -```reasonligo -let map_op = (m: moveset): moveset => { + + + +```reasonligo group=maps +let map_op = (m : register) : register => { let increment = ((i,j): (address, move)) => (j[0], j[1] + 1); - Map.map(increment, m); + Map.map (increment, m); }; ``` - -## Map.fold(folding_function: (b', (a', b')) -> b', m: (a', b') map, initial: b') : b' + -Combine every value in the map together according to a fold rule `folding_function`. - - -```pascaligo -function fold_op (const m : moveset) : int is +## Folded Operations over Maps + +A *folded operation* is the most general of iterations. The folded +function takes two arguments: an *accumulator* and the structure +*element* at hand, with which it then produces a new accumulator. This +enables having a partial result that becomes complete when the +traversal of the data structure is over. + + + + + +```pascaligo group=maps +function fold_op (const m : register) : int is block { - function aggregate (const j : int; const cur : address * (int * int)) : int is j + cur.1.1 - } with map_fold(aggregate, m, 5) + function folded (const i : int; const j : address * move) : int is + i + j.1.1 + } with Map.fold (folded, m, 5) ``` - -```cameligo -let fold_op (m : moveset) : moveset = - let aggregate = fun (i,j: int * (address * (int * int))) -> i + j.1.1 - in Map.fold aggregate m 5 +> Note that `map_fold` is *deprecated*. + + + + +```cameligo group=maps +let fold_op (m : register) : int = + let folded = fun (i,j : int * (address * move)) -> i + j.1.1 + in Map.fold folded m 5 ``` - -```reasonligo -let fold_op = (m: moveset): moveset => { - let aggregate = ((i,j): (int, (address, (int,int)))) => i + j[1][1]; - Map.fold(aggregate, m, 5); + + + +```reasonligo group=maps +let fold_op = (m : register) : int => { + let folded = ((i,j): (int, (address, move))) => i + j[1][1]; + Map.fold (folded, m, 5); }; - ``` - + - -## Map.mem(k: a', m: (a', b') map) : bool - -Test whether a particular key `k` exists in a given map `m`. - - - - -```pascaligo -function mem (const k: int; const m: map(int, int)) : bool is map_mem(k, m) -``` - -```cameligo -let mem (k,m: int * (int, int) map) : bool = Map.mem k m -``` - - -```reasonligo -let mem = ((k,m): (int, map(int,int))): bool => Map.mem(k, m); -``` - - - -## Map.empty() : (a', b') map - -Create an empty map. - - - - -```pascaligo -const empty_map : map(int, int) = map end -``` - -```cameligo -let empty_map : (int, int) map = Map.empty -``` - - -```reasonligo -let empty_map: map(int, int) = Map.empty; -``` - - - -## Map.literal(key_value_pair_list: (a', b') list) : (a', b') map - -Constructs a map from a list of key-value pair tuples. - - - - - -```pascaligo -const moves: moveset = map - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2); - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3); -end -``` - - - -```cameligo -let moves: moveset = Map.literal - [ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ; - (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ; - ] -``` - - - -```reasonligo -let moves : moveset = - Map.literal([ - ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)), - ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)), - ]); -``` - - -## Map.size(m: (a', b') map) : nat - -Get the size of a given map `m`. - - - - -```pascaligo -function size_ (const m : map(int, int)) : nat is - block {skip} with (size(m)) -``` - -```cameligo -let size_ (m: (int, int) map) : nat = Map.size m -``` - -```reasonligo -let size_ = (m: map(int, int)): nat => Map.size(m); -``` - - diff --git a/gitlab-pages/docs/reference/set.md b/gitlab-pages/docs/reference/set.md index 08c7be4f3..eca62a741 100644 --- a/gitlab-pages/docs/reference/set.md +++ b/gitlab-pages/docs/reference/set.md @@ -1,201 +1,272 @@ --- id: set-reference -title: Set — Unordered unique collection of a type +title: Sets — Unordered unique collection of a type --- -## Defining a set +import Syntax from '@theme/Syntax'; - - -```pascaligo group=a -type int_set is set (int); -const my_set : int_set = set 1; 2; 3 end +Sets are unordered collections of values of the same type, like lists +are ordered collections. Like the mathematical sets and lists, sets +can be empty and, if not, elements of sets in LIGO are *unique*, +whereas they can be repeated in a *list*. + +# Empty Sets + + + + +```pascaligo group=sets +const my_set : set (int) = set [] ``` - -```cameligo group=a -type int_set = int set -let my_set : int_set = - Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set))) + + + +```cameligo group=sets +let my_set : int set = Set.empty ``` - -```reasonligo group=a -type int_set = set (int); -let my_set : int_set = - Set.add (3, Set.add (2, Set.add (1, Set.empty: set (int)))); + + + +```reasonligo group=sets +let my_set : set (int) = Set.empty; ``` - + -## Set.mem(is_member: a', s: a' set) : bool +# Non-empty Sets -Check if a set `s` contains the element `is_member`. - - -```pascaligo group=a -const contains_three : bool = my_set contains 3 -// or alternatively -const contains_three_fn: bool = set_mem (3, my_set); + + +```pascaligo group=sets +const my_set : set (int) = set [3; 2; 2; 1] ``` - -```cameligo group=a -let contains_three: bool = Set.mem 3 my_set -``` - -```reasonligo group=a -let contains_three: bool = Set.mem(3, my_set); + + + +```cameligo group=sets +let my_set : int set = + Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set)))) ``` - + + - -## Set.empty() : a' set - -Create a new empty set. Needs to be annotated with the set type. - - - -```pascaligo group=a -const my_set: int_set = set end -const my_set_2: int_set = set_empty -``` - -```cameligo group=a -let my_set: int_set = (Set.empty: int set) -``` - -```reasonligo group=a -let my_set: int_set = (Set.empty: set (int)); -``` - - -## Set.literal(element_list_literal: 'a list) : 'a set - -Create a set from the elements of a list. Note that **you must pass a list literal** -to this function, a variable will not work. - - - - -```pascaligo -const s_fb : set(string) = set [ - "foo" ; - "bar" ; -] +```reasonligo group=sets +let my_set : set (int) = + Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int))))); ``` - -```cameligo -let literal_op (p: unit) : string set = - Set.literal ["foo"; "bar"; "foobar"] + + + +# Set Membership + + + + +```pascaligo group=sets +const contains_3 : bool = my_set contains 3 ``` - -```reasonligo -let literal_op = (p: unit) : set(string) => Set.literal(["foo", "bar", "foobar"]); + + + +```cameligo group=sets +let contains_3 : bool = Set.mem 3 my_set ``` - + + -## Set.add(addition: a', s: a' set) : a' set - -Add the element `addition` to a set `s`. - - - -```pascaligo group=a -function add_op (const s : set(string)) : set(string) is - begin skip end with set_add("foobar" , s) +```reasonligo group=sets +let contains_3 : bool = Set.mem (3, my_set); ``` - -```cameligo group=a -type int_set = int set -let my_set : int_set = - Set.add 3 (Set.add 2 (Set.add 1 (Set.empty: int set))) + + + +# Cardinal of Sets + +The predefined function `Set.size` returns the number of +elements in a given set as follows. + + + + +```pascaligo group=sets +const cardinal : nat = Set.size (my_set) ``` - -```reasonligo group=a -type int_set = set (int); -let my_set : int_set = - Set.add (3, Set.add (2, Set.add (1, Set.empty: set (int)))); +> Note that `size` is *deprecated*. + + + + +```cameligo group=sets +let cardinal : nat = Set.size my_set ``` - + + -## Set.remove(removal: a', s: a' set) : a' set - -Remove the element `removal` from a set `s`. - - - -```pascaligo group=a -const smaller_set: int_set = set_remove(3, my_set); +```reasonligo group=sets +let cardinal : nat = Set.size (my_set); ``` - + -```cameligo group=a -let smaller_set: int_set = Set.remove 3 my_set + +# Updating Sets + +There are two ways to update a set, that is to add or remove from it. + + + + +In PascaLIGO, either we create a new set from the given one, or we +modify it in-place. First, let us consider the former way: +```pascaligo group=sets +const larger_set : set (int) = Set.add (4, my_set) +const smaller_set : set (int) = Set.remove (3, my_set) ``` - +> Note that `set_add` and `set_remove` are *deprecated*. -```reasonligo group=a -let smaller_set: int_set = Set.remove(3, my_set); +If we are in a block, we can use an instruction to modify the set +bound to a given variable. This is called a *patch*. It is only +possible to add elements by means of a patch, not remove any: it is +the union of two sets. + +```pascaligo group=sets +function update (var s : set (int)) : set (int) is block { + patch s with set [4; 7] +} with s + +const new_set : set (int) = update (my_set) ``` - + + - -## Set.fold(folding_function: a' -> a' -> a', s: a' set, initial: a') : a' - -Combine the elements of a set into a single value using a folding function. - - - -```pascaligo group=a -function sum(const result: int; const i: int): int is result + i; -// Outputs 6 -const sum_of_a_set: int = set_fold(sum, my_set, 0); +```cameligo group=sets +let larger_set : int set = Set.add 4 my_set +let smaller_set : int set = Set.remove 3 my_set ``` - -```cameligo group=a -let sum (result, i: int * int) : int = result + i -let sum_of_a_set: int = Set.fold sum my_set 0 + + + +```reasonligo group=sets +let larger_set : set (int) = Set.add (4, my_set); +let smaller_set : set (int) = Set.remove (3, my_set); ``` - -```reasonligo group=a -let sum = (result_i: (int, int)): int => result_i[0] + result_i[1]; -let sum_of_a_set: int = Set.fold(sum, my_set, 0); -``` - + -## Set.size(s: a' set) : nat -Get the number of elements in a set. +# Functional Iteration over Sets - - -```pascaligo group=a -const set_size: nat = size (my_set) +A *functional iterator* is a function that traverses a data structure +and calls in turn a given function over the elements of that structure +to compute some value. Another approach is possible in PascaLIGO: +*loops* (see the relevant section). + +There are three kinds of functional iterations over LIGO maps: the +*iterated operation*, the *mapped operation* (not to be confused with +the *map data structure*) and the *folded operation*. + +## Iterated Operation + +The first, the *iterated operation*, is an iteration over the map with +no return value: its only use is to produce side-effects. This can be +useful if for example you would like to check that each value inside +of a map is within a certain range, and fail with an error otherwise. + + + + +```pascaligo group=sets +function iter_op (const s : set (int)) : unit is + block { + function iterated (const i : int) : unit is + if i > 2 then Unit else (failwith ("Below range.") : unit) + } with Set.iter (iterated, s) ``` - -```cameligo group=a -let set_size: nat = Set.size my_set +> Note that `set_iter` is *deprecated*. + + + + +```cameligo group=sets +let iter_op (s : int set) : unit = + let predicate = fun (i : int) -> assert (i > 3) + in Set.iter predicate s ``` - -```reasonligo group=a -let set_size: nat = Set.size (my_set); + + + +```reasonligo group=sets +let iter_op = (s : set (int)) : unit => { + let predicate = (i : int) => assert (i > 3); + Set.iter (predicate, s); +}; ``` - + + + +## Folded Operation + +A *folded operation* is the most general of iterations. The folded +function takes two arguments: an *accumulator* and the structure +*element* at hand, with which it then produces a new accumulator. This +enables having a partial result that becomes complete when the +traversal of the data structure is over. + + + + + +```pascaligo group=sets +function sum (const acc : int; const i : int): int is acc + i +const sum_of_elements : int = Set.fold (sum, my_set, 0) +``` + +> Note that `set_fold` is *deprecated*. + +It is possible to use a *loop* over a set as well. + +```pascaligo group=sets +function loop (const s : set (int)) : int is block { + var sum : int := 0; + for element in set s block { + sum := sum + element + } +} with sum +``` + + + + +```cameligo group=sets +let sum (acc, i : int * int) : int = acc + i +let sum_of_elements : int = Set.fold sum my_set 0 +``` + + + + +```reasonligo group=sets +let sum = ((acc, i) : (int, int)) : int => acc + i; +let sum_of_elements : int = Set.fold (sum, my_set, 0); +``` + + + diff --git a/gitlab-pages/docs/reference/string.md b/gitlab-pages/docs/reference/string.md index b29cdec54..c75e850a4 100644 --- a/gitlab-pages/docs/reference/string.md +++ b/gitlab-pages/docs/reference/string.md @@ -3,29 +3,37 @@ id: string-reference title: String — Manipulate string data --- +import Syntax from '@theme/Syntax'; + ## String.size(s: string) : nat Get the size of a string. [Michelson only supports ASCII strings](http://tezos.gitlab.io/whitedoc/michelson.html#constants) so for now you can assume that each character takes one byte of storage. - - + + + ```pascaligo function string_size (const s: string) : nat is size(s) ``` - + + + ```cameligo let size_op (s: string) : nat = String.size s ``` - + + + ```reasonligo let size_op = (s: string): nat => String.size(s); ``` - + + ## String.length(s: string) : nat @@ -36,20 +44,29 @@ Alias for `String.size`. Get the substring of `s` between `pos1` inclusive and `pos2` inclusive. For example the string "tata" given to the function below would return "at". - - + + + ```pascaligo function slice_op (const s : string) : string is string_slice(1n , 2n , s) ``` - + + + + ```cameligo let slice_op (s: string) : string = String.slice 1n 2n s ``` - + + + + ```reasonligo let slice_op = (s: string): string => String.slice(1n, 2n, s); ``` - + + + ## String.sub(pos1: nat, pos2: nat, s: string) : string @@ -59,21 +76,27 @@ Alias for `String.slice`. Concatenate two strings and return the result. - - + + + ```pascaligo function concat_op (const s : string) : string is s ^ "toto" ``` - + + + ```cameligo let concat_syntax (s: string) = s ^ "test_literal" ``` - + + + ```reasonligo let concat_syntax = (s: string) => s ++ "test_literal"; ``` - + + diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo index 5623eaf3f..9cf169397 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.ligo @@ -1,37 +1,46 @@ -type taco_supply is record +type taco_supply is + record [ current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); + max_price : tez + ] -const ownerAddress: address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV"; -const donationAddress: address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"; +type taco_shop_storage is map (nat, taco_supply) -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type return is list (operation) * taco_shop_storage + +const ownerAddress : address = "tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" +const donationAddress : address = "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" + +function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - fail("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind taco_shop_storage[taco_kind_index] := taco_kind; - const receiver: contract(unit) = get_contract(ownerAddress); - const donationReceiver: contract(unit) = get_contract(donationAddress); + const receiver : contract (unit) = get_contract (ownerAddress); + const donationReceiver : contract (unit) = get_contract (donationAddress); - const donationAmount: tez = amount / 10n; + const donationAmount : tez = amount / 10n; - const operations : list(operation) = list - transaction(unit, amount - donationAmount, receiver); - transaction(unit, donationAmount, donationReceiver); - end; - - end with (operations, taco_shop_storage) \ No newline at end of file + const operations : list (operation) = list [ + transaction (unit, amount - donationAmount, receiver); + transaction (unit, donationAmount, donationReceiver); + ] + } with (operations, taco_shop_storage) diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md index ae9a1b8e3..3d9330894 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-payout.md @@ -3,179 +3,229 @@ id: tezos-taco-shop-payout title: Paying out profits from the Taco Shop --- -In the [previous tutorial](tutorials/get-started/tezos-taco-shop-smart-contract.md) we've learned how to setup & interact with the LIGO CLI. Followed by implementation of a simple Taco Shop smart contract for our entepreneur Pedro. In this tutorial we'll make sure Pedro has access to tokens that people have spent at his shop when buying tacos. +In the +[previous tutorial](tutorials/get-started/tezos-taco-shop-smart-contract.md) +we have learnt how to setup & interact with the LIGO CLI. Followed an +implementation of a simple Taco Shop smart contract for our +entrepreneur Pedro. +In this tutorial we will make sure Pedro has access to tokens that +people have spent at his shop when buying tacos.
-
+
Icons made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY
-## Analyzing the current contract +## Analyzing the Current Contract ### **`taco-shop.ligo`** -```pascaligo group=a -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : - (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +```pascaligo group=a +type taco_supply is record [ + current_stock : nat; + max_price : tez +] + +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - failwith("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) ``` -### Purchase price formula -Pedro's Taco Shop contract currently enables customers to buy tacos, at a computed price based on a simple formula. +### Purchase Price Formula + +Pedro's Taco Shop contract currently enables customers to buy tacos, +at a price based on a simple formula. ```pascaligo skip -const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock ``` -### Replacing *spendable* smart contracts -However, due to the [recent protocol upgrade](http://tezos.gitlab.io/mainnet/protocols/004_Pt24m4xi.html) of the Tezos mainnet, Pedro can't access the tokens stored in his Shop's contract directly. This was previously possible via `spendable` smart contracts, which are no longer available in the new protocol. We will have to implement a solution to access tokens from the contract programatically. +### Replacing *spendable* Smart Contracts + +However, due to the +[recent protocol upgrade](http://tezos.gitlab.io/mainnet/protocols/004_Pt24m4xi.html) +of the Tezos `mainnet`, Pedro cannot access the tokens stored in his +shop's contract directly. This was previously possible via *spendable +smart contracts*, which are no longer available in the new +protocol. We will have to implement a solution to access tokens from +the contract programatically. --- -## Designing a payout scheme +## Designing a Payout Scheme -Pedro is a standalone bussines owner, and in our case, he doesn't have to split profits / earnings of the taco shop with anyone. So for the sake of simplicity, we'll payout all the earned XTZ directly to Pedro right after a succesful taco purchase. +Pedro is a standalone bussines owner, and in our case, he does not +have to split profits and earnings of the taco shop with anyone. So +for the sake of simplicity, we will payout all the earned XTZ directly +to Pedro right after a succesful purchase. -This means that after all the *purchase conditions* of our contract are met - e.g. correct amount is sent to the contract - we'll not only decrease the supply of the individual purchased *taco kind*, but we'll also transfer this amount in a *subsequent transaction* to Pedro's personal address. +This means that after all the *purchase conditions* of our contract +are met, e.g., the correct amount is sent to the contract, we will not +only decrease the supply of the individual purchased *taco kind*, but +we will also transfer this amount in a *subsequent transaction* to +Pedro's personal address. -## Forging a payout transaction +## Forging a Payout Transaction -### Defining the recipient -In order to send tokens, we will need a receiver address - which in our case will be Pedro's personal account. Additionally we'll wrap the given address as a *`contract(unit)`* - which represents either a contract with no parameters, or an implicit account. +### Defining the Recipient + +In order to send tokens, we will need a receiver address, which, in +our case, will be Pedro's personal account. Additionally we will wrap +the given address as a *`contract (unit)`*, which represents either a +contract with no parameters, or an implicit account. ```pascaligo group=ex1 const ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); -const receiver : contract(unit) = get_contract(ownerAddress); +const receiver : contract (unit) = get_contract (ownerAddress); ``` -> Would you like to learn more about addresses, contracts and operations in LIGO? Check out the [LIGO cheat sheet](api/cheat-sheet.md) +> Would you like to learn more about addresses, contracts and +> operations in LIGO? Check out the +> [LIGO cheat sheet](api/cheat-sheet.md) -### Adding the transaction to the list of output operations -Now we can transfer the `amount` received by `buy_taco` to Pedro's `ownerAddress`. We will do so by forging a `transaction(unit, amount, receiver)` within a list of operations returned at the end of our contract. +### Adding the Transaction to the List of Output Operations +Now we can transfer the amount received by `buy_taco` to Pedro's +`ownerAddress`. We will do so by forging a `transaction (unit, amount, +receiver)` within a list of operations returned at the end of our +contract. ```pascaligo group=ex1 -const payoutOperation : operation = transaction(unit, amount, receiver) ; -const operations : list(operation) = list - payoutOperation -end; +const payoutOperation : operation = transaction (unit, amount, receiver) ; +const operations : list (operation) = list [payoutOperation]; ``` --- -## Finalizing the contract +## Finalizing the Contract ### **`taco-shop.ligo`** + ```pascaligo group=b -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +type taco_supply is record [ + current_stock : nat; + max_price : tez +] -const ownerAddress: address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); +type taco_shop_storage is map (nat, taco_supply) -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type return is list (operation) * taco_shop_storage + +const ownerAddress : address = + ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address) + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - failwith("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind taco_shop_storage[taco_kind_index] := taco_kind; - const receiver : contract(unit) = get_contract(ownerAddress); - const payoutOperation : operation = transaction(unit, amount, receiver); - const operations : list(operation) = list - payoutOperation - end; - - end with (operations, taco_shop_storage) + const receiver : contract(unit) = get_contract (ownerAddress); + const payoutOperation : operation = transaction (unit, amount, receiver); + const operations : list(operation) = list [payoutOperation] + } with ((nil : list (operation)), taco_shop_storage) ``` +### Dry-run the Contract -### Dry-run the contract - -To confirm that our contract is valid, we can dry run it. As a result we see a *new operation* in the list of returned operations to be executed subsequently. +To confirm that our contract is valid, we can dry-run it. As a result, +we see a *new operation* in the list of returned operations to be +executed subsequently. ```pascaligo skip -ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end" +ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ]; +]" ``` -
+
Operation(...bytes) included in the output

-**Done! Our tokens are no longer locked in the contract, and instead they are sent to Pedro's personal account/wallet.** +**Done! Our tokens are no longer locked in the contract, and instead + they are sent to Pedro's personal account/wallet.** --- -## 👼 Bonus: donating part of the profits +## 👼 Bonus: Donating Part of the Profits -Because Pedro is a member of the (STA) Specialty Taco Association, he has decided to donate **10%** of the earnings to the STA. We'll just add a `donationAddress` to the contract, and compute a 10% donation sum from each taco purchase. +Because Pedro is a member of the Specialty Taco Association (STA), he +has decided to donate **10%** of the earnings to the STA. We will just +add a `donationAddress` to the contract, and compute a 10% donation +sum from each taco purchase. ```pascaligo group=bonus -const ownerAddress: address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); -const donationAddress: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); -``` +const ownerAddress : address = ("tz1TGu6TN5GSez2ndXXeDX6LgUDvLzPLqgYV" : address); +const donationAddress : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address); -```pascaligo group=bonus -const receiver : contract(unit) = get_contract(ownerAddress); -const donationReceiver : contract(unit) = get_contract(donationAddress); +const receiver : contract (unit) = get_contract (ownerAddress); +const donationReceiver : contract(unit) = get_contract (donationAddress); -const donationAmount: tez = amount / 10n; +const donationAmount : tez = amount / 10n; -const operations : list(operation) = list +const operations : list (operation) = list [ // Pedro will get 90% of the amount - transaction(unit, amount - donationAmount, receiver); - transaction(unit, donationAmount, donationReceiver); -end; + transaction (unit, amount - donationAmount, receiver); + transaction (unit, donationAmount, donationReceiver) +]; ``` This will result into two operations being subsequently executed on the blockchain: - Donation transfer (10%) -- Pedro's profits (90%) \ No newline at end of file +- Pedro's profits (90%) diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo index b51460bde..8871a9c87 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.ligo @@ -1,23 +1,33 @@ -type taco_supply is record +type taco_supply is + record [ current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); + max_price : tez + ] -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - fail("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) \ No newline at end of file + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) diff --git a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md index 9d2bf5ab2..986888889 100644 --- a/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md +++ b/gitlab-pages/docs/tutorials/get-started/tezos-taco-shop-smart-contract.md @@ -1,112 +1,147 @@ --- id: tezos-taco-shop-smart-contract -title: Taco shop smart contract +title: The Taco Shop Smart Contract ---
-Meet **Pedro**, our *artisan taco chef* who has decided to open a Taco shop on the Tezos blockchain, using a smart contract. He sells two different kinds of tacos, the **el clásico** and the **especial del chef**. +Meet **Pedro**, our *artisan taco chef*, who has decided to open a +Taco shop on the Tezos blockchain, using a smart contract. He sells +two different kinds of tacos: **el Clásico** and the **Especial +del Chef**. -To help Pedro open his dream taco shop, we'll implement a smart contract, that will manage supply, pricing & sales of his tacos to the consumers. +To help Pedro open his dream taco shop, we will implement a smart +contract that will manage supply, pricing & sales of his tacos to the +consumers.
-
Made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY
+
Made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY
--- ## Pricing -Pedro's tacos are a rare delicacy, so their **price goes up**, as the **stock for the day begins to deplete**. +Pedro's tacos are a rare delicacy, so their **price goes up** as the +**stock for the day begins to deplete**. -Each taco kind, has its own `max_price` that it sells for, and a finite supply for the current sales lifecycle. +Each taco kind, has its own `max_price` that it sells for, and a +finite supply for the current sales lifecycle. -> For the sake of simplicity, we won't implement replenishing of the supply after it runs out. +> For the sake of simplicity, we will not implement the replenishing +> of the supply after it has run out. -### Daily offer +### Daily Offer |**kind** |id |**available_stock**| **max_price**| |---|---|---|---| -|el clásico | `1n` | `50n` | `50000000mutez` | -|especial del chef | `2n` | `20n` | `75000000mutez` | +|Clásico | `1n` | `50n` | `50tez` | +|Especial del Chef | `2n` | `20n` | `75tez` | -### Calculating the current purchase price +### Calculating the Current Purchase Price -Current purchase price is calculated with the following equation: +The current purchase price is calculated with the following formula: ```pascaligo skip current_purchase_price = max_price / available_stock ``` -#### El clásico +#### El Clásico |**available_stock**|**max_price**|**current_purchase_price**| |---|---|---| -| `50n` | `50000000mutez` | `1tz`| -| `20n` | `50000000mutez` | `2.5tz` | -| `5n` | `50000000mutez` | `10tz` | +| `50n` | `50tez` | `1tez`| +| `20n` | `50tez` | `2.5tez` | +| `5n` | `50tez` | `10tez` | #### Especial del chef |**available_stock**|**max_price**|**current_purchase_price**| |---|---|---| -| `20n` | `75000000mutez` | `3.75tz` | -| `10n` | `75000000mutez` | `7.5tz`| -| `5n` | `75000000mutez` | `15tz` | +| `20n` | `75tez` | `3.75tez` | +| `10n` | `75tez` | `7.5tez`| +| `5n` | `75tez` | `15tez` | --- ## Installing LIGO -In this tutorial, we'll use LIGO's dockerized version for the sake of simplicity. You can find the installation instructions [here](intro/installation.md#dockerized-installation-recommended). +In this tutorial, we will use LIGO's dockerized version, for the sake +of simplicity. You can find the installation instructions +[here](intro/installation.md#dockerized-installation-recommended). -The best way to install the dockerized LIGO is as a **global executable** through the installation script, as shown in the screenshot below: +The best way to install the dockerized LIGO is as a **global +executable** through the installation script, as shown in the +screenshot below: -
Installing the next version of LIGO's CLI
+
Installing the next version of LIGO's CLI
-## Implementing our first entry point +## Implementing our First `main` Function -> From now on we'll get a bit more technical. If you run into something we have not covered yet - please try checking out the [LIGO cheat sheet](api/cheat-sheet.md) for some extra tips & tricks. +> From now on we will get a bit more technical. If you run into +> something we have not covered yet - please try checking out the +> [LIGO cheat sheet](api/cheat-sheet.md) for some extra tips & tricks. -To begin implementing our smart contract, we need an entry point. We'll call it `main` and it'll specify our contract's storage (`int`) and input parameter (`int`). Of course this is not the final storage/parameter of our contract, but it is something to get us started and test our LIGO installation as well. +To begin implementing our smart contract, we need a *main function*, +that is the first function being executed. We will call it `main` and +it will specify our contract's storage (`int`) and input parameter +(`int`). Of course this is not the final storage/parameter of our +contract, but it is something to get us started and test our LIGO +installation as well. ### `taco-shop.ligo` + ```pascaligo group=a -function main (const parameter: int; const contractStorage: int) : (list(operation) * int) is - block {skip} with ((nil : list(operation)), contractStorage + parameter) +function main (const parameter : int; const contractStorage : int) : +list (operation) * int is + ((nil : list (operation)), contractStorage + parameter) ``` -Let's break down the contract above to make sure we understand each bit of the LIGO syntax: +Let us break down the contract above to make sure we understand each +bit of the LIGO syntax: -- **`function main`** - definition of a function that serves as an entry point -- **`(const parameter : int; const contractStorage : int)`** - parameters passed to the function - - **`const parameter : int`** - parameter provided by a transaction that invokes our contract - - **`const contractStorage : int`** - definition of our storage (`int`) -- **`(list(operation) * int)`** - return type of our function, in our case a touple with a list of operations, and an int -- **`block {skip}`** - our function has no body, so we instruct LIGO to `skip` it -- **`with ((nil : list(operation)), contractStorage + parameter)`** - essentially a return statement - - **`(nil : list(operation))`** - a `nil` value annotated as a list of operations, because that's required by our return type specified above - - **`contractStorage + parameter`** - a new storage value for our contract, sum of previous storage and a transaction parameter -### Running LIGO for the first time +- **`function main`** - definition of the main function, which takes + a the parameter of the contract and the storage +- **`(const parameter : int; const contractStorage : int)`** - + parameters passed to the function: the first is called `parameter` + because it denotes the parameter of a specific invocation of the + contract, the second is the storage +- **`(list (operation) * int)`** - return type of our function, in our + case a tuple with a list of operations, and an `int` (new value for + the storage after a succesful run of the contract) +- **`((nil : list (operation)), contractStorage + parameter)`** - + essentially a return statement +- **`(nil : list (operation))`** - a `nil` value annotated as a list + of operations, because that is required by our return type specified + above + - **`contractStorage + parameter`** - a new storage value for our + contract, sum of previous storage and a transaction parameter -To test that we've installed LIGO correctly, and that `taco-shop.ligo` is a valid contract, we'll dry-run it. +### Running LIGO for the First Time -> Dry-running is a simulated execution of the smart contract, based on a mock storage value and a parameter. +To test that we have installed LIGO correctly, and that +`taco-shop.ligo` is a valid contract, we will dry-run it. -Our contract has a storage of `int` and accepts a parameter that is also an `int`. +> Dry-running is a simulated execution of the smart contract, based on +> a mock storage value and a parameter. + +Our contract has a storage of `int` and accepts a parameter that is +also an `int`. The `dry-run` command requires a few parameters: - **contract** *(file path)* -- **entrypoint** *(name of the entrypoint function in the contract)* +- **entrypoint** *(name of the main function in the contract)* - **parameter** *(parameter to execute our contract with)* - **storage** *(starting storage before our contract's code is executed)* - -And outputs what's returned from our entrypoint - in our case a touple containing an empty list (of operations to apply) and the new storage value - which in our case is the sum of the previous storage and the parameter we've used. +It outputs what is returned from our main function: in our case a +tuple containing an empty list (of operations to apply) and the new +storage value, which, in our case, is the sum of the previous storage +and the parameter we have used for the invocation. ```zsh # Contract: taco-shop.ligo -# Entry point: main +# Main function: main # Parameter: 4 # Storage: 3 ligo dry-run taco-shop.ligo --syntax pascaligo main 4 3 @@ -116,7 +151,7 @@ ligo dry-run taco-shop.ligo --syntax pascaligo main 4 3 ``` -
Simulating contract execution with the CLI
+
Simulating contract execution with the CLI

@@ -124,197 +159,257 @@ ligo dry-run taco-shop.ligo --syntax pascaligo main 4 3 --- -## Designing Taco shop's contract storage +## Designing the Taco Shop's Contract Storage -We know that Pedro's Taco Shop serves two kinds of tacos, so we'll need to manage stock individually, per kind. Let's define a type, that will keep the `stock` & `max_price` per kind - in a record with two fields. Additionally, we'll want to combine our `taco_supply` type into a map, consisting of the entire offer of Pedro's shop. +We know that Pedro's Taco Shop serves two kinds of tacos, so we will +need to manage stock individually, per kind. Let us define a type, +that will keep the `stock` & `max_price` per kind in a record with two +fields. Additionally, we will want to combine our `taco_supply` type +into a map, consisting of the entire offer of Pedro's shop. **Taco shop's storage** -```pascaligo group=b -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +```pascaligo group=b +type taco_supply is record [ + current_stock : nat; + max_price : tez +] + +type taco_shop_storage is map (nat, taco_supply) ``` -Next step is to update the `main` entry point to include `taco_shop_storage` in its storage - while doing that let's set the `parameter` to `unit` as well to clear things up. +Next step is to update the `main` function to include +`taco_shop_storage` in its storage. In the meanwhile, let us set the +`parameter` to `unit` as well to clear things up. **`taco-shop.ligo`** -```pascaligo group=b+ -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); -function main (const parameter: unit ; const taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - block {skip} with ((nil : list(operation)), taco_shop_storage) +```pascaligo group=b+ +type taco_supply is record [ + current_stock : nat; + max_price : tez +] + +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function main (const parameter : unit; const taco_shop_storage : taco_shop_storage) : return is + ((nil : list (operation)), taco_shop_storage) ``` -### Populating our storage in a dry-run +### Populating our Storage in a dry-run -When dry-running a contract, it is crucial to provide a correct initial storage value - in our case the storage is type-checked as `taco_shop_storage`. Reflecting [Pedro's daily offer](tutorials/get-started/tezos-taco-shop-smart-contract.md#daily-offer), our storage's value will be defined as following: +When dry-running a contract, it is crucial to provide a correct +initial storage value. In our case the storage is type-checked as +`taco_shop_storage`. Reflecting +[Pedro's daily offer](tutorials/get-started/tezos-taco-shop-smart-contract.md#daily-offer), +our storage's value will be defined as follows: **Storage value** + ```zsh -map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end +map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ] +] ``` -> Storage value is a map, with two items in it, both items are records identified by natural numbers `1n` & `2n`. +> The storage value is a map with two bindings (entries) distinguished +> by their keys `1n` and `2n`. **Dry run command with a multi-line storage value** + ```zsh -ligo dry-run taco-shop.ligo --syntax pascaligo main unit "map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end" +ligo dry-run taco-shop.ligo --syntax pascaligo main unit "map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ] +]" ``` -
Dry-run with a complex storage value
+
Dry-run with a complex storage value

-*If everything went as expected, the `dry-run` command will return an empty list of operations and the contract's current storage, which is the map of products we've defined based on the daily offer of Pedro's taco shop.* +*If everything went as expected, the `dry-run` command will return an + empty list of operations and the contract's current storage, which is + the map of the products we have defined based on the daily offer of + Pedro's taco shop.* --- -## Providing an entrypoint for buying tacos +## Providing another Access Function for Buying Tacos -Now that we have our stock well defined in form of storage, we can move on to the actual sales. We'll replace the `main` entrypoint with `buy_taco`, that takes an `id` - effectively a key from our `taco_shop_storage` map. This will allow us to calculate pricing, and if the sale is successful - then we can reduce our stock - because we have sold a taco! +Now that we have our stock well defined in form of storage, we can +move on to the actual sales. The `main` function will take a key `id` +from our `taco_shop_storage` map and will be renamed `buy_taco` for +more readability. This will allow us to calculate pricing, and if the +sale is successful, we will be able to reduce our stock because we +have sold a taco! -### Selling the tacos for free +### Selling the Tacos for Free -Let's start by customizing our contract a bit, we will: +Let is start by customizing our contract a bit, we will: -- rename the entrypoint from `main` to `buy_taco` - rename `parameter` to `taco_kind_index` -- change `taco_shop_storage` to a `var` instead of a `const`, because we'll want to modify it +- change `taco_shop_storage` to a `var` instead of a `const`, because + we will want to modify it **`taco-shop.ligo`** + ```pascaligo group=c -type taco_supply is record +type taco_supply is record [ current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); + max_price : tez +] +type taco_shop_storage is map (nat, taco_supply) -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - block { skip } with ((nil : list(operation)), taco_shop_storage) +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is + ((nil : list (operation)), taco_shop_storage) ``` -#### Decreasing `current_stock` when a taco is sold +#### Decreasing `current_stock` when a Taco is Sold -In order to decrease the stock in our contract's storage for a specific taco kind, a few things needs to happen: +In order to decrease the stock in our contract's storage for a +specific taco kind, a few things needs to happen: -- retrieve the `taco_kind` from our storage, based on the `taco_kind_index` provided -- subtract the `taco_kind.current_stock` by `1n` - - we can find the absolute (`nat`) value of the subtraction above by using `abs`, otherwise we'd be left with an `int` -- update the storage, and return it +- retrieve the `taco_kind` from our storage, based on the + `taco_kind_index` provided; +- subtract the `taco_kind.current_stock` by `1n`; +- we can find the absolute value of the subtraction above by + calling `abs` (otherwise we would be left with an `int`); +- update the storage, and return it. **`taco-shop.ligo`** ```pascaligo group=d -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); +type taco_supply is record [ + current_stock : nat; + max_price : tez +] + +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) ``` -
Stock decreases after selling a taco
+
Stock decreases after selling a taco

-### Making sure we get paid for our tacos +### Making Sure We Get Paid for Our Tacos -In order to make Pedro's taco shop profitable, he needs to stop giving away tacos for free. When a contract is invoked via a transaction, an amount of tezzies to be sent can be specified as well. This amount is accessible within LIGO as `amount`. +In order to make Pedro's taco shop profitable, he needs to stop giving +away tacos for free. When a contract is invoked via a transaction, an +amount of tezzies to be sent can be specified as well. This amount is +accessible within LIGO as `amount`. To make sure we get paid, we will: -- calculate a `current_purchase_price` based on the [equation specified earlier](tutorials/get-started/tezos-taco-shop-smart-contract.md#calculating-the-current-purchase-price) -- check if the sent `amount` matches the `current_purchase_price` - - if not, then our contract will `fail` and stop executing - - if yes, stock for the given `taco_kind` will be decreased and the payment accepted +- calculate a `current_purchase_price` based on the + [equation specified earlier](tutorials/get-started/tezos-taco-shop-smart-contract.md#calculating-the-current-purchase-price) +- check if the sent `amount` matches the `current_purchase_price`: + - if not, then our contract will fail (`failwith`) + - otherwise, stock for the given `taco_kind` will be decreased and + the payment accepted **`taco-shop.ligo`** -```pascaligo group=e -type taco_supply is record - current_stock : nat; - max_price : tez; -end -type taco_shop_storage is map(nat, taco_supply); -function buy_taco (const taco_kind_index: nat ; var taco_shop_storage : taco_shop_storage) : (list(operation) * taco_shop_storage) is - begin - // Retrieve the taco_kind from the contract's storage - const taco_kind : taco_supply = get_force(taco_kind_index, taco_shop_storage); - - const current_purchase_price : tez = taco_kind.max_price / taco_kind.current_stock; +```pascaligo group=e +type taco_supply is record [ + current_stock : nat; + max_price : tez +] + +type taco_shop_storage is map (nat, taco_supply) + +type return is list (operation) * taco_shop_storage + +function buy_taco (const taco_kind_index : nat ; var taco_shop_storage : taco_shop_storage) : return is + block { + // Retrieve the taco_kind from the contract's storage or fail + const taco_kind : taco_supply = + case taco_shop_storage[taco_kind_index] of + Some (kind) -> kind + | None -> (failwith ("Unknown kind of taco.") : taco_supply) + end; + + const current_purchase_price : tez = + taco_kind.max_price / taco_kind.current_stock; if amount =/= current_purchase_price then - // we won't sell tacos if the amount isn't correct - failwith("Sorry, the taco you're trying to purchase has a different price"); - else - // Decrease the stock by 1n, because we've just sold one - taco_kind.current_stock := abs(taco_kind.current_stock - 1n); + // We won't sell tacos if the amount is not correct + failwith ("Sorry, the taco you are trying to purchase has a different price"); + else skip; + + // Decrease the stock by 1n, because we have just sold one + taco_kind.current_stock := abs (taco_kind.current_stock - 1n); // Update the storage with the refreshed taco_kind - taco_shop_storage[taco_kind_index] := taco_kind; - end with ((nil : list(operation)), taco_shop_storage) + taco_shop_storage[taco_kind_index] := taco_kind + } with ((nil : list (operation)), taco_shop_storage) ``` -In order to test the `amount` sent, we'll use the `--amount` option of `dry-run`: +In order to test the `amount` sent, we will use the `--amount` option +of `dry-run`: ```zsh -ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map - 1n -> record - current_stock = 50n; - max_price = 50000000mutez; - end; - 2n -> record - current_stock = 20n; - max_price = 75000000mutez; - end; -end" +ligo dry-run taco-shop.ligo --syntax pascaligo --amount 1 buy_taco 1n "map [ + 1n -> record [ + current_stock = 50n; + max_price = 50tez + ]; + 2n -> record [ + current_stock = 20n; + max_price = 75tez + ] +]" ``` -**Purchasing a taco with 1.0tz** + +** Purchasing a Taco with 1tez ** -
Stock decreases after selling a taco, if the right amount of tezzies is provided
+
Stock decreases after selling a taco, if the right amount of tezzies is provided

-**Attempting to purchase a taco with 0.7tz** +**Attempting to Purchase a Taco with 0.7tez** -
Stock does not decrease after a purchase attempt with a lower than required amount.
+
Stock does not decrease after a purchase attempt +with an insufficient payment.

@@ -322,16 +417,19 @@ end" --- -## 💰 Bonus: *Accepting tips above the taco purchase price* +## 💰 Bonus: *Accepting Tips above the Taco Purchase Price* -If you'd like to accept tips in your contract as well, simply change the following line, depending on your preference. +If you would like to accept tips in your contract, simply change the +following line, depending on your preference. **Without tips** + ```pascaligo skip if amount =/= current_purchase_price then ``` **With tips** + ```pascaligo skip if amount >= current_purchase_price then ``` diff --git a/gitlab-pages/examples/counter.ligo b/gitlab-pages/examples/counter.ligo deleted file mode 100644 index 45ce7462a..000000000 --- a/gitlab-pages/examples/counter.ligo +++ /dev/null @@ -1,10 +0,0 @@ -type action is -| Increment of int -| Decrement of int - -function main (const p : action ; const s : int) : (list(operation) * int) is - block {skip} with ((nil : list(operation)), - case p of - | Increment n -> s + n - | Decrement n -> s - n - end) diff --git a/gitlab-pages/examples/functions.ligo b/gitlab-pages/examples/functions.ligo deleted file mode 100644 index 4f8e4dae1..000000000 --- a/gitlab-pages/examples/functions.ligo +++ /dev/null @@ -1,10 +0,0 @@ -function multiply (const a : int ; const b : int) : int is - begin - const result : int = a * b ; - end with result - -function add (const a : int ; const b : int) : int is - block { skip } with a + b - -function main (const p : unit ; const s : unit) : (list(operation) * unit) is - block {skip} with ((nil : list(operation)), s) \ No newline at end of file diff --git a/gitlab-pages/examples/multiple-entrypoints.ligo b/gitlab-pages/examples/multiple-entrypoints.ligo deleted file mode 100644 index 3b77ff009..000000000 --- a/gitlab-pages/examples/multiple-entrypoints.ligo +++ /dev/null @@ -1,16 +0,0 @@ -type action is -| Increment of int -| Decrement of int - -function add (const a : int ; const b : int) : int is - block { skip } with a + b - -function subtract (const a : int ; const b : int) : int is - block { skip } with a - b - -function main (const p : action ; const s : int) : (list(operation) * int) is - block {skip} with ((nil : list(operation)), - case p of - | Increment n -> add(s, n) - | Decrement n -> subtract(s, n) - end) diff --git a/gitlab-pages/examples/variables.ligo b/gitlab-pages/examples/variables.ligo deleted file mode 100644 index 09254da96..000000000 --- a/gitlab-pages/examples/variables.ligo +++ /dev/null @@ -1,5 +0,0 @@ -const four : int = 4; -const name : string = "John Doe"; - -function main (const p : unit ; const s : unit) : (list(operation) * unit) is - block {skip} with ((nil : list(operation)), s) \ No newline at end of file diff --git a/gitlab-pages/owner.pp.ligo b/gitlab-pages/owner.pp.ligo deleted file mode 100644 index e69de29bb..000000000 diff --git a/gitlab-pages/timestamp.pp.ligo b/gitlab-pages/timestamp.pp.ligo deleted file mode 100644 index e69de29bb..000000000 diff --git a/gitlab-pages/website/.gitignore b/gitlab-pages/website/.gitignore new file mode 100644 index 000000000..1b34df512 --- /dev/null +++ b/gitlab-pages/website/.gitignore @@ -0,0 +1,20 @@ +# dependencies +/node_modules + +# production +/build + +# generated files +.docusaurus +.cache-loader + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* \ No newline at end of file diff --git a/gitlab-pages/website/blog/2019-06-13-public-launch-of-ligo.md b/gitlab-pages/website/blog/2019-06-13-public-launch-of-ligo.md.old similarity index 100% rename from gitlab-pages/website/blog/2019-06-13-public-launch-of-ligo.md rename to gitlab-pages/website/blog/2019-06-13-public-launch-of-ligo.md.old diff --git a/gitlab-pages/website/blog/2019-07-11-ligo-update.md b/gitlab-pages/website/blog/2019-07-11-ligo-update.md.old similarity index 100% rename from gitlab-pages/website/blog/2019-07-11-ligo-update.md rename to gitlab-pages/website/blog/2019-07-11-ligo-update.md.old diff --git a/gitlab-pages/website/core/CodeExamples.js b/gitlab-pages/website/core/CodeExamples.js index 9cdf27c1c..289d14a73 100644 --- a/gitlab-pages/website/core/CodeExamples.js +++ b/gitlab-pages/website/core/CodeExamples.js @@ -1,8 +1,12 @@ -const React = require('react'); +import React from 'react'; +import Highlight, { defaultProps } from "prism-react-renderer"; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import useThemeContext from '@theme/hooks/useThemeContext'; +import defaultTheme from 'prism-react-renderer/themes/palenight'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -const pre = '```'; - -const PASCALIGO_EXAMPLE = `${pre}pascaligo +const PASCALIGO_EXAMPLE = ` type storage is int type parameter is @@ -14,8 +18,11 @@ type return is list (operation) * storage // Two entrypoints -function add (const store : storage; const delta : int) : storage is store + delta -function sub (const store : storage; const delta : int) : storage is store - delta +function add (const store : storage; const delta : int) : storage is + store + delta + +function sub (const store : storage; const delta : int) : storage is + store - delta (* Main access point that dispatches to the entrypoints according to the smart contract parameter. *) @@ -27,9 +34,9 @@ function main (const action : parameter; const store : storage) : return is | Decrement (n) -> sub (store, n) | Reset -> 0 end) -${pre}`; +`; -const CAMELIGO_EXAMPLE = `${pre}ocaml +const CAMELIGO_EXAMPLE = ` type storage = int type parameter = @@ -53,10 +60,10 @@ let main (action, store : parameter * storage) : return = Increment (n) -> add (store, n) | Decrement (n) -> sub (store, n) | Reset -> 0) -${pre}`; +`; -const REASONLIGO_EXAMPLE = `${pre}reasonligo +const REASONLIGO_EXAMPLE = ` type storage = int; type parameter = @@ -81,40 +88,82 @@ let main = ((action, store) : (parameter, storage)) : return => { | Decrement (n) => sub ((store, n)) | Reset => 0})) }; -${pre}`; +`; -module.exports = props => { - const MarkdownBlock = props.MarkdownBlock; +function CodeExamples (props) { + const { + siteConfig: { + themeConfig: {prism = {}}, + }, + } = useDocusaurusContext(); + const {isDarkTheme} = useThemeContext(); + const lightModeTheme = prism.theme || defaultTheme; + const darkModeTheme = prism.darkTheme || lightModeTheme; + const prismTheme = isDarkTheme ? darkModeTheme : lightModeTheme; return ( -
-
-
- PascaLIGO -
-
- CameLIGO -
-
- ReasonLIGO -
-
-
-
- {PASCALIGO_EXAMPLE} -
-
- {CAMELIGO_EXAMPLE} -
-
- {REASONLIGO_EXAMPLE} -
-
-
+ + + + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+                {tokens.map((line, i) => (
+                  
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ )} +
+
+ + + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+                {tokens.map((line, i) => (
+                  
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ )} +
+
+ + + + {({ className, style, tokens, getLineProps, getTokenProps }) => ( +
+                {tokens.map((line, i) => (
+                  
+ {line.map((token, key) => ( + + ))} +
+ ))} +
+ )} +
+
+ + + +
); }; + +export default CodeExamples \ No newline at end of file diff --git a/gitlab-pages/website/core/Footer.js b/gitlab-pages/website/core/Footer.js index c40de4b0a..3aae1cda0 100644 --- a/gitlab-pages/website/core/Footer.js +++ b/gitlab-pages/website/core/Footer.js @@ -1,3 +1,5 @@ +// deprecated. + const React = require('react'); const docUrl = require(`${process.cwd()}/core/UrlUtils`).docUrl; diff --git a/gitlab-pages/website/docusaurus.config.js b/gitlab-pages/website/docusaurus.config.js new file mode 100644 index 000000000..5fd7c6ddd --- /dev/null +++ b/gitlab-pages/website/docusaurus.config.js @@ -0,0 +1,226 @@ + +const repoUrl = 'https://gitlab.com/ligolang/ligo'; + +// let reasonHighlightJs = require('reason-highlightjs'); + +const siteConfig = { + title: 'LIGO', // Title for your website. + tagline: 'LIGO is a friendly smart contract language for Tezos', + // taglineSub: 'Michelson was never so easy', + url: 'https://ligolang.org', // Your website URL + baseUrl: '/', // Base URL for your project */ + // For github.io type URLs, you would set the url and baseUrl like: + // url: 'https://facebook.github.io', + // baseUrl: '/test-site/', + + // Used for publishing and more + projectName: 'ligo', + organizationName: 'TBN', + // For top-level user or org sites, the organization is still the same. + // e.g., for the https://JoelMarcey.github.io site, it would be set like... + // organizationName: 'JoelMarcey' + + // For no header links in the top nav bar -> headerLinks: [], + + + // footerLinks: { + // docs: [ + // { doc: 'intro/installation', label: 'Install' }, + // { doc: 'api/cli-commands', label: 'CLI Commands' }, + // { doc: 'contributors/origin', label: 'Contribute' }, + // { href: '/odoc', label: 'API Documentation' } + // ], + // community: [ + // { + // href: 'https://forum.tezosagora.org/tag/ligo', + // label: 'Tezos Agora Forum', + // blankTarget: true + // }, + // { + // href: 'https://tezos.stackexchange.com/questions/tagged/ligo', + // label: 'Tezos Stack Exchange', + // blankTarget: true + // }, + // { + // href: 'https://t.me/LigoLang', + // label: 'Telegram', + // blankTarget: true + // }, + // { + // href: 'https://discord.gg/9rhYaEt', + // label: 'Discord', + // blankTarget: true + // } + // ], + // more: [ + // { + // doc: 'tutorials/get-started/tezos-taco-shop-smart-contract', + // label: 'Tutorials' + // }, + // { href: repoUrl, label: 'GitLab' } + // ] + // }, + + favicon: 'img/circle.svg', + + /* highlight: { + // Highlight.js theme to use for syntax highlighting in code blocks. + theme: 'default', + hljs: function (hljs) { + hljs.registerLanguage('reasonligo', reasonHighlightJs); + hljs.registerLanguage('pascaligo', function (hljs) { + return { + // case_insensitive: true, + beginKeywords: '', + keywords: { + keyword: + 'and attributes begin big_map block case const contains else' + + ' end False for from function if in is list map mod nil' + + ' not of or patch record remove set skip then to True type' + + ' var while with', + literal: 'true false unit int string Some None bool nat list' + }, + lexemes: '[a-zA-Z][a-zA-Z0-9_]*', + contains: [ + hljs.C_LINE_COMMENT_MODE, + + { + className: 'type', + begin: /[A-Z][a-z]+/ + }, + { + begin: /[*+-:;\(\)\{\}|\>\<]/ + // className: 'ignore' + } + ] + }; + }); + } + },*/ + + // Add custom scripts here that would be placed in