Merge branch 'dev' into contract/hashlock
This commit is contained in:
commit
741374fa24
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,6 +5,8 @@ cache/*
|
|||||||
Version.ml
|
Version.ml
|
||||||
/_opam/
|
/_opam/
|
||||||
/*.pp.ligo
|
/*.pp.ligo
|
||||||
|
/*.pp.mligo
|
||||||
|
/*.pp.religo
|
||||||
**/.DS_Store
|
**/.DS_Store
|
||||||
.vscode/
|
.vscode/
|
||||||
/ligo.install
|
/ligo.install
|
||||||
|
124
.gitlab-ci.yml
124
.gitlab-ci.yml
@ -3,13 +3,25 @@ variables:
|
|||||||
GIT_SUBMODULE_STRATEGY: recursive
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
build_binary_script: "./scripts/distribution/generic/build.sh"
|
build_binary_script: "./scripts/distribution/generic/build.sh"
|
||||||
package_binary_script: "./scripts/distribution/generic/package.sh"
|
package_binary_script: "./scripts/distribution/generic/package.sh"
|
||||||
|
LIGO_REGISTRY_IMAGE_BASE_NAME: "${CI_PROJECT_PATH}/${CI_PROJECT_NAME}"
|
||||||
|
WEBIDE_IMAGE_NAME: "registry.gitlab.com/${CI_PROJECT_PATH}/ligo_webide"
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- test
|
- test
|
||||||
- build_and_package_binaries
|
- build_and_package_binaries
|
||||||
- build_docker
|
- build_docker
|
||||||
- build_and_deploy_docker
|
- build_and_deploy
|
||||||
- build_and_deploy_website
|
- ide-build
|
||||||
|
- ide-e2e-test
|
||||||
|
- ide-deploy
|
||||||
|
|
||||||
|
# TODO provide sensible CI for master
|
||||||
|
dont-merge-to-master:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- "false"
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
|
||||||
.build_binary: &build_binary
|
.build_binary: &build_binary
|
||||||
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
# To run in sequence and save CPU usage, use stage: build_and_package_binaries
|
||||||
@ -22,7 +34,7 @@ stages:
|
|||||||
- dist/package/**/*
|
- dist/package/**/*
|
||||||
|
|
||||||
.website_build: &website_build
|
.website_build: &website_build
|
||||||
stage: build_and_deploy_website
|
stage: build_and_deploy
|
||||||
image: node:8
|
image: node:8
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-and-package-debian-9
|
- build-and-package-debian-9
|
||||||
@ -30,6 +42,7 @@ stages:
|
|||||||
- build-and-package-ubuntu-18-04
|
- build-and-package-ubuntu-18-04
|
||||||
- build-and-package-ubuntu-19-04
|
- build-and-package-ubuntu-19-04
|
||||||
before_script:
|
before_script:
|
||||||
|
- export TERM=dumb
|
||||||
- scripts/install_native_dependencies.sh
|
- scripts/install_native_dependencies.sh
|
||||||
- scripts/install_opam.sh # TODO: or scripts/install_build_environment.sh ?
|
- scripts/install_opam.sh # TODO: or scripts/install_build_environment.sh ?
|
||||||
- export PATH="/usr/local/bin${PATH:+:}${PATH:-}"
|
- export PATH="/usr/local/bin${PATH:+:}${PATH:-}"
|
||||||
@ -67,15 +80,16 @@ stages:
|
|||||||
- public
|
- public
|
||||||
|
|
||||||
.docker: &docker
|
.docker: &docker
|
||||||
image: docker:1.11
|
image: docker:19
|
||||||
services:
|
services:
|
||||||
- docker:dind
|
- docker:19-dind
|
||||||
|
|
||||||
|
|
||||||
.before_script: &before_script
|
.before_script: &before_script
|
||||||
before_script:
|
before_script:
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
# rsync is needed by opam to sync a package installed from a local directory with the copy in ~/.opam
|
# rsync is needed by opam to sync a package installed from a local directory with the copy in ~/.opam
|
||||||
|
- export TERM=dumb
|
||||||
- scripts/install_native_dependencies.sh
|
- scripts/install_native_dependencies.sh
|
||||||
- scripts/install_opam.sh # TODO: or scripts/install_build_environment.sh ?
|
- scripts/install_opam.sh # TODO: or scripts/install_build_environment.sh ?
|
||||||
- export PATH="/usr/local/bin${PATH:+:}${PATH:-}"
|
- export PATH="/usr/local/bin${PATH:+:}${PATH:-}"
|
||||||
@ -95,6 +109,9 @@ local-dune-job:
|
|||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- _coverage_all
|
- _coverage_all
|
||||||
|
only:
|
||||||
|
- merge_requests
|
||||||
|
- dev
|
||||||
|
|
||||||
# Run a docker build without publishing to the registry
|
# Run a docker build without publishing to the registry
|
||||||
build-current-docker-image:
|
build-current-docker-image:
|
||||||
@ -105,24 +122,25 @@ build-current-docker-image:
|
|||||||
script:
|
script:
|
||||||
- sh scripts/build_docker_image.sh
|
- sh scripts/build_docker_image.sh
|
||||||
- sh scripts/test_cli.sh
|
- sh scripts/test_cli.sh
|
||||||
except:
|
only:
|
||||||
- master
|
- merge_requests
|
||||||
- dev
|
|
||||||
|
|
||||||
# When a MR/PR is merged to dev
|
# When a MR/PR is merged to dev
|
||||||
# take the previous build and publish it to Docker Hub
|
# take the previous build and publish it to Docker Hub
|
||||||
build-and-publish-latest-docker-image:
|
build-and-publish-latest-docker-image:
|
||||||
stage: build_and_deploy_docker
|
stage: build_and_deploy
|
||||||
<<: *docker
|
<<: *docker
|
||||||
dependencies:
|
dependencies:
|
||||||
- build-and-package-debian-10
|
- build-and-package-debian-10
|
||||||
script:
|
script:
|
||||||
- sh scripts/build_docker_image.sh
|
- sh scripts/build_docker_image.sh
|
||||||
- sh scripts/test_cli.sh
|
- sh scripts/test_cli.sh
|
||||||
- docker login -u $LIGO_REGISTRY_USER -p $LIGO_REGISTRY_PASSWORD
|
- echo ${LIGO_REGISTRY_PASSWORD} | docker login -u ${LIGO_REGISTRY_USER} --password-stdin
|
||||||
- docker push $LIGO_REGISTRY_IMAGE:next
|
- docker push ${LIGO_REGISTRY_IMAGE_BUILD:-ligolang/ligo}:next
|
||||||
only:
|
rules:
|
||||||
- dev
|
# Only deploy docker when from the dev branch AND on the canonical ligolang/ligo repository
|
||||||
|
- if: '$CI_COMMIT_REF_NAME == "dev" && $CI_PROJECT_PATH == "ligolang/ligo"'
|
||||||
|
when: always
|
||||||
|
|
||||||
# It'd be a good idea to generate those jobs dynamically,
|
# It'd be a good idea to generate those jobs dynamically,
|
||||||
# based on desired targets
|
# based on desired targets
|
||||||
@ -135,6 +153,8 @@ build-and-package-debian-9:
|
|||||||
target_os: "debian"
|
target_os: "debian"
|
||||||
target_os_version: "9"
|
target_os_version: "9"
|
||||||
<<: *build_binary
|
<<: *build_binary
|
||||||
|
only:
|
||||||
|
- dev
|
||||||
|
|
||||||
build-and-package-debian-10:
|
build-and-package-debian-10:
|
||||||
<<: *docker
|
<<: *docker
|
||||||
@ -145,6 +165,12 @@ build-and-package-debian-10:
|
|||||||
target_os: "debian"
|
target_os: "debian"
|
||||||
target_os_version: "10"
|
target_os_version: "10"
|
||||||
<<: *build_binary
|
<<: *build_binary
|
||||||
|
# this one is merge_requests and dev, because the debian 10 binary
|
||||||
|
# is used for build-current-docker-image and for
|
||||||
|
# build-and-publish-latest-docker-image
|
||||||
|
only:
|
||||||
|
- merge_requests
|
||||||
|
- dev
|
||||||
|
|
||||||
build-and-package-ubuntu-18-04:
|
build-and-package-ubuntu-18-04:
|
||||||
<<: *docker
|
<<: *docker
|
||||||
@ -155,6 +181,8 @@ build-and-package-ubuntu-18-04:
|
|||||||
target_os: "ubuntu"
|
target_os: "ubuntu"
|
||||||
target_os_version: "18.04"
|
target_os_version: "18.04"
|
||||||
<<: *build_binary
|
<<: *build_binary
|
||||||
|
only:
|
||||||
|
- dev
|
||||||
|
|
||||||
build-and-package-ubuntu-19-04:
|
build-and-package-ubuntu-19-04:
|
||||||
<<: *docker
|
<<: *docker
|
||||||
@ -165,11 +193,75 @@ build-and-package-ubuntu-19-04:
|
|||||||
target_os: "ubuntu"
|
target_os: "ubuntu"
|
||||||
target_os_version: "19.04"
|
target_os_version: "19.04"
|
||||||
<<: *build_binary
|
<<: *build_binary
|
||||||
|
only:
|
||||||
|
- dev
|
||||||
|
|
||||||
# Pages are deployed from both master & dev, be careful not to override 'next'
|
# Pages are deployed from dev, be careful not to override 'next'
|
||||||
# in case something gets merged into 'dev' while releasing.
|
# in case something gets merged into 'dev' while releasing.
|
||||||
pages:
|
pages:
|
||||||
<<: *website_build
|
<<: *website_build
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_REF_NAME == "dev" && $CI_PROJECT_PATH == "ligolang/ligo"'
|
||||||
|
when: always
|
||||||
|
|
||||||
|
# WEBIDE jobs
|
||||||
|
|
||||||
|
run-webide-unit-tests:
|
||||||
|
stage: test
|
||||||
|
image: node:12-alpine
|
||||||
|
script:
|
||||||
|
- cd tools/webide/packages/server
|
||||||
|
- npm ci
|
||||||
|
- npm run test
|
||||||
only:
|
only:
|
||||||
- master
|
changes:
|
||||||
- dev
|
- tools/webide/**
|
||||||
|
|
||||||
|
build-publish-ide-image:
|
||||||
|
stage: build_and_deploy
|
||||||
|
<<: *docker
|
||||||
|
script:
|
||||||
|
- ls -F
|
||||||
|
- find dist/
|
||||||
|
- find dist/package/ -name '*ligo_*deb'
|
||||||
|
- mv $(realpath dist/package/debian-10/*.deb) tools/webide/ligo_deb10.deb
|
||||||
|
- cd tools/webide
|
||||||
|
- echo "${CI_BUILD_TOKEN}" | docker login -u gitlab-ci-token --password-stdin registry.gitlab.com
|
||||||
|
- >
|
||||||
|
docker build
|
||||||
|
-t "${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
|
||||||
|
--build-arg GIT_TAG="${CI_COMMIT_SHA}"
|
||||||
|
--build-arg GIT_COMMIT="${CI_COMMIT_SHORT_SHA}"
|
||||||
|
.
|
||||||
|
- docker push "${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
when: always
|
||||||
|
- if: '$CI_COMMIT_REF_NAME == "dev"'
|
||||||
|
when: always
|
||||||
|
|
||||||
|
run-webide-e2e-tests:
|
||||||
|
stage: ide-e2e-test
|
||||||
|
<<: *docker
|
||||||
|
image: tmaier/docker-compose
|
||||||
|
script:
|
||||||
|
- cd tools/webide/packages/e2e
|
||||||
|
- export WEBIDE_IMAGE="${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}"
|
||||||
|
- docker-compose run e2e
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||||
|
when: always
|
||||||
|
- if: '$CI_COMMIT_REF_NAME == "dev"'
|
||||||
|
when: always
|
||||||
|
|
||||||
|
deploy-handoff:
|
||||||
|
# Handoff deployment duties to private repo
|
||||||
|
stage: ide-deploy
|
||||||
|
variables:
|
||||||
|
IDE_DOCKER_IMAGE: "registry.gitlab.com/${CI_PROJECT_PATH}/ligo_webide"
|
||||||
|
LIGO_COMMIT_REF_NAME: "${CI_COMMIT_SHORT_SHA}"
|
||||||
|
trigger: ligolang/ligo-webide-deploy
|
||||||
|
rules:
|
||||||
|
- if: '$CI_COMMIT_REF_NAME == "dev"'
|
||||||
|
when: always
|
||||||
|
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [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.
|
||||||
|
|
||||||
|
## [Add crypto reference page to docs](https://gitlab.com/ligolang/ligo/-/merge_requests/370)
|
||||||
|
### Changed
|
||||||
|
- Corrected typo in CameLIGO/ReasonLIGO front end where Crypto.blake2b was 'Crypto.black2b'
|
||||||
|
|
||||||
## [Failwith do not fail](https://gitlab.com/ligolang/ligo/merge_requests/337) - 2020-01-17
|
## [Failwith do not fail](https://gitlab.com/ligolang/ligo/merge_requests/337) - 2020-01-17
|
||||||
### Added
|
### Added
|
||||||
- running failing code in `ligo interpret`, `ligo dry-run`, `ligo run-function` will no longer be an error (return value : 0)
|
- running failing code in `ligo interpret`, `ligo dry-run`, `ligo run-function` will no longer be an error (return value : 0)
|
||||||
|
@ -1,104 +1,240 @@
|
|||||||
---
|
---
|
||||||
id: entrypoints-contracts
|
id: entrypoints-contracts
|
||||||
title: Entrypoints, Contracts
|
title: Access function and Entrypoints
|
||||||
---
|
---
|
||||||
|
|
||||||
## Entrypoints
|
## Access Functions
|
||||||
|
|
||||||
Each LIGO smart contract is essentially a single function, that has the following *(pseudo)* type signature:
|
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
|
||||||
|
*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.
|
||||||
|
|
||||||
|
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
|
||||||
|
`storage` has been defined elsewhere. (Note that you can use any type
|
||||||
|
with any name for the storage.)
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```
|
```pascaligo skip
|
||||||
(const parameter: my_type, const store: my_store_type): (list(operation), my_store_type)
|
type storage is ... // Any name, any type
|
||||||
|
type return is list (operation) * storage
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```
|
```cameligo skip
|
||||||
(parameter, store: my_type * my_store_type) : operation list * my_store_type
|
type storage = ... // Any name, any type
|
||||||
|
type return = operation list * storage
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo skip
|
||||||
|
type storage = ...; // Any name, any type
|
||||||
|
type return = (list (operation), storage);
|
||||||
```
|
```
|
||||||
(parameter_store: (my_type, my_store_type)) : (list(operation), my_store_type)
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
This means that every smart contract needs at least one entrypoint function, here's an example:
|
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 below literally does *nothing*
|
Here is an example where the storage is a single natural number that
|
||||||
|
is updated by the parameter.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
type parameter is unit;
|
type parameter is nat
|
||||||
type store is unit;
|
type storage is nat
|
||||||
function main(const parameter: parameter; const store: store): (list(operation) * store) is
|
type return is list (operation) * storage
|
||||||
block { skip } with ((nil : list(operation)), store)
|
|
||||||
|
function save (const action : parameter; const store : storage) : return is
|
||||||
|
((nil : list (operation)), store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
type parameter = unit
|
type parameter = nat
|
||||||
type store = unit
|
type storage = nat
|
||||||
let main (parameter, store: parameter * store) : operation list * store =
|
type return = operation list * storage
|
||||||
|
|
||||||
|
let save (action, store: parameter * storage) : return =
|
||||||
(([] : operation list), store)
|
(([] : operation list), store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
type parameter = unit;
|
type parameter = nat;
|
||||||
type store = unit;
|
type storage = nat;
|
||||||
let main = ((parameter, store): (parameter, store)) : (list(operation), store) => {
|
type return = (list (operation), storage);
|
||||||
(([]: list(operation)), store);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
|
let main = ((action, store): (parameter, storage)) : return =>
|
||||||
|
(([] : list (operation)), store);
|
||||||
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
Each entrypoint function receives two arguments:
|
## Entrypoints
|
||||||
- `parameter` - this is the parameter received in the invocation operation
|
|
||||||
- `storage` - this is the current (real) on-chain storage value
|
|
||||||
|
|
||||||
Storage can only be modified by running the smart contract entrypoint, which is responsible for returning a list of operations, and a new storage at the end of it's execution.
|
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*.
|
||||||
|
|
||||||
|
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
|
||||||
|
entrypoint.
|
||||||
|
|
||||||
## Built-in contract variables
|
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
|
||||||
|
control flow depending on a *pattern matching* on the contract
|
||||||
|
parameter.
|
||||||
|
|
||||||
Each LIGO smart contract deployed on the Tezos blockchain, has access to certain built-in variables/constants that can be used to determine a range
|
In the following example, the storage contains a counter of type `nat`
|
||||||
of useful things. In this section you'll find how those built-ins can be utilized.
|
and a name of type `string`. Depending on the parameter of the
|
||||||
|
contract, either the counter or the name is updated.
|
||||||
### Accepting/declining money in a smart contract
|
|
||||||
|
|
||||||
This example shows how `amount` and `failwith` can be used to decline a transaction that sends more tez than `0mutez`.
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
type parameter is
|
||||||
block {
|
Action_A of nat
|
||||||
if amount > 0mutez then failwith("This contract does not accept tez") else skip
|
| Action_B of string
|
||||||
} with ((nil : list(operation)), unit);
|
|
||||||
|
type storage is record [
|
||||||
|
counter : nat;
|
||||||
|
name : string
|
||||||
|
]
|
||||||
|
|
||||||
|
type return is list (operation) * storage
|
||||||
|
|
||||||
|
function entry_A (const n : nat; const store : storage) : return is
|
||||||
|
((nil : list (operation)), store with record [counter = n])
|
||||||
|
|
||||||
|
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
|
||||||
|
case action of
|
||||||
|
Action_A (n) -> entry_A (n, store)
|
||||||
|
| Action_B (s) -> entry_B (s, store)
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let main (p, s: unit * unit) : operation list * unit =
|
type parameter =
|
||||||
if amount > 0mutez
|
Action_A of nat
|
||||||
then (failwith "This contract does not accept tez": operation list * unit)
|
| Action_B of string
|
||||||
else (([]: operation list), unit)
|
|
||||||
|
type storage = {
|
||||||
|
counter : nat;
|
||||||
|
name : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type return = operation list * storage
|
||||||
|
|
||||||
|
let entry_A (n, store : nat * storage) : return =
|
||||||
|
([] : operation list), {store with counter = n}
|
||||||
|
|
||||||
|
let entry_B (s, store : string * storage) : return =
|
||||||
|
([] : operation list), {store with name = s}
|
||||||
|
|
||||||
|
let access (action, store: parameter * storage) : return =
|
||||||
|
match action with
|
||||||
|
Action_A n -> entry_A (n, store)
|
||||||
|
| Action_B s -> entry_B (s, store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let main = ((p,s): (unit, unit)) : (list(operation), unit) => {
|
type parameter =
|
||||||
if (amount > 0mutez) {
|
| Action_A (nat)
|
||||||
(failwith("This contract does not accept tez"): (list(operation), unit));
|
| Action_B (string);
|
||||||
}
|
|
||||||
else {
|
type storage = {
|
||||||
(([]: list(operation)), ());
|
counter : nat,
|
||||||
|
name : string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
|
||||||
|
let entry_A = ((n, store): (nat, storage)) : return =>
|
||||||
|
(([] : list (operation)), {...store, counter : n});
|
||||||
|
|
||||||
|
let entry_B = ((s, store): (string, storage)) : return =>
|
||||||
|
(([] : list (operation)), {...store, name : s});
|
||||||
|
|
||||||
|
let access = ((action, store): (parameter, storage)) : return =>
|
||||||
|
switch (action) {
|
||||||
|
| Action_A (n) => entry_A ((n, store))
|
||||||
|
| Action_B (s) => entry_B ((s, store))
|
||||||
|
};
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Tezos-specific Built-ins
|
||||||
|
|
||||||
|
A LIGO smart contract can query part of the state of the Tezos
|
||||||
|
blockchain by means of built-in values. In this section you will find
|
||||||
|
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
|
||||||
|
incoming tokens are accepted.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```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
|
||||||
|
(failwith ("This contract does not accept tokens.") : return)
|
||||||
|
else ((nil : list (operation)), store)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```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)
|
||||||
|
else (([] : operation list), store)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=c
|
||||||
|
type parameter = unit;
|
||||||
|
type storage = unit;
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
|
||||||
|
let deny = ((action, store): (parameter, storage)) : return => {
|
||||||
|
if (amount > 0mutez) {
|
||||||
|
(failwith("This contract does not accept tokens."): return); }
|
||||||
|
else { (([] : list (operation)), store); };
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -109,130 +245,168 @@ let main = ((p,s): (unit, unit)) : (list(operation), unit) => {
|
|||||||
This example shows how `sender` or `source` can be used to deny access to an entrypoint.
|
This example shows how `sender` or `source` can be used to deny access to an entrypoint.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
const owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
|
||||||
block {
|
function filter (const action : parameter; const store : storage) : return is
|
||||||
if source =/= owner then failwith("This address can't call the contract") else skip
|
if source =/= owner then (failwith ("Access denied.") : return)
|
||||||
} with ((nil : list(operation)), unit);
|
else ((nil : list(operation)), store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
|
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
|
||||||
let main (p,s: unit * unit) : operation list * unit =
|
|
||||||
if source <> owner
|
let filter (action, store: parameter * storage) : return =
|
||||||
then (failwith "This address can't call the contract": operation list * unit)
|
if source <> owner then (failwith "Access denied." : return)
|
||||||
else (([]: operation list), ())
|
else (([] : operation list), store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
let owner : address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
||||||
let main = ((p,s): (unit, unit)) : (list(operation), unit) => {
|
|
||||||
if (source != owner) {
|
let access = ((action, store): (parameter, storage)) : storage => {
|
||||||
(failwith("This address can't call the contract"): (list(operation), unit));
|
if (source != owner) { (failwith ("Access denied.") : return); }
|
||||||
}
|
else { (([] : list (operation)), store); };
|
||||||
else {
|
|
||||||
(([]: list(operation)), ());
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Cross contract calls
|
### Inter-Contract Invocations
|
||||||
|
|
||||||
This example shows how a contract can invoke another contract by emiting a transaction operation at the end of an entrypoint.
|
It would be somewhat misleading to speak of "contract calls", as this
|
||||||
|
wording may wrongly suggest an analogy between contract "calls" and
|
||||||
|
function "calls". Indeed, the control flow returns to the site of a
|
||||||
|
function call, and composed function calls therefore are *stacked*,
|
||||||
|
that is, they follow a last in, first out ordering. This is not what
|
||||||
|
happens when a contract invokes another: the invocation is *queued*,
|
||||||
|
that is, follows a first in, first our ordering, and the dequeuing
|
||||||
|
only starts at the normal end of a contract (no failure). That is why
|
||||||
|
we speak of "contract invocations" instead of "calls".
|
||||||
|
|
||||||
> The same technique can be used to transfer tez to an implicit account (tz1, ...), all you have to do is use `unit` instead of a parameter for a smart contract.
|
The following example shows how a contract can invoke another by
|
||||||
|
emiting a transaction operation at the end of an entrypoint.
|
||||||
|
|
||||||
In our case, we have a `counter.ligo` contract that accepts a parameter of type `action`, and we have a `proxy.ligo` contract that accepts the same parameter type, and forwards the call to the deployed counter contract.
|
> The same technique can be used to transfer tokens to an implicit
|
||||||
|
> account (tz1, ...): all you have to do is use a unit value as the
|
||||||
|
> parameter of the smart contract.
|
||||||
|
|
||||||
|
In our case, we have a `counter.ligo` contract that accepts an action
|
||||||
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo
|
|
||||||
// counter.ligo
|
|
||||||
type action is
|
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo skip
|
||||||
|
// counter.ligo
|
||||||
|
type parameter is
|
||||||
|
Increment of nat
|
||||||
|
| Decrement of nat
|
||||||
|
| Reset
|
||||||
|
|
||||||
|
type storage is unit
|
||||||
|
|
||||||
|
type return is list (operation) * storage
|
||||||
```
|
```
|
||||||
|
|
||||||
```pascaligo skip
|
```pascaligo group=d
|
||||||
// proxy.ligo
|
// proxy.ligo
|
||||||
|
|
||||||
type action is
|
type parameter is
|
||||||
| Increment of int
|
Increment of nat
|
||||||
| Decrement of int
|
| Decrement of nat
|
||||||
| Reset of unit
|
| Reset
|
||||||
|
|
||||||
const dest: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address);
|
type storage is unit
|
||||||
|
|
||||||
function proxy(const param: action; const store: unit): (list(operation) * unit)
|
type return is list (operation) * storage
|
||||||
is block {
|
|
||||||
const counter: contract(action) = get_contract(dest);
|
const dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address)
|
||||||
// re-use the param passed to the proxy in the subsequent transaction
|
|
||||||
// e.g.:
|
function proxy (const action : parameter; const store : storage): return is
|
||||||
// const mockParam: action = Increment(5);
|
block {
|
||||||
const op: operation = transaction(param, 0mutez, counter);
|
const counter : contract (parameter) = get_contract (dest);
|
||||||
const opList: list(operation) = list op; end;
|
(* Reuse the parameter in the subsequent
|
||||||
} with (opList, store)
|
transaction or use another one, `mock_param`. *)
|
||||||
|
const mock_param : parameter = Increment (5n);
|
||||||
|
const op : operation = transaction (action, 0mutez, counter);
|
||||||
|
const ops : list (operation) = list [op]
|
||||||
|
} with (ops, store)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo skip
|
||||||
// counter.mligo
|
// counter.mligo
|
||||||
type action =
|
|
||||||
| Increment of int
|
type parameter =
|
||||||
| Decrement of int
|
Increment of nat
|
||||||
| Reset of unit
|
| Decrement of nat
|
||||||
|
| Reset
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```cameligo
|
```cameligo group=d
|
||||||
// proxy.mligo
|
// proxy.mligo
|
||||||
|
|
||||||
type action =
|
type parameter =
|
||||||
| Increment of int
|
Increment of nat
|
||||||
| Decrement of int
|
| Decrement of nat
|
||||||
| Reset of unit
|
| Reset
|
||||||
|
|
||||||
|
type storage = unit
|
||||||
|
|
||||||
|
type return = operation list * storage
|
||||||
|
|
||||||
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address)
|
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address)
|
||||||
|
|
||||||
let proxy (param, storage: action * unit): operation list * unit =
|
let proxy (action, store : parameter * storage) : return =
|
||||||
let counter: action contract = Operation.get_contract dest in
|
let counter : parameter contract = Operation.get_contract dest in
|
||||||
let op: operation = Operation.transaction param 0mutez counter in
|
(* Reuse the parameter in the subsequent
|
||||||
[op], storage
|
transaction or use another one, `mock_param`. *)
|
||||||
|
let mock_param : parameter = Increment (5n) in
|
||||||
|
let op : operation = Operation.transaction action 0mutez counter
|
||||||
|
in [op], store
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo skip
|
||||||
// counter.religo
|
// counter.religo
|
||||||
|
|
||||||
type action =
|
type parameter =
|
||||||
| Increment(int)
|
| Increment (nat)
|
||||||
| Decrement(int)
|
| Decrement (nat)
|
||||||
| Reset(unit);
|
| Reset
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
```
|
```
|
||||||
|
|
||||||
```reasonligo
|
```reasonligo group=d
|
||||||
// proxy.religo
|
// proxy.religo
|
||||||
|
|
||||||
type action =
|
type parameter =
|
||||||
| Increment(int)
|
| Increment (nat)
|
||||||
| Decrement(int)
|
| Decrement (nat)
|
||||||
| Reset(unit);
|
| Reset;
|
||||||
|
|
||||||
|
type storage = unit;
|
||||||
|
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
|
||||||
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address);
|
let dest : address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3" : address);
|
||||||
|
|
||||||
let proxy = ((param, s): (action, unit)): (list(operation), unit) =>
|
let proxy = ((action, store): (parameter, storage)) : return => {
|
||||||
let counter: contract(action) = Operation.get_contract(dest);
|
let counter : contract (parameter) = Operation.get_contract (dest);
|
||||||
let op: operation = Operation.transaction(param, 0mutez, counter);
|
(* Reuse the parameter in the subsequent
|
||||||
([op], s);
|
transaction or use another one, `mock_param`. *)
|
||||||
|
let mock_param : parameter = Increment (5n);
|
||||||
|
let op : operation = Operation.transaction (action, 0mutez, counter);
|
||||||
|
([op], store)
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,20 +3,24 @@ id: first-contract
|
|||||||
title: First contract
|
title: First contract
|
||||||
---
|
---
|
||||||
|
|
||||||
So far so good, we've learned enough of the LIGO language, we're confident enough to write out first smart contract.
|
So far so good, we have learned enough of the LIGO language, we are
|
||||||
|
confident enough to write out first smart contract.
|
||||||
|
|
||||||
We'll be implementing a counter contract, let's go.
|
We will be implementing a counter contract.
|
||||||
|
|
||||||
## Dry-running a 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 entrypoint execution, as if it were deployed on a real chain. You need to provide the following:
|
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:
|
||||||
|
|
||||||
- `file` - contract to run
|
- `file` - contract to run
|
||||||
- `entrypoint` - name of the function to execute
|
- `entrypoint` - name of the function to execute
|
||||||
- `parameter` - parameter passed to the entrypoint (in a theoretical invocation operation)
|
- `parameter` - parameter passed to the access function (in a theoretical invocation operation)
|
||||||
- `storage` - a mock storage value, as if it were stored on a real chain
|
- `storage` - a mock storage value, as if it were stored on a real chain
|
||||||
|
|
||||||
Here's a full example:
|
Here is a full example:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
@ -29,25 +33,29 @@ ligo dry-run src/basic.ligo main Unit Unit
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
Output of the `dry-run` is the return value of our entrypoint 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 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`.
|
||||||
|
|
||||||
## Building a counter contract
|
## 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` entrypoint into two entrypoints for `addition` and `subtraction`.
|
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`.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
```
|
```
|
||||||
type action is
|
type action is
|
||||||
| Increment of int
|
Increment of int
|
||||||
| Decrement of int
|
| Decrement of int
|
||||||
|
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
function main (const p : action ; const s : int) : (list(operation) * int) is
|
||||||
block {skip} with ((nil : list(operation)),
|
((nil : list(operation)),
|
||||||
case p of
|
(case p of
|
||||||
| Increment (n) -> s + n
|
| Increment (n) -> s + n
|
||||||
| Decrement (n) -> s - n
|
| Decrement (n) -> s - n
|
||||||
end)
|
end))
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -98,11 +106,13 @@ ligo dry-run src/counter.ligo main "Increment(5)" 5
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
Yay, our contract's storage has been successfuly incremented to `10`.
|
Our contract's storage has been successfuly incremented to `10`.
|
||||||
|
|
||||||
## Deploying and interacting with a contract on a live-chain
|
## Deploying and interacting with a contract on a live-chain
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
@ -156,7 +166,10 @@ Command above will output the following Michelson code:
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--Pascaligo-->
|
||||||
@ -167,7 +180,7 @@ ligo compile-storage src/counter.ligo main 5
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
In our case the LIGO storage value maps 1:1 to it's 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
|
## Invoking a LIGO contract
|
||||||
|
|
||||||
@ -182,4 +195,5 @@ ligo compile-parameter src/counter.ligo main 'Increment(5)'
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
Now we can use `(Right 5)` which is a Michelson value, to invoke our contract - e.g. via `tezos-client`
|
Now we can use `(Right 5)` which is a Michelson value, to invoke our
|
||||||
|
contract - e.g. via `tezos-client`
|
||||||
|
@ -3,13 +3,12 @@ id: include
|
|||||||
title: Including Other Contracts
|
title: Including Other Contracts
|
||||||
---
|
---
|
||||||
|
|
||||||
Lets say we have a contract that's getting a bit too big. If it has a modular
|
Let us say that we have a contract that is getting a too large. If it
|
||||||
structure, you might find it useful to use the `#include` statement to split the
|
has a modular structure, you might find it useful to use the
|
||||||
contract up over multiple files.
|
`#include` statement to split the contract up over multiple files.
|
||||||
|
|
||||||
|
You take the code that you want to include and put it in a separate
|
||||||
You take the code that you want to include and put it in a separate file, for
|
file, for example `included.ligo`:
|
||||||
example `included.ligo`:
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
@ -23,7 +22,6 @@ const foo : int = 144
|
|||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo
|
||||||
|
|
||||||
// Demonstrate CameLIGO inclusion statements, see includer.mligo
|
// Demonstrate CameLIGO inclusion statements, see includer.mligo
|
||||||
|
|
||||||
let foo : int = 144
|
let foo : int = 144
|
||||||
@ -31,7 +29,6 @@ let foo : int = 144
|
|||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo
|
||||||
|
|
||||||
// Demonstrate ReasonLIGO inclusion statements, see includer.religo
|
// Demonstrate ReasonLIGO inclusion statements, see includer.religo
|
||||||
|
|
||||||
let foo : int = 144;
|
let foo : int = 144;
|
||||||
@ -46,7 +43,6 @@ And then you can include this code using the `#include` statement like so:
|
|||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo
|
||||||
|
|
||||||
#include "included.ligo"
|
#include "included.ligo"
|
||||||
|
|
||||||
const bar : int = foo
|
const bar : int = foo
|
||||||
@ -54,7 +50,6 @@ const bar : int = foo
|
|||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo
|
||||||
|
|
||||||
#include "included.mligo"
|
#include "included.mligo"
|
||||||
|
|
||||||
let bar : int = foo
|
let bar : int = foo
|
||||||
@ -62,7 +57,6 @@ let bar : int = foo
|
|||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo
|
||||||
|
|
||||||
#include "included.religo"
|
#include "included.religo"
|
||||||
|
|
||||||
let bar : int = foo;
|
let bar : int = foo;
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
|
||||||
block {
|
|
||||||
if amount > 0mutez then failwith("This contract does not accept tez") else skip
|
|
||||||
} with ((nil : list(operation)), unit);
|
|
@ -1,5 +0,0 @@
|
|||||||
const owner: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
|
||||||
function main (const p : unit ; const s : unit) : (list(operation) * unit) is
|
|
||||||
block {
|
|
||||||
if source =/= owner then failwith("This address can't call the contract") else skip
|
|
||||||
} with ((nil : list(operation)), unit);
|
|
@ -1,9 +0,0 @@
|
|||||||
#include "counter.types.ligo"
|
|
||||||
|
|
||||||
function counter (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
|
|
||||||
| Reset(n) -> 0
|
|
||||||
end)
|
|
@ -1,4 +0,0 @@
|
|||||||
type action is
|
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
@ -1,14 +0,0 @@
|
|||||||
#include "counter.types.ligo"
|
|
||||||
|
|
||||||
// Replace the following address with your deployed counter contract address
|
|
||||||
const address: address = ("KT19wgxcuXG9VH4Af5Tpm1vqEKdaMFpznXT3": address);
|
|
||||||
|
|
||||||
function proxy(const param: action; const store: unit): (list(operation) * unit)
|
|
||||||
is block {
|
|
||||||
const counter: contract(action) = get_contract(address);
|
|
||||||
// re-use the param passed to the proxy in the subsequent transaction
|
|
||||||
// e.g.:
|
|
||||||
// const mockParam: action = Increment(5);
|
|
||||||
const op: operation = transaction(param, 0mutez, counter);
|
|
||||||
const opList: list(operation) = list op; end;
|
|
||||||
} with (opList, store)
|
|
@ -1,9 +0,0 @@
|
|||||||
const today: timestamp = now;
|
|
||||||
const one_day: int = 86400;
|
|
||||||
const in_24_hrs: timestamp = today + one_day;
|
|
||||||
|
|
||||||
const today: timestamp = now;
|
|
||||||
const one_day: int = 86400;
|
|
||||||
const a_24_hrs_ago: timestamp = today - one_day;
|
|
||||||
|
|
||||||
const not_tommorow: bool = (now = in_24_hrs)
|
|
@ -5,17 +5,20 @@ title: Timestamps, Addresses
|
|||||||
|
|
||||||
## Timestamps
|
## Timestamps
|
||||||
|
|
||||||
Timestamps in LIGO, or in Michelson in general are available in smart contracts, while bakers baking the block (including the transaction in a block) are responsible for providing the given current timestamp for the contract.
|
LIGO features timestamps, as Michelson does, while bakers baking the
|
||||||
|
block (including the transaction in a block) are responsible for
|
||||||
|
providing the given current timestamp for the contract.
|
||||||
|
|
||||||
### Current time
|
### Current Time
|
||||||
|
|
||||||
You can obtain the current time using the built-in syntax specific expression, please be aware that it's up to the baker to set the current timestamp value.
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const today: timestamp = now;
|
const today : timestamp = now
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -30,21 +33,24 @@ let today: timestamp = Current.time;
|
|||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
> When running code with ligo CLI, the option `--predecessor-timestamp` allows you to control what `now` returns.
|
> When running code, the LIGO CLI option
|
||||||
|
> `--predecessor-timestamp` allows you to control what `now` returns.
|
||||||
|
|
||||||
### Timestamp arithmetic
|
### Timestamp Arithmetics
|
||||||
|
|
||||||
In LIGO, timestamps can be added with `int`(s), this enables you to set e.g. time constraints for your smart contracts like this:
|
In LIGO, timestamps can be added to integers, allowing you to set time
|
||||||
|
constraints on your smart contracts. Consider the following scenarios.
|
||||||
|
|
||||||
#### In 24 hours
|
#### In 24 hours
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
const today: timestamp = now;
|
const today : timestamp = now
|
||||||
const one_day: int = 86400;
|
const one_day : int = 86400
|
||||||
const in_24_hrs: timestamp = today + one_day;
|
const in_24_hrs : timestamp = today + one_day
|
||||||
const some_date: timestamp = ("2000-01-01T10:10:10Z" : timestamp);
|
const some_date : timestamp = ("2000-01-01T10:10:10Z" : timestamp)
|
||||||
const one_day_later: timestamp = some_date + one_day;
|
const one_day_later : timestamp = some_date + one_day
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -67,13 +73,14 @@ let one_day_later: timestamp = some_date + one_day;
|
|||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
#### 24 hours ago
|
#### 24 hours Ago
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const today: timestamp = now;
|
const today : timestamp = now
|
||||||
const one_day: int = 86400;
|
const one_day : int = 86400
|
||||||
const in_24_hrs: timestamp = today - one_day;
|
const in_24_hrs : timestamp = today - one_day
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -92,12 +99,13 @@ let in_24_hrs: timestamp = today - one_day;
|
|||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
### Comparing timestamps
|
### Comparing Timestamps
|
||||||
|
|
||||||
You can also compare timestamps using the same comparison operators as for numbers:
|
You can compare timestamps using the same comparison operators
|
||||||
|
applying to numbers.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const not_tommorow : bool = (now = in_24_hrs)
|
const not_tommorow : bool = (now = in_24_hrs)
|
||||||
```
|
```
|
||||||
@ -116,66 +124,83 @@ let not_tomorrow: bool = (Current.time == in_24_hrs);
|
|||||||
|
|
||||||
## Addresses
|
## Addresses
|
||||||
|
|
||||||
`address` is a LIGO datatype used for Tezos addresses (tz1, tz2, tz3, KT1, ...).
|
The `address` type in LIGO denotes Tezos addresses (tz1, tz2, tz3,
|
||||||
|
KT1, ...). Currently, addresses are created by casting a string to the
|
||||||
Here's how you can define an address:
|
`address` type. Beware of failures if the address is invalid. Consider
|
||||||
|
the following examples.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
const my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
const my_account : address =
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=d
|
```cameligo group=d
|
||||||
let my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)
|
let my_account : address =
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=d
|
```reasonligo group=d
|
||||||
let my_account: address = ("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address);
|
let my_account : address =
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address);
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Signatures
|
## Signatures
|
||||||
|
|
||||||
`signature` is a LIGO datatype used for Tezos signature (edsig, spsig).
|
The `signature` type in LIGO datatype is used for Tezos signatures
|
||||||
|
(edsig, spsig). Signatures are created by casting a string. Beware of
|
||||||
|
failure if the signature is invalid.
|
||||||
|
|
||||||
Here's how you can define a signature:
|
Here is how you can define a signature:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
const my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature);
|
const my_sig : signature =
|
||||||
|
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
||||||
|
signature)
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=e
|
```cameligo group=e
|
||||||
let my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature)
|
let my_sig : signature =
|
||||||
|
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
||||||
|
signature)
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=e
|
```reasonligo group=e
|
||||||
let my_signature: signature = ("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7": signature);
|
let my_sig : signature =
|
||||||
|
("edsigthTzJ8X7MPmNeEwybRAvdxS1pupqcM5Mk4uCuyZAe7uEk68YpuGDeViW8wSXMrCi5CwoNgqs8V2w8ayB5dMJzrYCHhD8C7" :
|
||||||
|
signature);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## keys
|
## Keys
|
||||||
|
|
||||||
`key` is a LIGO datatype used for Tezos public key.
|
The `key` type in LIGO is used for Tezos public keys. Do not confuse
|
||||||
|
them with map keys. Keys are made by casting strings. Beware of
|
||||||
|
failure if the key is invalid.
|
||||||
|
|
||||||
Here's how you can define a key:
|
Here is how you can define a key.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=f
|
```pascaligo group=f
|
||||||
const my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key);
|
const my_key : key =
|
||||||
|
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key)
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=f
|
```cameligo group=f
|
||||||
let my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key)
|
let my_key : key =
|
||||||
|
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key)
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=f
|
```reasonligo group=f
|
||||||
let my_key: key = ("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav": key);
|
let my_key : key =
|
||||||
|
("edpkuBknW28nW72KG6RoHtYW7p12T6GKc7nAbwYX5m8Wd9sDVC9yav" : key);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -4,6 +4,9 @@ title: Cheat Sheet
|
|||||||
---
|
---
|
||||||
<div class="cheatsheet">
|
<div class="cheatsheet">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Note that this table is not compiled before production and currently needs to be managed manually.
|
||||||
|
-->
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
@ -67,11 +70,42 @@ title: Cheat Sheet
|
|||||||
|Variants|<pre><code>type action =<br/>| Increment of int<br/>| Decrement of int</code></pre>|
|
|Variants|<pre><code>type action =<br/>| Increment of int<br/>| Decrement of int</code></pre>|
|
||||||
|Variant *(pattern)* matching|<pre><code>let a: action = Increment 5<br/>match a with<br/>| Increment n -> n + 1<br/>| Decrement n -> n - 1<br/></code></pre>|
|
|Variant *(pattern)* matching|<pre><code>let a: action = Increment 5<br/>match a with<br/>| Increment n -> n + 1<br/>| Decrement n -> n - 1<br/></code></pre>|
|
||||||
|Records|<pre><code>type person = {<br/> age: int ;<br/> name: string ;<br/>}<br/><br/>let john : person = {<br/> age = 18;<br/> name = "John Doe";<br/>}<br/><br/>let name: string = john.name</code></pre>|
|
|Records|<pre><code>type person = {<br/> age: int ;<br/> name: string ;<br/>}<br/><br/>let john : person = {<br/> age = 18;<br/> name = "John Doe";<br/>}<br/><br/>let name: string = john.name</code></pre>|
|
||||||
|Maps|<pre><code>type prices = (nat, tez) map<br/><br/>let prices : prices = Map.literal [<br/> (10n, 60mutez);<br/> (50n, 30mutez);<br/> (100n, 10mutez)<br/>]<br/><br/>let price: tez option = Map.find 50n prices<br/><br/>let prices: prices = Map.update 200n 5mutez prices</code></pre>|
|
|Maps|<pre><code>type prices = (nat, tez) map<br/><br/>let prices : prices = Map.literal [<br/> (10n, 60mutez);<br/> (50n, 30mutez);<br/> (100n, 10mutez)<br/>]<br/><br/>let price: tez option = Map.find_opt 50n prices<br/><br/>let prices: prices = Map.update 200n (Some 5mutez) prices</code></pre>|
|
||||||
|Contracts & Accounts|<pre><code>let destination_address : address = "tz1..."<br/>let contract : unit contract = <br/> Operation.get_contract destination_address</code></pre>|
|
|Contracts & Accounts|<pre><code>let destination_address : address = "tz1..."<br/>let contract : unit contract = <br/> Operation.get_contract destination_address</code></pre>|
|
||||||
|Transactions|<pre><code>let payment : operation = <br/> Operation.transaction (unit, receiver, amount)</code></pre>|
|
|Transactions|<pre><code>let payment : operation = <br/> Operation.transaction unit amount receiver</code></pre>|
|
||||||
|Exception/Failure|`failwith("Your descriptive error message for the user goes here.")`|
|
|Exception/Failure|`failwith("Your descriptive error message for the user goes here.")`|
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
|Primitive |Example|
|
||||||
|
|--- |---|
|
||||||
|
|Strings | `"Tezos"`|
|
||||||
|
|Characters | `"t"`|
|
||||||
|
|Integers | `42`, `7`|
|
||||||
|
|Natural numbers | `42n`, `7n`|
|
||||||
|
|Unit| `unit`|
|
||||||
|
|Boolean|<pre><code>let has_drivers_license: bool = false;<br/>let adult: bool = true;</code></pre> |
|
||||||
|
|Boolean Logic|<pre><code>(not true) = false = (false && true) = (false || false)</code></pre>|
|
||||||
|
|Mutez (micro tez)| `42mutez`, `7mutez` |
|
||||||
|
|Address | `("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address)`, `("KT1JepfBfMSqkQyf9B1ndvURghGsSB8YCLMD": address)`|
|
||||||
|
|Addition |`3 + 4`, `3n + 4n`|
|
||||||
|
|Multiplication & Division| `3 * 4`, `3n * 4n`, `10 / 5`, `10n / 5n`|
|
||||||
|
|Modulo| `10 mod 3`|
|
||||||
|
|Tuples| <pre><code>type name = (string, string);<br/>let winner: name = ("John", "Doe");<br/>let first_name: string = winner[0];<br/>let last_name: string = winner[1];</code></pre>|
|
||||||
|
|Types|`type age = int;`, `type name = string;` |
|
||||||
|
|Includes|```#include "library.mligo"```|
|
||||||
|
|Functions |<pre><code>let add = (a: int, b: int) : int => a + b; </code></pre>|
|
||||||
|
| If Statement | <pre><code>let new_id: int = if (age < 16) {<br/> failwith("Too young to drive."); <br/> } else { prev_id + 1; }</code></pre>|
|
||||||
|
|Options|<pre><code>type middle_name = option(string);<br/>let middle_name : middle_name = Some("Foo");<br/>let middle_name : middle_name = None;</code></pre>|
|
||||||
|
|Variable Binding | ```let age: int = 5;```|
|
||||||
|
|Type Annotations| ```("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address)```|
|
||||||
|
|Variants|<pre><code>type action =<br/>| Increment(int)<br/>| Decrement(int);</code></pre>|
|
||||||
|
|Variant *(pattern)* matching|<pre><code>let a: action = Increment(5);<br/>switch(a) {<br/>| Increment(n) => n + 1<br/>| Decrement(n) => n - 1;<br/> } <br/></code></pre>|
|
||||||
|
|Records|<pre><code>type person = {<br/> age: int,<br/> name: string<br/>}<br/><br/>let john : person = {<br/> age: 18,<br/> name: "John Doe"<br/>};<br/><br/>let name: string = john.name;</code></pre>|
|
||||||
|
|Maps|<pre><code>type prices = map(nat, tez);<br/><br/>let prices : prices = Map.literal([<br/> (10n, 60mutez),<br/> (50n, 30mutez),<br/> (100n, 10mutez)<br/>]);<br/><br/>let price: option(tez) = Map.find_opt(50n, prices);<br/><br/>let prices: prices = Map.update(200n, Some (5mutez), prices);</code></pre>|
|
||||||
|
|Contracts & Accounts|<pre><code>let destination_address : address = "tz1...";<br/>let contract : contract(unit) = <br/> Operation.get_contract(destination_address);</code></pre>|
|
||||||
|
|Transactions|<pre><code>let payment : operation = <br/> Operation.transaction (unit, amount, receiver);</code></pre>|
|
||||||
|
|Exception/Failure|`failwith("Your descriptive error message for the user goes here.");`|
|
||||||
|
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -23,9 +23,9 @@ The first issues will most likely be:
|
|||||||
>Tests are **really** important, we don’t have lots of them, and mostly regression ones. This can’t be stressed enough. Some features are missing not because we can’t add them, but because we don’t know as no tests tell us they are missing.
|
>Tests are **really** important, we don’t have lots of them, and mostly regression ones. This can’t be stressed enough. Some features are missing not because we can’t add them, but because we don’t know as no tests tell us they are missing.
|
||||||
|
|
||||||
## How
|
## How
|
||||||
Issues will be added to Gitlab tagged with `On-boarding and Front-End` / `Middle-End` / `Back-End` / `Everything`.
|
Issues will be added to GitLab tagged with `On-boarding and Front-End` / `Middle-End` / `Back-End` / `Everything`.
|
||||||
|
|
||||||
If you try to tackle an issue and you have **any** problem, please tell us by creating a new Gitlab issue, contacting us on Riot, on Discord, or even by mail!
|
If you try to tackle an issue and you have **any** problem, please tell us by creating a new GitLab issue, contacting us on Riot, on Discord, or even by mail!
|
||||||
|
|
||||||
Problems might include:
|
Problems might include:
|
||||||
* Installing the repository or the tools needed to work on it
|
* Installing the repository or the tools needed to work on it
|
||||||
|
@ -111,4 +111,4 @@ What if we want to write a test of our own? If the test is in the integration te
|
|||||||
1. Write a test contract which uses the new syntax or feature in [src/test/contracts](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts).
|
1. Write a test contract which uses the new syntax or feature in [src/test/contracts](https://gitlab.com/ligolang/ligo/tree/dev/src/test/contracts).
|
||||||
2. Write an integration test in [src/test/integration_tests.ml](https://gitlab.com/ligolang/ligo/blob/dev/src/test/integration_tests.ml) in the vein of existing tests, make sure you add it to the test runner that is currently located at the bottom of the file.
|
2. Write an integration test in [src/test/integration_tests.ml](https://gitlab.com/ligolang/ligo/blob/dev/src/test/integration_tests.ml) in the vein of existing tests, make sure you add it to the test runner that is currently located at the bottom of the file.
|
||||||
3. Write the feature, assuming it doesn't already exist. Build the resulting version of LIGO without errors.
|
3. Write the feature, assuming it doesn't already exist. Build the resulting version of LIGO without errors.
|
||||||
4. Run the test suite, see if your test(s) pass. If they do, you're probably done. If not it's time to go debugging.
|
4. Run the test suite, see if your test(s) pass. If they do, you're probably done. If not, it's time to go debugging.
|
||||||
|
@ -3,8 +3,8 @@ id: origin
|
|||||||
title: Origin
|
title: Origin
|
||||||
---
|
---
|
||||||
|
|
||||||
LIGO is a programming language that aims to provide developers with an uncomplicated and safe way to implement smart-contracts. Since it is being implemented for the Tezos blockchain LIGO compiles to Michelson—the native smart-contract language of Tezos.
|
LIGO is a programming language that aims to provide developers with an uncomplicated and safe way to implement smart contracts. Since it is being implemented for the Tezos blockchain LIGO compiles to Michelson—the native smart contract language of Tezos.
|
||||||
|
|
||||||
> Smart-contracts are programs that run within a blockchain network.
|
> Smart contracts are programs that run within a blockchain network.
|
||||||
|
|
||||||
LIGO was meant to be a language for developing Marigold on top of a hacky framework called Meta-Michelson. However, due to the attention received by the Tezos community, LIGO is now a standalone language being developed to support Tezos directly.
|
LIGO was meant to be a language for developing Marigold on top of a hacky framework called Meta-Michelson. However, due to the attention received by the Tezos community, LIGO is now a standalone language being developed to support Tezos directly.
|
@ -6,7 +6,7 @@ title: Philosophy
|
|||||||
To understand LIGO’s design choices it’s important to understand its philosophy. We have two main concerns in mind while building LIGO.
|
To understand LIGO’s design choices it’s important to understand its philosophy. We have two main concerns in mind while building LIGO.
|
||||||
|
|
||||||
## Safety
|
## Safety
|
||||||
Once a smart-contract is deployed, it will likely be impossible to change it. You must get it right on the first try, and LIGO should help as much as possible. There are multiple ways to make LIGO a safer language for smart-contracts.
|
Once a smart contract is deployed, it will likely be impossible to change it. You must get it right on the first try, and LIGO should help as much as possible. There are multiple ways to make LIGO a safer language for smart contracts.
|
||||||
|
|
||||||
### Automated Testing
|
### Automated Testing
|
||||||
Automated Testing is the process through which a program runs another program, and checks that this other program behaves correctly.
|
Automated Testing is the process through which a program runs another program, and checks that this other program behaves correctly.
|
||||||
@ -18,7 +18,7 @@ Static analysis is the process of having a program analyze another one.
|
|||||||
For instance, type systems are a kind of static analysis through which it is possible to find lots of bugs. LIGO already has a simple type system, and we plan to make it much stronger.
|
For instance, type systems are a kind of static analysis through which it is possible to find lots of bugs. LIGO already has a simple type system, and we plan to make it much stronger.
|
||||||
|
|
||||||
### Conciseness
|
### Conciseness
|
||||||
Writing less code gives you less room to introduce errors. That's why LIGO encourages writing lean rather than chunky smart-contracts.
|
Writing less code gives you less room to introduce errors. That's why LIGO encourages writing lean rather than chunky smart contracts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@ executable (see below). This manages the Docker bits for you.
|
|||||||
* Use the Docker image available at [Docker Hub](https://hub.docker.com/r/ligolang/ligo).
|
* Use the Docker image available at [Docker Hub](https://hub.docker.com/r/ligolang/ligo).
|
||||||
This lets you run multiple versions and keep your installation(s) self contained, but requires more familiarity with Docker.
|
This lets you run multiple versions and keep your installation(s) self contained, but requires more familiarity with Docker.
|
||||||
|
|
||||||
Sources for the image can be found on [Gitlab](https://gitlab.com/ligolang/ligo/blob/master/docker/Dockerfile).
|
Sources for the image can be found on [GitLab](https://gitlab.com/ligolang/ligo/blob/master/docker/Dockerfile).
|
||||||
If this is your first time using Docker, you probably want to set up a global LIGO executable as shown below.
|
If this is your first time using Docker, you probably want to set up a global LIGO executable as shown below.
|
||||||
|
|
||||||
### Setting up a globally available `ligo` executable
|
### Setting up a globally available `ligo` executable
|
||||||
|
|
||||||
> You can install additional ligo versions by replacing `next` with the required version number
|
> You can install additional ligo versions by replacing `next` with the desired version number
|
||||||
|
|
||||||
Download the latest binaries here: https://gitlab.com/ligolang/ligo/pipelines/85536879/builds or get the latest pre-release:
|
Download the latest binaries here: https://gitlab.com/ligolang/ligo/pipelines/85536879/builds or get the latest pre-release:
|
||||||
|
|
||||||
|
@ -1,12 +1,25 @@
|
|||||||
type action is
|
type storage is int
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
||||||
|
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
type parameter is
|
||||||
block { skip } with ((nil : list(operation)),
|
Increment of int
|
||||||
case p of
|
| Decrement of int
|
||||||
| Increment(n) -> s + n
|
| Reset
|
||||||
| Decrement(n) -> s - n
|
|
||||||
| Reset(n) -> 0
|
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
|
||||||
|
|
||||||
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
|
the smart contract parameter. *)
|
||||||
|
|
||||||
|
function main (const action : parameter; const store : storage) : return is
|
||||||
|
((nil : list (operation)), // No operations
|
||||||
|
case action of
|
||||||
|
Increment (n) -> add (store, n)
|
||||||
|
| Decrement (n) -> sub (store, n)
|
||||||
|
| Reset -> 0
|
||||||
end)
|
end)
|
23
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo
Normal file
23
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.mligo
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
type storage = int
|
||||||
|
|
||||||
|
type parameter =
|
||||||
|
Increment of int
|
||||||
|
| Decrement of int
|
||||||
|
| Reset
|
||||||
|
|
||||||
|
type return = operation list * storage
|
||||||
|
|
||||||
|
(* Two entrypoints *)
|
||||||
|
|
||||||
|
let add (store, delta : storage * int) : storage = store + delta
|
||||||
|
let sub (store, delta : storage * int) : storage = store - delta
|
||||||
|
|
||||||
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
|
the smart contract parameter. *)
|
||||||
|
|
||||||
|
let main (action, store : parameter * storage) : return =
|
||||||
|
([] : operation list), // No operations
|
||||||
|
(match action with
|
||||||
|
Increment (n) -> add (store, n)
|
||||||
|
| Decrement (n) -> sub (store, n)
|
||||||
|
| Reset -> 0)
|
26
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo
Normal file
26
gitlab-pages/docs/intro/src/what-and-why/ligo-counter.religo
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
type storage = int;
|
||||||
|
|
||||||
|
type parameter =
|
||||||
|
Increment (int)
|
||||||
|
| Decrement (int)
|
||||||
|
| Reset;
|
||||||
|
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
|
||||||
|
(* Two entrypoints *)
|
||||||
|
|
||||||
|
let add = ((store, delta) : (storage, int)) : storage => store + delta;
|
||||||
|
|
||||||
|
let sub = ((store, delta) : (storage, int)) : storage => store - delta;
|
||||||
|
|
||||||
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
|
the smart contract parameter. *)
|
||||||
|
|
||||||
|
let main = ((action, store) : (parameter, storage)) : return => {
|
||||||
|
(([] : list (operation)), // No operations
|
||||||
|
(switch (action) {
|
||||||
|
| Increment (n) => add ((store, n))
|
||||||
|
| Decrement (n) => sub ((store, n))
|
||||||
|
| Reset => 0
|
||||||
|
}))
|
||||||
|
};
|
@ -1,14 +1,73 @@
|
|||||||
---
|
---
|
||||||
id: what-and-why
|
id: what-and-why
|
||||||
title: What & Why
|
title: Michelson and LIGO
|
||||||
---
|
---
|
||||||
|
|
||||||
Before we get into what LIGO is and why LIGO needs to exist, let's take a look at what options the Tezos blockchain offers us out of the box. If you want to implement smart contracts natively on Tezos, you have to learn [Michelson](https://tezos.gitlab.io/whitedoc/michelson.html).
|
Before we get into what LIGO is and why LIGO needs to exist, let us
|
||||||
|
take a look at what options the Tezos blockchain offers us out of the
|
||||||
|
box. If you want to implement smart contracts natively on Tezos, you
|
||||||
|
have to learn
|
||||||
|
[Michelson](https://tezos.gitlab.io/whitedoc/michelson.html).
|
||||||
|
|
||||||
> 💡 The (Michelson) language is stack-based, with high level data types and primitives and strict static type checking.
|
**The rationale and design of Michelson**
|
||||||
|
|
||||||
|
The language native to the Tezos blockchain for writing smart
|
||||||
|
contracts is *Michelson*, a Domain-Specific Language (DSL) inspired by
|
||||||
|
Lisp and Forth. This unusual lineage aims at satisfying unusual
|
||||||
|
constraints, but entails some tensions in the design.
|
||||||
|
|
||||||
Here's an example of Michelson code:
|
First, to measure stepwise gas consumption, *Michelson is interpreted*.
|
||||||
|
|
||||||
|
On the one hand, to assess gas usage per instruction, instructions
|
||||||
|
should be simple, which points to low-level features (a RISC-like
|
||||||
|
language). On the other hand, it was originally thought that users
|
||||||
|
will want to write in Michelson instead of lowering a language to
|
||||||
|
Michelson, because the gas cost would otherwise be harder to
|
||||||
|
predict. This means that *high-level features* were deemed necessary
|
||||||
|
(like a restricted variant of Lisp lambdas, a way to encode algebraic
|
||||||
|
data types, as well as built-in sets, maps and lists).
|
||||||
|
|
||||||
|
To avoid ambiguous and otherwise misleading contracts, the layout of
|
||||||
|
Michelson contracts has been constrained (e.g., indentation, no
|
||||||
|
UTF-8), and a *canonical form* was designed and enforced when storing
|
||||||
|
contracts on the chain.
|
||||||
|
|
||||||
|
To reduce the size of the code, Michelson was designed as *a
|
||||||
|
stack-based language*, whence the lineage from Forth and other
|
||||||
|
concatenative languages like PostScript, Joy, Cat, Factor etc. (Java
|
||||||
|
bytecode would count too.)
|
||||||
|
|
||||||
|
Programs in those languages are *compact* because they assume an
|
||||||
|
implicit stack in which some input values are popped, and output
|
||||||
|
values are pushed, according to the current instruction being
|
||||||
|
executed.
|
||||||
|
|
||||||
|
*Each Michelson instruction modifies a prefix of the stack*, that is,
|
||||||
|
a segment starting at the top.
|
||||||
|
|
||||||
|
Whilst the types of Michelson instructions can be polymorphic, their
|
||||||
|
instantiations must be monomorphic, hence *Michelson instructions are
|
||||||
|
not first-class values* and cannot be partially interpreted.
|
||||||
|
|
||||||
|
This enables a simple *static type checking*, as opposed to a complex
|
||||||
|
type inference. It can be performed efficiently: *contract type
|
||||||
|
checking consumes gas*. Basically, type checking aims at validating
|
||||||
|
the composition of instructions, therefore is key to safely composing
|
||||||
|
contracts (concatenation, activations). Once a contract passes type
|
||||||
|
checking, it cannot fail due to inconsistent assumptions on the
|
||||||
|
storage and other values (there are no null values, no casts), but it
|
||||||
|
can still fail for other reasons: division by zero, token exhaustion,
|
||||||
|
gas exhaustion, or an explicit `FAILWITH` instruction. This property
|
||||||
|
is called *type safety*. Also, such a contract cannot remain stuck:
|
||||||
|
this is the *progress property*.
|
||||||
|
|
||||||
|
The existence of a formal type system for Michelson, of a formal
|
||||||
|
specification of its dynamic semantics (evaluation), of a Michelson
|
||||||
|
interpreter in Coq, of proofs in Coq of properties of some typical
|
||||||
|
contracts, all those achievements are instances of *formal methods in
|
||||||
|
Tezos*.
|
||||||
|
|
||||||
|
Here is an example of a Michelson contract.
|
||||||
|
|
||||||
**`counter.tz`**
|
**`counter.tz`**
|
||||||
```text
|
```text
|
||||||
@ -21,111 +80,160 @@ Here's an example of Michelson code:
|
|||||||
NIL operation ; PAIR } }
|
NIL operation ; PAIR } }
|
||||||
```
|
```
|
||||||
|
|
||||||
The contract above maintains an `int` in its storage. It has two entrypoints *(functions)* `add` and `sub` to modify it, and the default *entrypoint* of type unit will reset it to 0.
|
The contract above maintains an `int` as its storage. It has two
|
||||||
|
[entrypoints](https://tezos.gitlab.io/whitedoc/michelson.html#entrypoints),
|
||||||
|
`add` and `sub`, to modify it, and the `default` entrypoint of type
|
||||||
|
`unit` will reset it to `0`.
|
||||||
|
|
||||||
The contract itself contains three main parts:
|
The contract itself contains three sections:
|
||||||
|
- `parameter` - The argument provided by a transaction invoking the contract.
|
||||||
|
- `storage` - The type definition for the contract's data storage.
|
||||||
|
- `code` - Actual Michelson code that has the provided parameter and
|
||||||
|
the current storage value in its initial stack. It outputs in the
|
||||||
|
resulting stack a pair made of a list of operations and a new
|
||||||
|
storage value.
|
||||||
|
|
||||||
- `parameter` - Argument provided by a transaction invoking the contract
|
Michelson code consists of *instructions* like `IF_LEFT`, `PUSH ...`,
|
||||||
- `storage` - Type definition for the contract's data storage.
|
`UNPAIR` etc. that are composed sequentially in what is called a
|
||||||
- `code` - Actual Michelson code that has the provided parameter & the current storage value in its initial stack. It outputs a pair of operations and a new storage value as its resulting stack.
|
*sequence*. The implicit stack contains at all times the state of the
|
||||||
|
evaluation of the program, whilst the storage represents the
|
||||||
|
persistent state. If the contract execution is successful, the new
|
||||||
|
storage state will be committed to the chain and become visible to all
|
||||||
|
the nodes. Instructions are used to transform a prefix of the stack,
|
||||||
|
that is, the topmost part of it, for example, by duplicating its top
|
||||||
|
element, dropping it, subtracting the first two etc.
|
||||||
|
|
||||||
Michelson code consists of *instructions* like `IF_LEFT`, `PUSH ...`, `UNPAIR` that are bundled togeter in what is called a *sequence*. Stack represents an intermediate state of the program, while **storage represents a persistent state**. Instructions are used to modify the run-time stack in order to yield a desired stack value when the program terminates.
|
> 💡 A Michelson program running on the Tezos blockchain is meant to
|
||||||
|
> output a pair of values including a `list of operations` to include
|
||||||
|
> in a transaction, and a new `storage` value to persist on the chain.
|
||||||
|
|
||||||
> 💡 A Michelson program running on the Tezos blockchain is meant to output a pair of values including a `list of operations` to emit and a new `storage` value to persist
|
## Stack versus variables
|
||||||
|
|
||||||
## Differences between a stack and traditional variable management
|
Perhaps the biggest challenge when programming in Michelson is the
|
||||||
|
lack of *variables* to denote the data: the stack layout has to be
|
||||||
Stack management might be a little bit challanging, especially if you're coming from a *C-like language*. Let's implement a similar program in Javascript:
|
kept in mind when retrieving and storing data. For example, let us
|
||||||
|
implement a program in Javascript that is similar to the Michelson
|
||||||
|
above:
|
||||||
|
|
||||||
**`counter.js`**
|
**`counter.js`**
|
||||||
```javascript
|
```javascript
|
||||||
var storage = 0;
|
var storage = 0;
|
||||||
|
|
||||||
function add(a) {
|
function add (a) { storage += a; }
|
||||||
storage += a
|
function sub (a) { storage -= a; }
|
||||||
}
|
|
||||||
|
|
||||||
function sub(a) {
|
// We are calling this function "reset" instead of "default"
|
||||||
storage -= a
|
// because `default` is a Javascript keyword
|
||||||
}
|
|
||||||
|
|
||||||
// We're calling this function reset instead of default
|
function reset () { storage = 0; }
|
||||||
// because `default` is a javascript keyword
|
|
||||||
function reset() {
|
|
||||||
storage = 0;
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
In our javascript program the initial `storage` value is `0` and it can be modified by running the functions `add(a)`, `sub(a)` and `reset()`.
|
In our Javascript program the initial `storage` value is `0` and it
|
||||||
|
can be modified by calling `add (a)`, `sub (a)` and `reset ()`.
|
||||||
|
|
||||||
Unfortunately (???), we **can't run Javascript on the Tezos blockchain** at the moment. But we can choose LIGO, which will abstract the stack management and allow us to create readable, type-safe, and efficient smart contracts.
|
We cannot run Javascript on the Tezos blockchain, but we can choose
|
||||||
|
LIGO, which will abstract the stack management and allow us to create
|
||||||
|
readable, type-safe, and efficient smart contracts.
|
||||||
|
|
||||||
> 💡 You can try running the javascript program [here](https://codepen.io/maht0rz/pen/dyyvoPQ?editors=0012)
|
## LIGO for Programming Smart Contracts on Tezos
|
||||||
|
|
||||||
## C-like smart contracts instead of Michelson
|
Perhaps the most striking feature of LIGO is that it comes in
|
||||||
|
different concrete syntaxes, and even different programming
|
||||||
|
paradigms. In other words, LIGO is not defined by one syntax and one
|
||||||
|
paradigm, like imperative versus functional.
|
||||||
|
|
||||||
Let's take a look at a similar LIGO program. Don't worry if it's a little confusing at first; we'll explain all the syntax in the upcoming sections of the documentation.
|
- There is **PascaLIGO**, which is inspired by Pascal, hence is an
|
||||||
|
imperative language with lots of keywords, where values can be
|
||||||
|
locally mutated after they have been annotated with their types
|
||||||
|
(declaration).
|
||||||
|
|
||||||
|
- There is **CameLIGO**, which is inspired by the pure subset of
|
||||||
|
[OCaml](https://ocaml.org/), hence is a functional language with
|
||||||
|
few keywords, where values cannot be mutated, but still require
|
||||||
|
type annotations (unlike OCaml, whose compiler performs almost
|
||||||
|
full type inference).
|
||||||
|
|
||||||
|
- There is **ReasonLIGO**, which is inspired by the pure subset of
|
||||||
|
[ReasonML](https://reasonml.github.io/), which is based upon
|
||||||
|
OCaml.
|
||||||
|
|
||||||
|
Let us decline the same 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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
type action is
|
type storage is int
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
||||||
|
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
type parameter is
|
||||||
block { skip } with ((nil : list(operation)),
|
Increment of int
|
||||||
case p of
|
| Decrement of int
|
||||||
| Increment(n) -> s + n
|
| Reset
|
||||||
| Decrement(n) -> s - n
|
|
||||||
| Reset(n) -> 0
|
type return is list (operation) * storage
|
||||||
|
|
||||||
|
function main (const action : parameter; const store : storage) : return is
|
||||||
|
((nil : list (operation)),
|
||||||
|
case action of
|
||||||
|
Increment (n) -> store + n
|
||||||
|
| Decrement (n) -> store - n
|
||||||
|
| Reset -> 0
|
||||||
end)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
type action =
|
type storage = int
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
|
||||||
| Reset of unit
|
|
||||||
|
|
||||||
let main (p, s: action * int) : operation list * int =
|
type parameter =
|
||||||
let result =
|
Increment of int
|
||||||
match p with
|
| Decrement of int
|
||||||
| Increment n -> s + n
|
| Reset
|
||||||
| Decrement n -> s - n
|
|
||||||
| Reset n -> 0
|
type return = operation list * storage
|
||||||
in
|
|
||||||
(([]: operation list), result)
|
let main (action, store : parameter * storage) : return =
|
||||||
|
([] : operation list),
|
||||||
|
(match action with
|
||||||
|
Increment n -> store + n
|
||||||
|
| Decrement n -> store - n
|
||||||
|
| Reset -> 0)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
type action =
|
type storage = int;
|
||||||
| Increment(int)
|
|
||||||
| Decrement(int)
|
|
||||||
| Reset(unit);
|
|
||||||
|
|
||||||
let main = ((p,s): (action, int)) : (list(operation), int) => {
|
type parameter =
|
||||||
let result =
|
Increment (int)
|
||||||
switch (p) {
|
| Decrement (int)
|
||||||
| Increment(n) => s + n
|
| Reset;
|
||||||
| Decrement(n) => s - n
|
|
||||||
| Reset n => 0
|
type return = (list (operation), storage);
|
||||||
};
|
|
||||||
(([]: list(operation)), result);
|
let main = ((action, store): (parameter, storage)) : return => {
|
||||||
|
(([] : list (operation)),
|
||||||
|
(switch (action) {
|
||||||
|
| Increment (n) => store + n
|
||||||
|
| Decrement (n) => store - n
|
||||||
|
| Reset => 0}));
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
> 💡 You can find the Michelson compilation output of the contract -->
|
||||||
|
<!--above in **`ligo-counter.tz`** -->
|
||||||
|
|
||||||
> 💡 You can find the Michelson compilation output of the contract above in **`ligo-counter.tz`**
|
This LIGO contract behaves almost exactly* like the Michelson
|
||||||
|
contract we saw first, and it accepts the following LIGO expressions:
|
||||||
|
`Increment(n)`, `Decrement(n)` and `Reset`. Those serve as
|
||||||
|
`entrypoint` identification, same as `%add` `%sub` or `%default` in
|
||||||
|
the Michelson contract.
|
||||||
|
|
||||||
The LIGO contract behaves exactly* like the Michelson contract we've saw first, and it accepts the following LIGO expressions/values: `Increment(n)`, `Decrement(n)` and `Reset(n)`. Those serve as `entrypoint` identification, same as `%add` `%sub` or `%default` in the Michelson contract.
|
**The Michelson contract also checks if the `AMOUNT` sent is `0`*
|
||||||
|
|
||||||
**not exactly, the Michelson contract also checks if the `AMOUNT` sent is `0`*
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -141,7 +249,7 @@ For example **code snippets** for the *Types* subsection of this doc, can be fou
|
|||||||
### Exercises
|
### Exercises
|
||||||
Solutions to exercises can be found e.g. here: `gitlab-pages/docs/language-basics/exercises/types/**/solutions/**`
|
Solutions to exercises can be found e.g. here: `gitlab-pages/docs/language-basics/exercises/types/**/solutions/**`
|
||||||
|
|
||||||
### Running snippets / excercise solutions
|
### Running snippets / exercise solutions
|
||||||
In certain cases it makes sense to be able to run/evaluate the given snippet or a solution, usually there'll be an example command which you can use, such as:
|
In certain cases it makes sense to be able to run/evaluate the given snippet or a solution, usually there'll be an example command which you can use, such as:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -1,26 +1,24 @@
|
|||||||
---
|
---
|
||||||
id: boolean-if-else
|
id: boolean-if-else
|
||||||
title: Boolean, If, Else
|
title: Booleans and Conditionals
|
||||||
---
|
---
|
||||||
|
|
||||||
## Boolean
|
## Booleans
|
||||||
|
|
||||||
The type of a Boolean is `bool` and the possible values are `True` and `False`.
|
The type of a boolean value is `bool`. Here is how to define a boolean
|
||||||
|
value:
|
||||||
Here's how to define a boolean:
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
const a: bool = True;
|
const a : bool = True // Notice the capital letter
|
||||||
const b: bool = False;
|
const b : bool = False // Same.
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
let a : bool = true
|
let a : bool = true
|
||||||
let b : bool = false
|
let b : bool = false
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
let a : bool = true;
|
let a : bool = true;
|
||||||
@ -28,51 +26,53 @@ let b: bool = false;
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Comparing Values
|
||||||
|
|
||||||
## Comparing two values
|
In LIGO, only values of the same type can be compared. Moreover, not
|
||||||
|
all values of the same type can be compared, only those with
|
||||||
|
*comparable types*, which is a concept lifted from
|
||||||
|
Michelson. Comparable types include, for instance, `int`, `nat`,
|
||||||
|
`string`, `tez`, `timestamp`, `address`, etc. As an example of
|
||||||
|
non-comparable types: maps, sets or lists are not comparable: if you
|
||||||
|
wish to compare them, you will have to write your own comparison
|
||||||
|
function.
|
||||||
|
|
||||||
In LIGO, only values of the same type can be compared. We call these "comparable types." Comparable types include e.g. `int`, `nat`, `string`, `tez`, `timestamp`, `address`, ...
|
### Comparing Strings
|
||||||
|
|
||||||
### Comparing strings
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
const a: string = "Alice";
|
const a : string = "Alice"
|
||||||
const b: string = "Alice";
|
const b : string = "Alice"
|
||||||
// True
|
const c : bool = (a = b) // True
|
||||||
const c: bool = (a = b);
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let a : string = "Alice"
|
let a : string = "Alice"
|
||||||
let b : string = "Alice"
|
let b : string = "Alice"
|
||||||
// true
|
let c : bool = (a = b) // true
|
||||||
let c: bool = (a = b)
|
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let a : string = "Alice";
|
let a : string = "Alice";
|
||||||
let b : string = "Alice";
|
let b : string = "Alice";
|
||||||
(* true *)
|
let c : bool = (a == b); // true
|
||||||
let c: bool = (a == b);
|
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Comparing numbers
|
### Comparing numbers
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const a: int = 5;
|
const a : int = 5
|
||||||
const b: int = 4;
|
const b : int = 4
|
||||||
const c: bool = (a = b);
|
const c : bool = (a = b)
|
||||||
const d: bool = (a > b);
|
const d : bool = (a > b)
|
||||||
const e: bool = (a < b);
|
const e : bool = (a < b)
|
||||||
const f: bool = (a <= b);
|
const f : bool = (a <= b)
|
||||||
const g: bool = (a >= b);
|
const g : bool = (a >= b)
|
||||||
const h: bool = (a =/= b);
|
const h : bool = (a =/= b)
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
@ -99,107 +99,107 @@ let h: bool = (a != b);
|
|||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
### Comparing tez
|
### Comparing tez
|
||||||
|
|
||||||
> 💡 Comparing `tez` values is especially useful when dealing with an `amount` sent in a transaction.
|
> 💡 Comparing `tez` values is especially useful when dealing with an
|
||||||
|
> amount sent in a transaction.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
const a: tez = 5mutez;
|
const a : tez = 5mutez
|
||||||
const b: tez = 10mutez;
|
const b : tez = 10mutez
|
||||||
const c: bool = (a = b);
|
const c : bool = (a = b) // False
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=d
|
```cameligo group=d
|
||||||
let a : tez = 5mutez
|
let a : tez = 5mutez
|
||||||
let b : tez = 10mutez
|
let b : tez = 10mutez
|
||||||
// false
|
let c : bool = (a = b) // false
|
||||||
let c: bool = (a = b)
|
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=d
|
```reasonligo group=d
|
||||||
let a : tez = 5mutez;
|
let a : tez = 5mutez;
|
||||||
let b : tez = 10mutez;
|
let b : tez = 10mutez;
|
||||||
(* false *)
|
let c : bool = (a == b); // false
|
||||||
let c: bool = (a == b);
|
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Conditionals, if staments, and more
|
## Conditionals
|
||||||
|
|
||||||
Conditional logic is an important part of every real world program.
|
Conditional logic enables forking the control flow depending on the
|
||||||
|
state.
|
||||||
### If/else statements
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
const min_age: nat = 16n;
|
type magnitude is Small | Large // See variant types.
|
||||||
|
|
||||||
(*
|
function compare (const n : nat) : magnitude is
|
||||||
This function is really obnoxious, but it showcases
|
if n < 10n then Small (Unit) else Large (Unit) // Unit is needed for now.
|
||||||
how the if statement and it's syntax can be used.
|
|
||||||
|
|
||||||
Normally, you'd use `with (age > min_age)` instead.
|
|
||||||
*)
|
|
||||||
function is_adult(const age: nat): bool is
|
|
||||||
block {
|
|
||||||
var is_adult: bool := False;
|
|
||||||
if (age > min_age) then begin
|
|
||||||
is_adult := True;
|
|
||||||
end else begin
|
|
||||||
is_adult := False;
|
|
||||||
end
|
|
||||||
} with is_adult
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> You can run the function above with
|
You can run the `compare` function defined above using the LIGO compiler
|
||||||
> ```
|
like this:
|
||||||
> ligo run-function -s pascaligo src/if-else.ligo is_adult 21n
|
```shell
|
||||||
> ```
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/boolean-if-else/cond.ligo compare 21n'
|
||||||
|
# Outputs: Large (Unit)
|
||||||
|
```
|
||||||
|
|
||||||
|
When the branches of the conditional are not a single expression, as
|
||||||
|
above, we need a block:
|
||||||
|
|
||||||
|
```pascaligo skip
|
||||||
|
if x < y then
|
||||||
|
block {
|
||||||
|
const z : nat = x;
|
||||||
|
x := y; y := z
|
||||||
|
}
|
||||||
|
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
|
||||||
|
instead:
|
||||||
|
```pascaligo skip
|
||||||
|
if x < y then {
|
||||||
|
const z : nat = x;
|
||||||
|
x := y; y := z
|
||||||
|
}
|
||||||
|
else skip;
|
||||||
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=e
|
```cameligo group=e
|
||||||
let min_age: nat = 16n
|
type magnitude = Small | Large // See variant types.
|
||||||
|
|
||||||
(**
|
let compare (n : nat) : magnitude =
|
||||||
|
if n < 10n then Small else Large
|
||||||
This function is really obnoxious, but it showcases
|
|
||||||
how the if statement and it's syntax can be used.
|
|
||||||
|
|
||||||
Normally, you'd use `with (age > min_age)` instead.
|
|
||||||
|
|
||||||
*)
|
|
||||||
let is_adult (age: nat) : bool =
|
|
||||||
if (age > min_age) then true else false
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can run the `compare` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/boolean-if-else/cond.mligo compare 21n'
|
||||||
|
# Outputs: Large
|
||||||
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=e
|
```reasonligo group=e
|
||||||
let min_age: nat = 16n;
|
type magnitude = | Small | Large; // See variant types.
|
||||||
|
|
||||||
(**
|
let compare = (n : nat) : magnitude =>
|
||||||
|
if (n < 10n) { Small; } else { Large; };
|
||||||
This function is really obnoxious, but it showcases
|
|
||||||
how the if statement and it's syntax can be used.
|
|
||||||
|
|
||||||
Normally, you'd use `with (age > min_age)` instead.
|
|
||||||
|
|
||||||
*)
|
|
||||||
|
|
||||||
let is_adult = (age: nat): bool =>
|
|
||||||
if (age > min_age) {
|
|
||||||
true;
|
|
||||||
} else {
|
|
||||||
false;
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> You can run the function above with
|
You can run the `compare` function defined above using the LIGO compiler
|
||||||
> ```
|
like this:
|
||||||
> ligo run-function -s reasonligo src/if-else.religo is_adult 21n
|
```shell
|
||||||
> ```
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/boolean-if-else/cond.religo compare 21n'
|
||||||
|
# Outputs: Large
|
||||||
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
@ -3,131 +3,270 @@ id: functions
|
|||||||
title: Functions
|
title: Functions
|
||||||
---
|
---
|
||||||
|
|
||||||
Writing code is fun as long as it doesn't get out of hand. To make sure our code doesn't turn into spaghetti we can group some logic into functions.
|
LIGO functions are the basic building block of contracts. For example,
|
||||||
|
entrypoints are functions.
|
||||||
|
|
||||||
## Instruction blocks
|
## Declaring Functions
|
||||||
|
|
||||||
With `block`(s) you can wrap *instructions* and *expressions* into an isolated scope.
|
|
||||||
Each `block` needs to include at least one `instruction`, or a *placeholder* instruction called `skip`.
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
There are two ways in PascaLIGO to define functions: with or without a
|
||||||
|
*block*.
|
||||||
|
|
||||||
|
### Blocks
|
||||||
|
|
||||||
|
In PascaLIGO, *blocks* enable the sequential composition of
|
||||||
|
instructions into an isolated scope. Each block needs to include at
|
||||||
|
least one instruction.
|
||||||
|
|
||||||
```pascaligo skip
|
```pascaligo skip
|
||||||
// shorthand syntax
|
block { a := a + 1 }
|
||||||
block { skip }
|
|
||||||
// verbose syntax
|
|
||||||
begin
|
|
||||||
skip
|
|
||||||
end
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
If we need a placeholder, we use the instruction `skip` which leaves
|
||||||
|
the state unchanged. The rationale for `skip` instead of a truly
|
||||||
|
empty block is that it prevents you from writing an empty block by
|
||||||
|
mistake.
|
||||||
|
|
||||||
## Defining a function
|
```pascaligo skip
|
||||||
|
block { skip }
|
||||||
|
```
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
Blocks are more versatile than simply containing instructions: they
|
||||||
<!--Pascaligo-->
|
can also include *declarations* of values, like so:
|
||||||
|
|
||||||
Functions in PascaLIGO are defined using the `function` keyword followed by their `name`, `parameters` and `return` type definitions.
|
```pascaligo skip
|
||||||
|
block { const a : int = 1 }
|
||||||
|
```
|
||||||
|
|
||||||
Here's how you define a basic function that accepts two `ints` and returns a single `int`:
|
Functions in PascaLIGO are defined using the `function` keyword
|
||||||
|
followed by their `name`, `parameters` and `return` type definitions.
|
||||||
|
|
||||||
|
Here is how you define a basic function that computes the sum of two
|
||||||
|
integers:
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
function add (const a : int; const b : int) : int is
|
function add (const a : int; const b : int) : int is
|
||||||
begin
|
block {
|
||||||
const result: int = a + b;
|
const sum : int = a + b
|
||||||
end with result;
|
} with sum
|
||||||
```
|
```
|
||||||
|
|
||||||
The function body consists of two parts:
|
The function body consists of two parts:
|
||||||
|
|
||||||
- `block {<code>}` - logic of the function
|
- `block { <instructions and declarations> }` is the logic of the function;
|
||||||
- `with <value>` - the return value of the function
|
- `with <value>` is the value returned by the function.
|
||||||
|
|
||||||
#### Blockless functions
|
### Blockless functions
|
||||||
|
|
||||||
Functions that can contain all of their logic into a single instruction/expression, can be defined without the surrounding `block`.
|
Functions that can contain all of their logic into a single
|
||||||
Instead, you can inline the necessary logic directly, like this:
|
*expression* can be defined without the need of a block:
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
function identity (const n : int) : int is block { skip } with n // Bad! Empty block not needed!
|
||||||
|
|
||||||
|
function identity (const n : int) : int is n // Blockless
|
||||||
|
```
|
||||||
|
|
||||||
|
The value of the expression is implicitly returned by the
|
||||||
|
function. Another example is as follows:
|
||||||
|
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
function add (const a: int; const b : int) : int is a + b
|
function add (const a: int; const b : int) : int is a + b
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
You can call the function `add` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
Functions in CameLIGO are defined using the `let` keyword, like value bindings.
|
```shell
|
||||||
The difference is that after the value name a list of function parameters is provided,
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.ligo add '(1,2)'
|
||||||
along with a return type.
|
# Outputs: 3
|
||||||
|
|
||||||
CameLIGO is a little different from other syntaxes when it comes to function
|
|
||||||
parameters. In OCaml, functions can only take one parameter. To get functions
|
|
||||||
with multiple arguments like we're used to in traditional programming languages,
|
|
||||||
a technique called [currying](https://en.wikipedia.org/wiki/Currying) is used.
|
|
||||||
Currying essentially translates a function with multiple arguments into a series
|
|
||||||
of single argument functions, each returning a new function accepting the next
|
|
||||||
argument until every parameter is filled. This is useful because it means that
|
|
||||||
CameLIGO can support [partial application](https://en.wikipedia.org/wiki/Partial_application).
|
|
||||||
|
|
||||||
Currying is however *not* the preferred way to pass function arguments in CameLIGO.
|
|
||||||
While this approach is faithful to the original OCaml, it's costlier in Michelson
|
|
||||||
than naive function execution accepting multiple arguments. Instead for most
|
|
||||||
functions with more than one parameter we should place the arguments in a
|
|
||||||
[tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as a single
|
|
||||||
parameter.
|
|
||||||
|
|
||||||
Here's how you define a basic function that accepts two `ints` and returns an `int` as well:
|
|
||||||
|
|
||||||
```cameligo group=b
|
|
||||||
|
|
||||||
let add (a,b: int * int) : int = a + b
|
|
||||||
|
|
||||||
let add_curry (a: int) (b: int) : int = a + b
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The function body is a series of expressions, which are evaluated to give the return
|
<!--CameLIGO-->
|
||||||
value.
|
|
||||||
|
|
||||||
|
Functions in CameLIGO are defined using the `let` keyword, like other
|
||||||
|
values. The difference is that a succession of parameters is provided
|
||||||
|
after the value name, followed by the return type. This follows OCaml
|
||||||
|
syntax. For example:
|
||||||
|
```cameligo group=c
|
||||||
|
let add (a : int) (b : int) : int = a + b
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `add` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.mligo add '(1,2)'
|
||||||
|
# Outputs: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
CameLIGO is a little different from other syntaxes when it comes to
|
||||||
|
function parameters. In OCaml, functions can only take one
|
||||||
|
parameter. To get functions with multiple arguments like we are used
|
||||||
|
to in imperative programming languages, a technique called
|
||||||
|
[currying](https://en.wikipedia.org/wiki/Currying) is used. Currying
|
||||||
|
essentially translates a function with multiple arguments into a
|
||||||
|
series of single argument functions, each returning a new function
|
||||||
|
accepting the next argument until every parameter is filled. This is
|
||||||
|
useful because it means that CameLIGO supports
|
||||||
|
[partial application](https://en.wikipedia.org/wiki/Partial_application).
|
||||||
|
|
||||||
|
Currying is however *not* the preferred way to pass function arguments
|
||||||
|
in CameLIGO. While this approach is faithful to the original OCaml,
|
||||||
|
it is costlier in Michelson than naive function execution accepting
|
||||||
|
multiple arguments. Instead, for most functions with more than one
|
||||||
|
parameter, we should gather the arguments in a
|
||||||
|
[tuple](language-basics/sets-lists-tuples.md) and pass the tuple in as
|
||||||
|
a single parameter.
|
||||||
|
|
||||||
|
Here is how you define a basic function that accepts two integers and
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
You can run the `increment` function defined above using the LIGO
|
||||||
|
compiler like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/curry.mligo increment 5
|
||||||
|
# Outputs: 6
|
||||||
|
```
|
||||||
|
|
||||||
|
The function body is a single expression, whose value is returned.
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
Functions in ReasonLIGO are defined using the `let` keyword, like value bindings.
|
Functions in ReasonLIGO are defined using the `let` keyword, like
|
||||||
The difference is that after the value name a list of function parameters is provided,
|
other values. The difference is that a tuple of parameters is provided
|
||||||
along with a return type.
|
after the value name, with its type, then followed by the return type.
|
||||||
|
|
||||||
Here's how you define a basic function that accepts two `ints` and returns an `int` as well:
|
|
||||||
|
|
||||||
|
Here is how you define a basic function that sums two integers:
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let add = ((a, b): (int, int)) : int => a + b;
|
let add = ((a, b): (int, int)) : int => a + b;
|
||||||
```
|
```
|
||||||
|
|
||||||
The function body is a series of expressions, which are evaluated to give the return
|
You can call the function `add` defined above using the LIGO compiler
|
||||||
value.
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo run-function gitlab-pages/docs/language-basics/src/functions/blockless.religo add '(1,2)'
|
||||||
|
# Outputs: 3
|
||||||
|
```
|
||||||
|
|
||||||
|
As in CameLIGO and with blockless functions in PascaLIGO, the function
|
||||||
|
body is a single expression, whose value is returned.
|
||||||
|
|
||||||
|
If the body contains more than a single expression, you use block
|
||||||
|
between braces:
|
||||||
|
```reasonligo group=b
|
||||||
|
let myFun = ((x, y) : (int, int)) : int => {
|
||||||
|
let doubleX = x + x;
|
||||||
|
let doubleY = y + y;
|
||||||
|
doubleX + doubleY
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Anonymous functions
|
## Anonymous functions (a.k.a. lambdas)
|
||||||
|
|
||||||
Functions without a name, also known as anonymous functions are useful in cases when you want to pass the function as an argument or assign it to a key in a record/map.
|
It is possible to define functions without assigning them a name. They
|
||||||
|
are useful when you want to pass them as arguments, or assign them to
|
||||||
|
a key in a record or a map.
|
||||||
|
|
||||||
|
Here is how to define an anonymous function:
|
||||||
|
|
||||||
Here's how to define an anonymous function assigned to a variable `increment`, with it's appropriate function type signature.
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
function increment (const b : int) : int is
|
||||||
// a = 2
|
(function (const a : int) : int is a + 1) (b)
|
||||||
const a: int = increment(1);
|
const a : int = increment (1); // a = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check the value of `a` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.ligo a
|
||||||
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let increment : (int -> int) = fun (i: int) -> i + 1
|
let increment (b : int) : int = (fun (a : int) -> a + 1) b
|
||||||
|
let a : int = increment 1 // a = 2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check the value of `a` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.mligo a
|
||||||
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let increment: (int => int) = (i: int) => i + 1;
|
let increment = (b : int) : int => ((a : int) : int => a + 1) (b);
|
||||||
|
let a : int = increment (1); // a == 2
|
||||||
|
```
|
||||||
|
|
||||||
|
You can check the value of `a` defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
|
```shell
|
||||||
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/functions/anon.religo a
|
||||||
|
# Outputs: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo group=c
|
||||||
|
function incr_map (const l : list (int)) : list (int) is
|
||||||
|
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:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/functions/incr_map.ligo incr_map
|
||||||
|
"list [1;2;3]"
|
||||||
|
# Outputs: [ 2 ; 3 ; 4 ]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=c
|
||||||
|
let incr_map (l : int list) : int list =
|
||||||
|
List.map (fun (i : int) -> i + 1) l
|
||||||
|
```
|
||||||
|
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.mligo incr_map
|
||||||
|
"list [1;2;3]"
|
||||||
|
# Outputs: [ 2 ; 3 ; 4 ]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=c
|
||||||
|
let incr_map = (l : list (int)) : list (int) =>
|
||||||
|
List.map ((i : int) => i + 1, l);
|
||||||
|
```
|
||||||
|
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.religo incr_map
|
||||||
|
"list [1;2;3]"
|
||||||
|
# Outputs: [ 2 ; 3 ; 4 ]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,108 +3,246 @@ id: loops
|
|||||||
title: Loops
|
title: Loops
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## General Iteration
|
||||||
|
|
||||||
## While Loop
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
The PascaLIGO while loop should look familiar to users of imperative languages.
|
General iteration in PascaLIGO takes the shape of general loops, which
|
||||||
While loops are of the form `while <condition clause> <block>`, and evaluate
|
should be familiar to programmers of imperative languages as "while
|
||||||
their associated block until the condition evaluates to false.
|
loops". Those loops are of the form `while <condition> <block>`. Their
|
||||||
|
associated block is repeatedly evaluated until the condition becomes
|
||||||
|
true, or never evaluated if the condition is false at the start. The
|
||||||
|
loop never terminates if the condition never becomes true. Because we
|
||||||
|
are writing smart contracts on Tezos, when the condition of a "while"
|
||||||
|
loops fails to become true, the execution will run out of gas and stop
|
||||||
|
with a failure anyway.
|
||||||
|
|
||||||
> ⚠️ The current PascaLIGO while loop has semantics that have diverged from other LIGO syntaxes. The goal of LIGO is that the various syntaxes express the same semantics, so this will be corrected in future versions. For details on how loops will likely work after refactoring, see the CameLIGO tab of this example.
|
Here is how to compute the greatest common divisors of two natural
|
||||||
|
numbers by means of Euclid's algorithm:
|
||||||
|
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
function while_sum (var n : nat) : nat is block {
|
function gcd (var x : nat; var y : nat) : nat is
|
||||||
var i : nat := 0n ;
|
block {
|
||||||
var r : nat := 0n ;
|
if x < y then {
|
||||||
while i < n block {
|
const z : nat = x;
|
||||||
i := i + 1n;
|
x := y; y := z
|
||||||
r := r + i;
|
|
||||||
}
|
}
|
||||||
} with r
|
else skip;
|
||||||
|
var r : nat := 0n;
|
||||||
|
while y =/= 0n block {
|
||||||
|
r := x mod y;
|
||||||
|
x := y;
|
||||||
|
y := r
|
||||||
|
}
|
||||||
|
} with x
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `gcd` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/gcd.ligo gcd '(2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)'
|
||||||
|
# Outputs: +12
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
`Loop.fold_while` is a fold operation that takes an initial value of a certain type
|
CameLIGO is a functional language where user-defined values are
|
||||||
and then iterates on it until a condition is reached. The auxillary function
|
constant, therefore it makes no sense in CameLIGO to feature loops,
|
||||||
that does the fold returns either boolean true or boolean false to indicate
|
which we understand as syntactic constructs where the state of a
|
||||||
whether the fold should continue or not. The initial value must match the input
|
stopping condition is mutated, as with "while" loops in PascaLIGO.
|
||||||
parameter of the auxillary function, and the auxillary should return type `(bool * input)`.
|
|
||||||
|
|
||||||
`continue` and `stop` are provided as syntactic sugar for the return values.
|
Instead, CameLIGO implements a *folded operation* by means of a
|
||||||
|
predefined function named `Loop.fold_while`. It takes an initial value
|
||||||
|
of a certain type, called an *accumulator*, and repeatedly calls a
|
||||||
|
given function, called *folded function*, that takes that
|
||||||
|
accumulator and returns the next value of the accumulator, until a
|
||||||
|
condition is met and the fold stops with the final value of the
|
||||||
|
accumulator. The iterated function needs to have a special type: if
|
||||||
|
the type of the accumulator is `t`, then it must have the type `bool *
|
||||||
|
t` (not simply `t`). It is the boolean value that denotes whether the
|
||||||
|
stopping condition has been reached.
|
||||||
|
|
||||||
```cameligo
|
Here is how to compute the greatest common divisors of two natural
|
||||||
let aux (i: int) : bool * int =
|
numbers by means of Euclid's algorithm:
|
||||||
if i < 100 then continue (i + 1) else stop i
|
|
||||||
|
|
||||||
let counter_simple (n: int) : int =
|
```cameligo group=a
|
||||||
Loop.fold_while aux n
|
let iter (x,y : nat * nat) : bool * (nat * nat) =
|
||||||
|
if y = 0n then false, (x,y) else true, (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
|
||||||
|
```
|
||||||
|
|
||||||
|
To ease the writing and reading of the iterated functions (here,
|
||||||
|
`iter`), two predefined functions are provided: `continue` and `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)
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
You can call the function `gcd` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/gcd.mligo gcd (2n*2n*3n*11n, 2n*2n*2n*3n*3n*5n*7n)'
|
||||||
|
# Outputs: +12
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
`Loop.fold_while` is a fold operation that takes an initial value of a certain type
|
ReasonLIGO is a functional language where user-defined values are
|
||||||
and then iterates on it until a condition is reached. The auxillary function
|
constant, therefore it makes no sense in ReasonLIGO to feature loops,
|
||||||
that does the fold returns either boolean true or boolean false to indicate
|
which we understand as syntactic constructs where the state of a
|
||||||
whether the fold should continue or not. The initial value must match the input
|
stopping condition is mutated, as with "while" loops in PascaLIGO.
|
||||||
parameter of the auxillary function, and the auxillary should return type `(bool, input)`.
|
|
||||||
|
|
||||||
`continue` and `stop` are provided as syntactic sugar for the return values.
|
Instead, ReasonLIGO features a *fold operation* as a predefined
|
||||||
|
function named `Loop.fold_while`. It takes an initial value of a
|
||||||
|
certain type, called an *accumulator*, and repeatedly calls a given
|
||||||
|
function, called *iterated function*, that takes that accumulator and
|
||||||
|
returns the next value of the accumulator, until a condition is met
|
||||||
|
and the fold stops with the final value of the accumulator. The
|
||||||
|
iterated function needs to have a special type: if the type of the
|
||||||
|
accumulator is `t`, then it must have the type `bool * t` (not simply
|
||||||
|
`t`). It is the boolean value that denotes whether the stopping
|
||||||
|
condition has been reached.
|
||||||
|
|
||||||
```reasonligo
|
Here is how to compute the greatest common divisors of two natural
|
||||||
let aux = (i: int): (bool, int) =>
|
numbers by means of Euclid's algorithm:
|
||||||
if (i < 100) {
|
|
||||||
continue(i + 1);
|
```reasonligo group=a
|
||||||
} else {
|
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
|
||||||
stop(i);
|
if (y == 0n) { (false, (x,y)); } else { (true, (y, x mod y)); };
|
||||||
|
|
||||||
|
let gcd = ((x,y) : (nat, nat)) : nat => {
|
||||||
|
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
|
||||||
|
let (x,y) = Loop.fold_while (iter, (x,y));
|
||||||
|
x
|
||||||
};
|
};
|
||||||
|
|
||||||
let counter_simple = (n: int): int => Loop.fold_while(aux, n);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To ease the writing and reading of the iterated functions (here,
|
||||||
|
`iter`), two predefined functions are provided: `continue` and `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)); };
|
||||||
|
|
||||||
|
let gcd = ((x,y) : (nat, nat)) : nat => {
|
||||||
|
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
|
||||||
|
let (x,y) = Loop.fold_while (iter, (x,y));
|
||||||
|
x
|
||||||
|
};
|
||||||
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## For Loop
|
## Bounded Loops
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
In addition to general loops, PascaLIGO features a specialised kind of
|
||||||
<!--Pascaligo-->
|
*loop to iterate over bounded intervals*. These loops are familiarly
|
||||||
|
known as "for loops" and they have the form `for <variable assignment>
|
||||||
|
to <upper bound> <block>`, as found in imperative languages.
|
||||||
|
|
||||||
To iterate over a range of integers you use a loop of the form `for <variable assignment> to <integer> <block>`.
|
Consider how to sum the natural numbers up to `n`:
|
||||||
|
|
||||||
```pascaligo
|
```pascaligo group=c
|
||||||
function for_sum (var n : nat) : int is block {
|
function sum (var n : nat) : int is block {
|
||||||
var acc : int := 0;
|
var acc : int := 0;
|
||||||
for i := 1 to int(n)
|
for i := 1 to int (n) block {
|
||||||
begin
|
acc := acc + i
|
||||||
acc := acc + i;
|
}
|
||||||
end
|
|
||||||
} with acc
|
} with acc
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
(Please do not use that function: there exists a closed form formula.)
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
You can call the function `sum` defined above using the LIGO compiler
|
||||||
<!--Pascaligo-->
|
like so:
|
||||||
|
```shell
|
||||||
PascaLIGO for loops can also iterate through the contents of a collection. This is
|
ligo run-function
|
||||||
done with a loop of the form `for <element var> in <collection type> <collection var> <block>`.
|
gitlab-pages/docs/language-basics/src/loops/sum.ligo sum 7n
|
||||||
|
# Outputs: 28
|
||||||
```pascaligo
|
|
||||||
function for_collection_list (var nee : unit) : (int * string) is block {
|
|
||||||
var acc : int := 0;
|
|
||||||
var st : string := "to";
|
|
||||||
var mylist : list(int) := list 1; 1; 1 end;
|
|
||||||
for x in list mylist
|
|
||||||
begin
|
|
||||||
acc := acc + x;
|
|
||||||
st := st ^ "to";
|
|
||||||
end
|
|
||||||
} with (acc, st)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
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 <element var> in <collection type> <collection var>
|
||||||
|
<block>`, where `<collection type>` is any of the following keywords:
|
||||||
|
`list`, `set` or `map`.
|
||||||
|
|
||||||
|
Here is an example where the integers in a list are summed up.
|
||||||
|
|
||||||
|
```pascaligo group=d
|
||||||
|
function sum_list (var l : list (int)) : int is block {
|
||||||
|
var total : int := 0;
|
||||||
|
for i in list l block {
|
||||||
|
total := total + i
|
||||||
|
}
|
||||||
|
} with total
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `sum_list` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_list
|
||||||
|
'list [1;2;3]'
|
||||||
|
# Outputs: 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is an example where the integers in a set are summed up.
|
||||||
|
|
||||||
|
```pascaligo group=d
|
||||||
|
function sum_set (var s : set (int)) : int is block {
|
||||||
|
var total : int := 0;
|
||||||
|
for i in set s block {
|
||||||
|
total := total + i
|
||||||
|
}
|
||||||
|
} with total
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `sum_set` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_set
|
||||||
|
'set [1;2;3]'
|
||||||
|
# Outputs: 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Loops over maps are actually loops over the bindings of the map, that
|
||||||
|
is, a pair key-value noted `key -> value` (or any other
|
||||||
|
variables). Given a map from strings to integers, here is how to sum
|
||||||
|
all the integers and concatenate all the strings.
|
||||||
|
|
||||||
|
Here is an example where the keys are concatenated and the values are
|
||||||
|
summed up.
|
||||||
|
|
||||||
|
```pascaligo group=d
|
||||||
|
function sum_map (var m : map (string, int)) : string * int is block {
|
||||||
|
var string_total : string := "";
|
||||||
|
var int_total : int := 0;
|
||||||
|
for key -> value in map m block {
|
||||||
|
string_total := string_total ^ key;
|
||||||
|
int_total := int_total + value
|
||||||
|
}
|
||||||
|
} with (string_total, int_total)
|
||||||
|
```
|
||||||
|
|
||||||
|
You can call the function `sum_map` defined above using the LIGO compiler
|
||||||
|
like so:
|
||||||
|
```shell
|
||||||
|
ligo run-function
|
||||||
|
gitlab-pages/docs/language-basics/src/loops/collection.ligo sum_map
|
||||||
|
'map ["1"->1; "2"->2; "3"->3]'
|
||||||
|
# Outputs: ( "123", 6 )
|
||||||
|
```
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,119 +7,150 @@ LIGO offers three built-in numerical types: `int`, `nat` and `tez`.
|
|||||||
|
|
||||||
## Addition
|
## Addition
|
||||||
|
|
||||||
Addition in ligo is acomplished by using the `+` operator. Some type constraints apply; for example you can't add `tez + nat`.
|
Addition in LIGO is accomplished by means of the `+` infix
|
||||||
|
operator. Some type constraints apply, for example you cannot add a
|
||||||
|
value of type `tez` to a value of type `nat`.
|
||||||
|
|
||||||
In the following example you can find a series of arithmetic operations, including various numerical types. However, some bits of the example won't compile because adding an `int` to a `nat` produces an `int`, not a `nat`. Similiar rules apply for `tez`:
|
In the following example you can find a series of arithmetic
|
||||||
|
operations, including various numerical types. However, some bits
|
||||||
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
// int + int produces int
|
// int + int yields int
|
||||||
const a: int = 5 + 10;
|
const a : int = 5 + 10
|
||||||
// nat + int produces int
|
|
||||||
const b: int = 5n + 10;
|
// nat + int yields int
|
||||||
// tez + tez produces tez
|
const b : int = 5n + 10
|
||||||
const c: tez = 5mutez + 10mutez;
|
|
||||||
// you can't add tez + int or tez + nat, this won't compile
|
// tez + tez yields tez
|
||||||
// const d: tez = 5mutez + 10n;
|
const c : tez = 5mutez + 10mutez
|
||||||
// two nats produce a nat
|
|
||||||
const e: nat = 5n + 10n;
|
//tez + int or tez + nat is invalid
|
||||||
// nat + int produces an int, this won't compile
|
// const d : tez = 5mutez + 10n
|
||||||
|
|
||||||
|
// two nats yield a nat
|
||||||
|
const e : nat = 5n + 10n
|
||||||
|
|
||||||
|
// nat + int yields an int: invalid
|
||||||
// const f : nat = 5n + 10;
|
// const f : nat = 5n + 10;
|
||||||
const g: int = 1_000_000;
|
|
||||||
|
const g : int = 1_000_000
|
||||||
```
|
```
|
||||||
|
|
||||||
> Pro tip: you can use underscores for readability when defining large numbers
|
> Pro tip: you can use underscores for readability when defining large
|
||||||
|
> numbers:
|
||||||
>
|
>
|
||||||
>```pascaligo
|
>```pascaligo
|
||||||
>const g: int = 1_000_000;
|
> const sum : tez = 100_000mutez
|
||||||
>```
|
>```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```cameligo group=a
|
```cameligo group=a
|
||||||
// int + int produces int
|
// int + int yields int
|
||||||
let a : int = 5 + 10
|
let a : int = 5 + 10
|
||||||
// nat + int produces int
|
|
||||||
|
// nat + int yields int
|
||||||
let b : int = 5n + 10
|
let b : int = 5n + 10
|
||||||
// tez + tez produces tez
|
|
||||||
|
// tez + tez yields tez
|
||||||
let c : tez = 5mutez + 10mutez
|
let c : tez = 5mutez + 10mutez
|
||||||
// you can't add tez + int or tez + nat, this won't compile
|
|
||||||
// const d: tez = 5mutez + 10n
|
// tez + int or tez + nat is invalid
|
||||||
// two nats produce a nat
|
// let d : tez = 5mutez + 10n
|
||||||
|
|
||||||
|
// two nats yield a nat
|
||||||
let e : nat = 5n + 10n
|
let e : nat = 5n + 10n
|
||||||
// nat + int produces an int, this won't compile
|
|
||||||
// const f: nat = 5n + 10
|
// nat + int yields an int: invalid
|
||||||
|
// let f : nat = 5n + 10
|
||||||
|
|
||||||
let g : int = 1_000_000
|
let g : int = 1_000_000
|
||||||
```
|
```
|
||||||
|
|
||||||
> Pro tip: you can use underscores for readability when defining large numbers
|
> Pro tip: you can use underscores for readability when defining large
|
||||||
|
> numbers:
|
||||||
>
|
>
|
||||||
>```cameligo
|
>```cameligo
|
||||||
>let g: int = 1_000_000;
|
>let sum : tez = 100_000mutez
|
||||||
>```
|
>```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```reasonligo group=a
|
```reasonligo group=a
|
||||||
(* int + int produces int *)
|
// int + int yields int
|
||||||
let a : int = 5 + 10;
|
let a : int = 5 + 10;
|
||||||
(* nat + int produces int *)
|
|
||||||
|
// nat + int yields int
|
||||||
let b : int = 5n + 10;
|
let b : int = 5n + 10;
|
||||||
(* tez + tez produces tez *)
|
|
||||||
|
// tez + tez yields tez
|
||||||
let c : tez = 5mutez + 10mutez;
|
let c : tez = 5mutez + 10mutez;
|
||||||
(* you can't add tez + int or tez + nat, this won't compile:
|
|
||||||
let d: tez = 5mutez + 10n; *)
|
// tez + int or tez + nat is invalid:
|
||||||
(* two nats produce a nat *)
|
// let d : tez = 5mutez + 10n;
|
||||||
|
|
||||||
|
// two nats yield a nat
|
||||||
let e : nat = 5n + 10n;
|
let e : nat = 5n + 10n;
|
||||||
(* nat + int produces an int, this won't compile:
|
|
||||||
let f: nat = 5n + 10; *)
|
// nat + int yields an int: invalid
|
||||||
|
// let f : nat = 5n + 10;
|
||||||
|
|
||||||
let g : int = 1_000_000;
|
let g : int = 1_000_000;
|
||||||
```
|
```
|
||||||
|
|
||||||
> Pro tip: you can use underscores for readability when defining large numbers
|
> Pro tip: you can use underscores for readability when defining large
|
||||||
>
|
> numbers:
|
||||||
>```reasonligo
|
>```reasonligo
|
||||||
>let g: int = 1_000_000;
|
>let sum : tex = 100_000mutez;
|
||||||
>```
|
>```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Subtraction
|
## Subtraction
|
||||||
|
|
||||||
The simpliest substraction looks like this:
|
Subtraction looks as follows.
|
||||||
|
|
||||||
> ⚠️ Even when subtracting two `nats`, the result is an `int`
|
> ⚠️ Even when subtracting two `nats`, the result is an `int`
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=b
|
```pascaligo group=b
|
||||||
const a: int = 5 - 10;
|
const a : int = 5 - 10
|
||||||
// substraction of two nats, yields an int
|
|
||||||
const b: int = 5n - 2n;
|
// Subtraction of two nats yields an int
|
||||||
// won't compile, result is an int, not a nat
|
const b : int = 5n - 2n
|
||||||
// const c: nat = 5n - 2n;
|
|
||||||
const d: tez = 5mutez - 1mutez;
|
// Therefore the following is invalid
|
||||||
|
// const c : nat = 5n - 2n
|
||||||
|
|
||||||
|
const d : tez = 5mutez - 1mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=b
|
```cameligo group=b
|
||||||
let a : int = 5 - 10
|
let a : int = 5 - 10
|
||||||
// substraction of two nats, yields an int
|
|
||||||
|
// Subtraction of two nats yields an int
|
||||||
let b : int = 5n - 2n
|
let b : int = 5n - 2n
|
||||||
// won't compile, result is an int, not a nat
|
|
||||||
// const c: nat = 5n - 2n
|
// Therefore the following is invalid
|
||||||
|
// let c : nat = 5n - 2n
|
||||||
|
|
||||||
let d : tez = 5mutez - 1mutez
|
let d : tez = 5mutez - 1mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo group=b
|
```reasonligo group=b
|
||||||
let a : int = 5 - 10;
|
let a : int = 5 - 10;
|
||||||
(* substraction of two nats, yields an int *)
|
|
||||||
|
// Subtraction of two nats yields an int
|
||||||
let b : int = 5n - 2n;
|
let b : int = 5n - 2n;
|
||||||
(* won't compile, result is an int, not a nat *)
|
|
||||||
(* let c: nat = 5n - 2n; *)
|
// Therefore the following is invalid
|
||||||
|
// let c : nat = 5n - 2n;
|
||||||
|
|
||||||
let d : tez = 5mutez - 1mutez;
|
let d : tez = 5mutez - 1mutez;
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -131,20 +162,22 @@ let d: tez = 5mutez - 1mutez;
|
|||||||
You can multiply values of the same type, such as:
|
You can multiply values of the same type, such as:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
```pascaligo group=c
|
```pascaligo group=c
|
||||||
const a: int = 5 * 5;
|
const a : int = 5 * 5
|
||||||
const b: nat = 5n * 5n;
|
const b : nat = 5n * 5n
|
||||||
// you can also multiply `nat` and `tez`
|
|
||||||
const c: tez = 5n * 5mutez;
|
// You can also multiply `nat` and `tez`
|
||||||
|
const c : tez = 5n * 5mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo group=c
|
```cameligo group=c
|
||||||
let a : int = 5 * 5
|
let a : int = 5 * 5
|
||||||
let b : nat = 5n * 5n
|
let b : nat = 5n * 5n
|
||||||
// you can also multiply `nat` and `tez`
|
|
||||||
|
// You can also multiply `nat` and `tez`
|
||||||
let c : tez = 5n * 5mutez
|
let c : tez = 5n * 5mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -152,25 +185,25 @@ let c: tez = 5n * 5mutez
|
|||||||
```reasonligo group=c
|
```reasonligo group=c
|
||||||
let a : int = 5 * 5;
|
let a : int = 5 * 5;
|
||||||
let b : nat = 5n * 5n;
|
let b : nat = 5n * 5n;
|
||||||
(* you can also multiply `nat` and `tez` *)
|
|
||||||
|
// You can also multiply `nat` and `tez`
|
||||||
let c : tez = 5n * 5mutez;
|
let c : tez = 5n * 5mutez;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Division
|
## Division
|
||||||
|
|
||||||
In LIGO you can divide `int`, `nat`, and `tez`. Here's how:
|
In LIGO you can divide `int`, `nat`, and `tez`. Here is how:
|
||||||
|
|
||||||
> ⚠️ Division of two `tez` values results into a `nat`
|
> ⚠️ Division of two `tez` values results into a `nat`
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=d
|
```pascaligo group=d
|
||||||
const a: int = 10 / 3;
|
const a : int = 10 / 3
|
||||||
const b: nat = 10n / 3n;
|
const b : nat = 10n / 3n
|
||||||
const c: nat = 10mutez / 3mutez;
|
const c : nat = 10mutez / 3mutez
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
@ -191,13 +224,19 @@ let c: nat = 10mutez / 3mutez;
|
|||||||
|
|
||||||
## From `int` to `nat` and back
|
## From `int` to `nat` and back
|
||||||
|
|
||||||
You can *cast* an `int` to a `nat` and vice versa, here's how:
|
You can *cast* an `int` to a `nat` and vice versa. Here is how:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo group=e
|
```pascaligo group=e
|
||||||
const a: int = int(1n);
|
const a : int = int (1n)
|
||||||
const b: nat = abs(1);
|
const b : nat = abs (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=e
|
||||||
|
let a : int = int (1n)
|
||||||
|
let b : nat = abs (1)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
@ -207,3 +246,28 @@ let b: nat = abs(1);
|
|||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Checking a `nat`
|
||||||
|
|
||||||
|
You can check if a value is a `nat` by using a predefined cast
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo group=e
|
||||||
|
const is_a_nat : option (nat) = is_nat (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=e
|
||||||
|
let is_a_nat : nat option = Michelson.is_nat (1)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=e
|
||||||
|
let is_a_nat : option (nat) = Michelson.is_nat (1);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +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)
|
@ -0,0 +1,4 @@
|
|||||||
|
type magnitude = Small | Large // See variant types
|
||||||
|
|
||||||
|
let compare (n : nat) : magnitude =
|
||||||
|
if n < 10n then Small else Large
|
@ -0,0 +1,4 @@
|
|||||||
|
type magnitude = | Small | Large; // See variant types
|
||||||
|
|
||||||
|
let compare = (n : nat) : magnitude =>
|
||||||
|
if (n < 10n) { Small; } else { Large; };
|
@ -1,4 +0,0 @@
|
|||||||
function add(const a: int; const b: int): int is
|
|
||||||
begin
|
|
||||||
const result: int = a + b;
|
|
||||||
end with result;
|
|
@ -1,3 +1,4 @@
|
|||||||
const increment : (int -> int) = (function (const i : int) : int is i + 1);
|
function increment (const b : int) : int is
|
||||||
// a = 2
|
(function (const a : int) : int is a + 1) (b)
|
||||||
const a: int = increment(1);
|
|
||||||
|
const a : int = increment (1); // a = 2
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
let increment (b : int) : int = (fun (a : int) -> a + 1) b
|
||||||
|
let a : int = increment 1 // a = 2
|
@ -0,0 +1,2 @@
|
|||||||
|
let increment = (b : int) : int => ((a : int) : int => a + 1)(b);
|
||||||
|
let a : int = increment (1); // a = 2
|
@ -0,0 +1 @@
|
|||||||
|
let add (a : int) (b : int) : int = a + b
|
@ -0,0 +1 @@
|
|||||||
|
let add = ((a, b): (int, int)) : int => a + b;
|
@ -0,0 +1,3 @@
|
|||||||
|
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
|
@ -0,0 +1,5 @@
|
|||||||
|
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)
|
@ -0,0 +1,4 @@
|
|||||||
|
let increment (b : int) : int = (fun (a : int) -> a + 1) b
|
||||||
|
|
||||||
|
let incr_map (l : int list) : int list =
|
||||||
|
List.map (fun (i : int) -> i + 1) l
|
@ -0,0 +1,4 @@
|
|||||||
|
let increment = (b : int) : int => ((a : int) : int => a + 1)(b);
|
||||||
|
|
||||||
|
let incr_map = (l : list (int)) : list (int) =>
|
||||||
|
List.map ((i : int) => i + 1, l);
|
22
gitlab-pages/docs/language-basics/src/loops/collection.ligo
Normal file
22
gitlab-pages/docs/language-basics/src/loops/collection.ligo
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
function sum_list (var l : list (int)) : int is block {
|
||||||
|
var total : int := 0;
|
||||||
|
for i in list l block {
|
||||||
|
total := total + i
|
||||||
|
}
|
||||||
|
} with total
|
||||||
|
|
||||||
|
function sum_set (var s : set (int)) : int is block {
|
||||||
|
var total : int := 0;
|
||||||
|
for i in set s block {
|
||||||
|
total := total + i
|
||||||
|
}
|
||||||
|
} with total
|
||||||
|
|
||||||
|
function sum_map (var m : map (string, int)) : string * int is block {
|
||||||
|
var string_total : string := "";
|
||||||
|
var int_total : int := 0;
|
||||||
|
for key -> value in map m block {
|
||||||
|
string_total := string_total ^ key;
|
||||||
|
int_total := int_total + value
|
||||||
|
}
|
||||||
|
} with (string_total, int_total)
|
14
gitlab-pages/docs/language-basics/src/loops/gcd.ligo
Normal file
14
gitlab-pages/docs/language-basics/src/loops/gcd.ligo
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
function gcd (var x : nat; var y : nat) : nat is block {
|
||||||
|
if x < y then
|
||||||
|
block {
|
||||||
|
const z : nat = x;
|
||||||
|
x := y; y := z
|
||||||
|
}
|
||||||
|
else skip;
|
||||||
|
var r : nat := 0n;
|
||||||
|
while y =/= 0n block {
|
||||||
|
r := x mod y;
|
||||||
|
x := y;
|
||||||
|
y := r
|
||||||
|
}
|
||||||
|
} with x
|
7
gitlab-pages/docs/language-basics/src/loops/gcd.mligo
Normal file
7
gitlab-pages/docs/language-basics/src/loops/gcd.mligo
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
let iter (x,y : nat * nat) : bool * (nat * nat) =
|
||||||
|
if y = 0n then stop (x,y) else continue (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
|
7
gitlab-pages/docs/language-basics/src/loops/gcd.religo
Normal file
7
gitlab-pages/docs/language-basics/src/loops/gcd.religo
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
let iter = ((x,y) : (nat, nat)) : (bool, (nat, nat)) =>
|
||||||
|
if (y == 0n) { stop ((x,y)); } else { continue ((y, x mod y)); };
|
||||||
|
|
||||||
|
let gcd = ((x,y) : (nat, nat)) : nat =>
|
||||||
|
let (x,y) = if (x < y) { (y,x); } else { (x,y); };
|
||||||
|
let (x,y) = Loop.fold_while (iter, (x,y));
|
||||||
|
x;
|
6
gitlab-pages/docs/language-basics/src/loops/sum.ligo
Normal file
6
gitlab-pages/docs/language-basics/src/loops/sum.ligo
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function sum (var n : nat) : int is block {
|
||||||
|
var acc : int := 0;
|
||||||
|
for i := 1 to int (n) block {
|
||||||
|
acc := acc + i
|
||||||
|
}
|
||||||
|
} with acc
|
@ -0,0 +1,10 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is
|
||||||
|
block {
|
||||||
|
patch p with record [x = p.x + vec.dx];
|
||||||
|
patch p with record [y = p.y + vec.dy]
|
||||||
|
} with p
|
@ -0,0 +1,9 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is
|
||||||
|
block {
|
||||||
|
patch p with record [x = p.x + vec.dx; y = p.y + vec.dy]
|
||||||
|
} with p
|
@ -0,0 +1,8 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is block {
|
||||||
|
const p : point = p with record [x = p.x + vec.dx; y = p.y + vec.dy]
|
||||||
|
} with p
|
@ -0,0 +1,7 @@
|
|||||||
|
type point is record [x : int; y : int; z : int]
|
||||||
|
type vector is record [dx : int; dy : int]
|
||||||
|
|
||||||
|
const origin : point = record [x = 0; y = 0; z = 0]
|
||||||
|
|
||||||
|
function xy_translate (var p : point; const vec : vector) : point is
|
||||||
|
p with record [x = p.x + vec.dx; y = p.y + vec.dy]
|
@ -0,0 +1,7 @@
|
|||||||
|
type point = {x : int; y : int; z : int}
|
||||||
|
type vector = {dx : int; dy : int}
|
||||||
|
|
||||||
|
let origin : point = {x = 0; y = 0; z = 0}
|
||||||
|
|
||||||
|
let xy_translate (p, vec : point * vector) : point =
|
||||||
|
{p with x = p.x + vec.dx; y = p.y + vec.dy}
|
@ -0,0 +1,7 @@
|
|||||||
|
type point = {x : int, y : int, z : int};
|
||||||
|
type vector = {dx : int, dy : int};
|
||||||
|
|
||||||
|
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};
|
@ -1,13 +0,0 @@
|
|||||||
// int + int produces int
|
|
||||||
const a: int = 5 + 10;
|
|
||||||
// nat + int produces int
|
|
||||||
const b: int = 5n + 10;
|
|
||||||
// tez + tez produces tez
|
|
||||||
const c: tez = 5mutez + 10mutez;
|
|
||||||
// you can't add tez + int or tez + nat, this won't compile
|
|
||||||
// const d: tez = 5mutez + 10n;
|
|
||||||
// two nats produce a nat
|
|
||||||
const e: nat = 5n + 10n;
|
|
||||||
// nat + int produces an int, this won't compile
|
|
||||||
// const f: nat = 5n + 10;
|
|
||||||
const g: int = 1_000_000;
|
|
@ -1,2 +0,0 @@
|
|||||||
const a: int = int(1n);
|
|
||||||
const b: nat = abs(1);
|
|
@ -1,5 +0,0 @@
|
|||||||
const a: int = 10 / 3;
|
|
||||||
const b: nat = 10n / 3n;
|
|
||||||
const c: nat = 10mutez / 3mutez;
|
|
||||||
|
|
||||||
const d: int = 10 / 5 / 2 * 5;
|
|
@ -1,3 +0,0 @@
|
|||||||
const a: int = 5 * 5;
|
|
||||||
const b: nat = 5n * 5n;
|
|
||||||
const c: tez = 5n * 5mutez;
|
|
@ -1,6 +0,0 @@
|
|||||||
const a: int = 5 - 10;
|
|
||||||
// substraction of two nats, yields an int
|
|
||||||
const b: int = 5n - 2n;
|
|
||||||
// won't compile, result is an int, not a nat
|
|
||||||
// const c: nat = 5n - 2n;
|
|
||||||
const d: tez = 5mutez - 1mutez;
|
|
@ -0,0 +1,12 @@
|
|||||||
|
const my_list : list (int) = list [1; 2; 2] // The head is 1
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
function sum (const acc : int; const i : int): int is acc + i
|
||||||
|
|
||||||
|
const sum_of_elements : int = list_fold (sum, my_list, 0)
|
@ -0,0 +1,28 @@
|
|||||||
|
type int_set is set (int)
|
||||||
|
|
||||||
|
const my_set : int_set = set [3; 2; 2; 1]
|
||||||
|
|
||||||
|
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 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]
|
||||||
|
} with s
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
function loop (const s : set (int)) : int is block {
|
||||||
|
var sum : int := 0;
|
||||||
|
for element in set s block {
|
||||||
|
sum := sum + element
|
||||||
|
}
|
||||||
|
} with sum
|
@ -0,0 +1,16 @@
|
|||||||
|
type int_set = int set
|
||||||
|
|
||||||
|
let my_set : int_set =
|
||||||
|
Set.add 3 (Set.add 2 (Set.add 2 (Set.add 1 (Set.empty : int set))))
|
||||||
|
|
||||||
|
let contains_3 : bool = Set.mem 3 my_set
|
||||||
|
|
||||||
|
let set_size : nat = Set.size my_set
|
||||||
|
|
||||||
|
let larger_set : int_set = Set.add 4 my_set
|
||||||
|
|
||||||
|
let smaller_set : int_set = Set.remove 3 my_set
|
||||||
|
|
||||||
|
let sum (acc, i : int * int) : int = acc + i
|
||||||
|
|
||||||
|
let sum_of_elements : int = Set.fold sum my_set 0
|
@ -0,0 +1,16 @@
|
|||||||
|
type int_set = set (int);
|
||||||
|
|
||||||
|
let my_set : int_set =
|
||||||
|
Set.add (3, Set.add (2, Set.add (2, Set.add (1, Set.empty : set (int)))));
|
||||||
|
|
||||||
|
let contains_3 : bool = Set.mem (3, my_set);
|
||||||
|
|
||||||
|
let set_size : nat = Set.size (my_set);
|
||||||
|
|
||||||
|
let larger_set : int_set = Set.add (4, my_set);
|
||||||
|
|
||||||
|
let smaller_set : int_set = Set.remove (3, my_set);
|
||||||
|
|
||||||
|
let sum = ((acc, i) : (int, int)) : int => acc + i;
|
||||||
|
|
||||||
|
let sum_of_elements : int = Set.fold (sum, my_set, 0);
|
@ -1 +0,0 @@
|
|||||||
const a: string = string_concat("Hello ", "World");
|
|
@ -1,2 +0,0 @@
|
|||||||
type animalBreed is string;
|
|
||||||
const dogBreed : animalBreed = "Saluki";
|
|
@ -1,19 +0,0 @@
|
|||||||
// alias two types
|
|
||||||
type account is address;
|
|
||||||
type numberOfTransactions is nat;
|
|
||||||
// accountData consists of a record with two fields (balance, numberOfTransactions)
|
|
||||||
type accountData is record
|
|
||||||
balance: tez;
|
|
||||||
numberOfTransactions: numberOfTransactions;
|
|
||||||
end
|
|
||||||
// our ledger / accountBalances is a map of account <-> accountData
|
|
||||||
type accountBalances is map(account, accountData);
|
|
||||||
|
|
||||||
// pseudo-JSON representation of our map
|
|
||||||
// { "tz1...": {balance: 10mutez, numberOfTransactions: 5n} }
|
|
||||||
const ledger: accountBalances = map
|
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> record
|
|
||||||
balance = 10mutez;
|
|
||||||
numberOfTransactions = 5n;
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,6 +1,7 @@
|
|||||||
// accountBalances is a simple type, a map of address <-> tez
|
// The type accountBalances denotes maps from addresses to tez
|
||||||
type accountBalances is map(address, tez);
|
|
||||||
|
|
||||||
const ledger: accountBalances = map
|
type account_balances is map (address, tez)
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> 10mutez
|
|
||||||
end
|
const ledger : account_balances =
|
||||||
|
map
|
||||||
|
[("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez]
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
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
|
||||||
|
end
|
@ -0,0 +1,6 @@
|
|||||||
|
type coin = Head | Tail
|
||||||
|
|
||||||
|
let flip (c : coin) : coin =
|
||||||
|
match c with
|
||||||
|
Head -> Tail
|
||||||
|
| Tail -> Head
|
@ -0,0 +1,7 @@
|
|||||||
|
type coin = | Head | Tail;
|
||||||
|
|
||||||
|
let flip = (c : coin) : coin =>
|
||||||
|
switch (c) {
|
||||||
|
| Head => Tail
|
||||||
|
| Tail => Head
|
||||||
|
};
|
@ -1,7 +1,4 @@
|
|||||||
// won't work, use const for global values instead
|
|
||||||
// var four: int = 4;
|
|
||||||
|
|
||||||
function add (const a : int; const b : int) : int is
|
function add (const a : int; const b : int) : int is
|
||||||
block {
|
block {
|
||||||
var c : int := a + b;
|
var c : int := a + b
|
||||||
} with c
|
} with c
|
@ -0,0 +1,2 @@
|
|||||||
|
let add (a : int) (b : int) : int =
|
||||||
|
let c : int = a + b in c
|
@ -0,0 +1 @@
|
|||||||
|
let age : int = 25
|
@ -3,14 +3,12 @@ id: strings
|
|||||||
title: Strings
|
title: Strings
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Strings are defined using the built-in `string` type like this:
|
Strings are defined using the built-in `string` type like this:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```
|
```
|
||||||
const a: string = "Hello Alice";
|
const a : string = "Hello Alice"
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```
|
```
|
||||||
@ -23,24 +21,21 @@ let a: string = "Hello Alice";
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Concatenating strings
|
## Concatenating Strings
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
Strings can be concatenated using the `^` operator.
|
Strings can be concatenated using the `^` operator.
|
||||||
|
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
const name: string = "Alice";
|
const name : string = "Alice"
|
||||||
const greeting: string = "Hello";
|
const greeting : string = "Hello"
|
||||||
// Hello Alice
|
const full_greeting : string = greeting ^ " " ^ name
|
||||||
const full_greeting: string = greeting ^ " " ^ name;
|
|
||||||
// Hello Alice! (alternatively)
|
|
||||||
const full_greeting_exclamation: string = string_concat(full_greeting, "!");
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
Strings can be concatenated using the `^` operator.
|
Strings can be concatenated using the `^` operator.
|
||||||
|
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
let name : string = "Alice"
|
let name : string = "Alice"
|
||||||
let greeting : string = "Hello"
|
let greeting : string = "Hello"
|
||||||
let full_greeting : string = greeting ^ " " ^ name
|
let full_greeting : string = greeting ^ " " ^ name
|
||||||
@ -48,7 +43,7 @@ let full_greeting: string = greeting ^ " " ^ name
|
|||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
Strings can be concatenated using the `++` operator.
|
Strings can be concatenated using the `++` operator.
|
||||||
|
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
let name : string = "Alice";
|
let name : string = "Alice";
|
||||||
let greeting : string = "Hello";
|
let greeting : string = "Hello";
|
||||||
let full_greeting : string = greeting ++ " " ++ name;
|
let full_greeting : string = greeting ++ " " ++ name;
|
||||||
@ -56,51 +51,49 @@ let full_greeting: string = greeting ++ " " ++ name;
|
|||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
## Slicing strings
|
## Slicing Strings
|
||||||
|
|
||||||
Strings can be sliced using the syntax specific built-in built-in function:
|
Strings can be sliced using a built-in function:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=b
|
||||||
const name: string = "Alice";
|
const name : string = "Alice"
|
||||||
// slice = "A"
|
const slice : string = string_slice (0n, 1n, name)
|
||||||
const slice: string = string_slice(0n, 1n, name);
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=b
|
||||||
let name : string = "Alice"
|
let name : string = "Alice"
|
||||||
let slice : string = String.slice 0n 1n name
|
let slice : string = String.slice 0n 1n name
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=b
|
||||||
let name : string = "Alice";
|
let name : string = "Alice";
|
||||||
let slice : string = String.slice (0n, 1n, name);
|
let slice : string = String.slice (0n, 1n, name);
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
> ⚠️ Notice that the `offset` and slice `length` are `nats`
|
> ⚠️ Notice that the offset and length of the slice are natural numbers.
|
||||||
|
|
||||||
## Aquiring the length of a string
|
## Length of Strings
|
||||||
|
|
||||||
The length of a string can be found using the syntax specific built-in function:
|
The length of a string can be found using a built-in function:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=c
|
||||||
const name: string = "Alice";
|
const name : string = "Alice"
|
||||||
// length = 5
|
const length : nat = size (name) // length = 5
|
||||||
const length: nat = size(name);
|
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=c
|
||||||
let name : string = "Alice"
|
let name : string = "Alice"
|
||||||
let length: nat = String.size name
|
let length : nat = String.size name // length = 5
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=c
|
||||||
let name : string = "Alice";
|
let name : string = "Alice";
|
||||||
let length: nat = String.size(name);
|
let length : nat = String.size (name); // length == 5
|
||||||
```
|
```
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,38 +3,44 @@ id: tezos-specific
|
|||||||
title: Tezos Domain-Specific Operations
|
title: Tezos Domain-Specific Operations
|
||||||
---
|
---
|
||||||
|
|
||||||
LIGO is a language for writing Tezos smart contracts. It would be a little odd if
|
LIGO is a programming language for writing Tezos smart contracts. It
|
||||||
it didn't have any Tezos specific functions. This page will tell you about them.
|
would be a little odd if it did not have any Tezos specific
|
||||||
|
functions. This page will tell you about them.
|
||||||
|
|
||||||
## Pack and Unpack
|
## Pack and Unpack
|
||||||
|
|
||||||
Michelson provides the `PACK` and `UNPACK` instructions for data serialization.
|
Michelson provides the `PACK` and `UNPACK` instructions for data
|
||||||
`PACK` converts Michelson data structures to a binary format, and `UNPACK`
|
serialization. The former converts Michelson data structures into a
|
||||||
reverses it. This functionality can be accessed from within LIGO.
|
binary format, and the latter reverses that transformation. This
|
||||||
|
functionality can be accessed from within LIGO.
|
||||||
|
|
||||||
> ⚠️ `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.
|
> ⚠️ `PACK` and `UNPACK` are Michelson instructions that are intended
|
||||||
|
> to be used by people that really know what they are doing. There are
|
||||||
|
> several risks and failure cases, such as unpacking a lambda from an
|
||||||
|
> untrusted source or casting the result to the wrong type. Do not use
|
||||||
|
> the corresponding LIGO functions without doing your homework first.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
function id_string (const p : string) : option (string) is block {
|
function id_string (const p : string) : option (string) is block {
|
||||||
const packed : bytes = bytes_pack(p) ;
|
const packed : bytes = bytes_pack (p)
|
||||||
} with (bytes_unpack (packed) : option (string))
|
} with (bytes_unpack (packed) : option (string))
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
let id_string (p : string) : string option =
|
let id_string (p : string) : string option =
|
||||||
let packed: bytes = Bytes.pack p in
|
let packed: bytes = Bytes.pack p in
|
||||||
((Bytes.unpack packed): string option)
|
(Bytes.unpack packed : string option)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
let id_string = (p : string) : option (string) => {
|
let id_string = (p : string) : option (string) => {
|
||||||
let packed : bytes = Bytes.pack (p);
|
let packed : bytes = Bytes.pack (p);
|
||||||
((Bytes.unpack(packed)): option(string));
|
(Bytes.unpack(packed) : option (string));
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -42,41 +48,36 @@ let id_string = (p: string) : option(string) => {
|
|||||||
|
|
||||||
## Hashing Keys
|
## Hashing Keys
|
||||||
|
|
||||||
It's often desirable to hash a public key. In Michelson, certain data structures
|
It is often desirable to hash a public key. In Michelson, certain data
|
||||||
such as maps will not allow the use of the `key` type. Even if this weren't the case
|
structures such as maps will not allow the use of the `key` type. Even
|
||||||
hashes are much smaller than keys, and storage on blockchains comes at a cost premium.
|
if this were not the case, hashes are much smaller than keys, and
|
||||||
You can hash keys with the `key_hash` type and associated built in function.
|
storage on blockchains comes at a cost premium. You can hash keys with
|
||||||
|
a predefined functions returning a value of type `key_hash`.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=b
|
||||||
function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block {
|
function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is
|
||||||
|
block {
|
||||||
var ret : bool := False;
|
var ret : bool := False;
|
||||||
var kh2 : key_hash := crypto_hash_key (k2);
|
var kh2 : key_hash := crypto_hash_key (k2);
|
||||||
if kh1 = kh2 then ret := True else skip;
|
if kh1 = kh2 then ret := True else skip
|
||||||
} with (ret, kh2)
|
} with (ret, kh2)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=b
|
||||||
let check_hash_key (kh1, k2 : key_hash * key) : bool * key_hash =
|
let check_hash_key (kh1, k2 : key_hash * key) : bool * key_hash =
|
||||||
let kh2 : key_hash = Crypto.hash_key k2 in
|
let kh2 : key_hash = Crypto.hash_key k2 in
|
||||||
if kh1 = kh2
|
if kh1 = kh2 then true, kh2 else false, kh2
|
||||||
then (true, kh2)
|
|
||||||
else (false, kh2)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=b
|
||||||
let check_hash_key = ((kh1, k2) : (key_hash, key)) : (bool, key_hash) => {
|
let check_hash_key = ((kh1, k2) : (key_hash, key)) : (bool, key_hash) => {
|
||||||
let kh2 : key_hash = Crypto.hash_key (k2);
|
let kh2 : key_hash = Crypto.hash_key (k2);
|
||||||
if (kh1 == kh2) {
|
if (kh1 == kh2) { (true, kh2); } else { (false, kh2); }
|
||||||
(true, kh2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
(false, kh2);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -84,17 +85,20 @@ let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => {
|
|||||||
|
|
||||||
## Checking Signatures
|
## Checking Signatures
|
||||||
|
|
||||||
Sometimes a contract will want to check that a message has been signed by a
|
Sometimes a contract will want to check that a message has been signed
|
||||||
particular key. For example, a point-of-sale system might want a customer to
|
by a particular key. For example, a point-of-sale system might want a
|
||||||
sign a transaction so it can be processed asynchronously. You can do this in LIGO
|
customer to sign a transaction so it can be processed
|
||||||
using the `key` and `signature` types.
|
asynchronously. You can do this in LIGO using the `key` and
|
||||||
|
`signature` types.
|
||||||
|
|
||||||
> ⚠️ 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.
|
> ⚠️ 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 is not... private anymore.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=c
|
||||||
function check_signature
|
function check_signature
|
||||||
(const pk : key;
|
(const pk : key;
|
||||||
const signed : signature;
|
const signed : signature;
|
||||||
@ -103,41 +107,43 @@ function check_signature
|
|||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=c
|
||||||
let check_signature (pk, signed, msg : key * signature * bytes) : bool =
|
let check_signature (pk, signed, msg : key * signature * bytes) : bool =
|
||||||
Crypto.check pk signed msg
|
Crypto.check pk signed msg
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=c
|
||||||
let check_signature = ((pk, signed, msg): (key, signature, bytes)) : bool => {
|
let check_signature =
|
||||||
|
((pk, signed, msg) : (key, signature, bytes)) : bool =>
|
||||||
Crypto.check (pk, signed, msg);
|
Crypto.check (pk, signed, msg);
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Getting The Contract's Own Address
|
## Contract's Own Address
|
||||||
|
|
||||||
Often you want to get the address of the contract being executed. You can do it with
|
Often you want to get the address of the contract being executed. You
|
||||||
`self_address`.
|
can do it with `self_address`.
|
||||||
|
|
||||||
> ⚠️ Due to limitations in Michelson, self_address in a contract is only allowed at the entry-point level. Using it in a utility function will cause an error.
|
> ⚠️ 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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
<!--PascaLIGO-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=d
|
||||||
const current_addr : address = self_address;
|
const current_addr : address = self_address
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=d
|
||||||
let current_addr : address = Current.self_address
|
let current_addr : address = Current.self_address
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=d
|
||||||
let current_addr : address = Current.self_address;
|
let current_addr : address = Current.self_address;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -3,146 +3,270 @@ id: types
|
|||||||
title: Types
|
title: Types
|
||||||
---
|
---
|
||||||
|
|
||||||
LIGO is strongly and statically typed. This means that the compiler checks your program at compilation time and makes sure there won't be any type related runtime errors. LIGO types are built on top of Michelson's type system.
|
*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
|
||||||
|
your data. This is called *type checking*.
|
||||||
|
|
||||||
|
LIGO types are built on top of Michelson's type system.
|
||||||
|
|
||||||
## Built-in types
|
## Built-in types
|
||||||
|
|
||||||
For quick referrence, you can find all the built-in types [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L35).
|
For quick reference, you can find all the built-in types [here](https://gitlab.com/ligolang/ligo/blob/dev/src/passes/operators/operators.ml#L35).
|
||||||
|
|
||||||
## Type aliases
|
## Type aliases
|
||||||
|
|
||||||
Type aliasing is great for creating a readable / maintainable smart contract. One well typed type/variable is worth a thousand words. For example we can choose to *alias* a string as an animal breed - this will allow us to comunicate our intent with added clarity.
|
*Type aliasing* consists in renaming a given type, when the context
|
||||||
|
calls for a more precise name. This increases readability and
|
||||||
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
type animalBreed is string;
|
type breed is string
|
||||||
const dogBreed : animalBreed = "Saluki";
|
const dog_breed : breed = "Saluki"
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
type animal_breed = string
|
type breed = string
|
||||||
let dog_breed: animal_breed = "Saluki"
|
let dog_breed : breed = "Saluki"
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
type animal_breed = string;
|
type breed = string;
|
||||||
let dog_breed: animal_breed = "Saluki";
|
let dog_breed : breed = "Saluki";
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Simple types
|
> The above type definitions are aliases, which means that `breed` and
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
> `string` are interchangable in all contexts.
|
||||||
<!--Pascaligo-->
|
|
||||||
```pascaligo
|
|
||||||
// accountBalances is a simple type, a map of address <-> tez
|
|
||||||
type accountBalances is map(address, tez);
|
|
||||||
|
|
||||||
const ledger: accountBalances = map
|
## Simple types
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> 10mutez
|
|
||||||
end
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo group=b
|
||||||
|
// The type account_balances denotes maps from addresses to tez
|
||||||
|
|
||||||
|
type account_balances is map (address, tez)
|
||||||
|
|
||||||
|
const ledger : account_balances =
|
||||||
|
map [("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) -> 10mutez]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=b
|
||||||
// account_balances is a simple type, a map of address <-> tez
|
// The type account_balances denotes maps from addresses to tez
|
||||||
|
|
||||||
type account_balances = (address, tez) map
|
type account_balances = (address, tez) map
|
||||||
|
|
||||||
let ledger: account_balances = Map.literal
|
let ledger : account_balances =
|
||||||
|
Map.literal
|
||||||
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), 10mutez)]
|
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address), 10mutez)]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=b
|
||||||
(* account_balances is a simple type, a map of address <-> tez *)
|
// The type account_balances denotes maps from addresses to tez
|
||||||
|
|
||||||
type account_balances = map (address, tez);
|
type account_balances = map (address, tez);
|
||||||
|
|
||||||
let ledger: account_balances =
|
let ledger: account_balances =
|
||||||
Map.literal([
|
Map.literal
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, 10mutez)
|
([("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address, 10mutez)]);
|
||||||
]);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Composed types
|
## Structured types
|
||||||
|
|
||||||
Often contracts require complex data structures, which in turn require well-typed storage or functions to work with. LIGO offers a simple way to compose simple types into larger & more expressive composed types.
|
Often contracts require complex data structures, which in turn require
|
||||||
|
well-typed storage or functions to work with. LIGO offers a simple way
|
||||||
|
to compose simple types into *structured types*.
|
||||||
|
|
||||||
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.
|
The first of those structured types is the *record*, which aggregates
|
||||||
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=c
|
||||||
// alias two types
|
// Type aliasing
|
||||||
type account is address;
|
|
||||||
type numberOfTransactions is nat;
|
|
||||||
// accountData consists of a record with two fields (balance, numberOfTransactions)
|
|
||||||
type accountData is record
|
|
||||||
balance: tez;
|
|
||||||
numberOfTransactions: numberOfTransactions;
|
|
||||||
end
|
|
||||||
// our ledger / accountBalances is a map of account <-> accountData
|
|
||||||
type accountBalances is map(account, accountData);
|
|
||||||
|
|
||||||
// pseudo-JSON representation of our map
|
type account is address
|
||||||
// { "tz1...": {balance: 10mutez, numberOfTransactions: 5n} }
|
type number_of_transactions is nat
|
||||||
const ledger: accountBalances = map
|
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> record
|
// The type account_data is a record with two fields.
|
||||||
|
|
||||||
|
type account_data is record [
|
||||||
|
balance : tez;
|
||||||
|
transactions : number_of_transactions
|
||||||
|
]
|
||||||
|
|
||||||
|
// A ledger is a map from accounts to account_data
|
||||||
|
|
||||||
|
type ledger is map (account, account_data)
|
||||||
|
|
||||||
|
const my_ledger : ledger = map [
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address) ->
|
||||||
|
record [
|
||||||
balance = 10mutez;
|
balance = 10mutez;
|
||||||
numberOfTransactions = 5n;
|
transactions = 5n
|
||||||
end
|
]
|
||||||
end
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=c
|
||||||
(* alias two types *)
|
// Type aliasing
|
||||||
|
|
||||||
type account = address
|
type account = address
|
||||||
type number_of_transactions = nat
|
type number_of_transactions = nat
|
||||||
(* account_data consists of a record with two fields (balance, number_of_transactions) *)
|
|
||||||
|
// The type account_data is a record with two fields.
|
||||||
|
|
||||||
type account_data = {
|
type account_data = {
|
||||||
balance : tez;
|
balance : tez;
|
||||||
number_of_transactions: number_of_transactions;
|
transactions : number_of_transactions
|
||||||
}
|
}
|
||||||
(* our ledger / account_balances is a map of account <-> account_data *)
|
|
||||||
type account_balances = (account, account_data) map
|
|
||||||
|
|
||||||
// pseudo-JSON representation of our map
|
// A ledger is a map from accounts to account_data
|
||||||
// {"tz1...": {balance: 10mutez, number_of_transactions: 5n}}
|
|
||||||
let ledger: account_balances = Map.literal
|
type ledger = (account, account_data) map
|
||||||
|
|
||||||
|
let my_ledger : ledger = Map.literal
|
||||||
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address),
|
[(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address),
|
||||||
{balance = 10mutez;
|
{balance = 10mutez; transactions = 5n})]
|
||||||
number_of_transactions = 5n;}
|
|
||||||
)]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=c
|
||||||
(* alias two types *)
|
// Type aliasing
|
||||||
|
|
||||||
type account = address;
|
type account = address;
|
||||||
type number_of_transactions = nat;
|
type number_of_transactions = nat;
|
||||||
(* account_data consists of a record with two fields (balance, number_of_transactions) *)
|
|
||||||
|
// The type account_data is a record with two fields.
|
||||||
|
|
||||||
type account_data = {
|
type account_data = {
|
||||||
balance : tez,
|
balance : tez,
|
||||||
number_of_transactions,
|
transactions : number_of_transactions
|
||||||
};
|
};
|
||||||
(* our ledger / account_balances is a map of account <-> account_data *)
|
|
||||||
type account_balances = map(account, account_data);
|
|
||||||
|
|
||||||
(* pseudo-JSON representation of our map
|
// A ledger is a map from accounts to account_data
|
||||||
{"tz1...": {balance: 10mutez, number_of_transactions: 5n}} *)
|
|
||||||
let ledger: account_balances =
|
type ledger = map (account, account_data);
|
||||||
|
|
||||||
|
let my_ledger : ledger =
|
||||||
Map.literal([
|
Map.literal([
|
||||||
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address,
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx" : address,
|
||||||
{balance: 10mutez, number_of_transactions: 5n})
|
{balance: 10mutez, transactions: 5n})]);
|
||||||
]);
|
```
|
||||||
|
|
||||||
|
The structured types which are dual to records are the *variant types*
|
||||||
|
and they are described in the section about *pattern matching*. They
|
||||||
|
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).
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Annotations
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo group=d
|
||||||
|
type parameter is Back | Claim | Withdraw
|
||||||
|
|
||||||
|
type storage is
|
||||||
|
record
|
||||||
|
owner : address;
|
||||||
|
goal : tez;
|
||||||
|
deadline : timestamp;
|
||||||
|
backers : map (address, tez);
|
||||||
|
funded : bool
|
||||||
|
end
|
||||||
|
|
||||||
|
type return is list (operation) * storage
|
||||||
|
|
||||||
|
function back (var action : unit; var store : storage) : return is
|
||||||
|
begin
|
||||||
|
if now > store.deadline then
|
||||||
|
failwith ("Deadline passed.")
|
||||||
|
else case store.backers[sender] of
|
||||||
|
None -> store.backers[sender] := amount
|
||||||
|
| Some (x) -> skip
|
||||||
|
end
|
||||||
|
end with ((nil : list (operation)), store) // Annotation
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=d
|
||||||
|
type parameter = Back | Claim | Withdraw
|
||||||
|
|
||||||
|
type storage = {
|
||||||
|
owner : address;
|
||||||
|
goal : tez;
|
||||||
|
deadline : timestamp;
|
||||||
|
backers : (address, tez) map;
|
||||||
|
funded : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type return = operation list * storage
|
||||||
|
|
||||||
|
let back (param, store : unit * storage) : return =
|
||||||
|
let no_op : operation list = [] in
|
||||||
|
if Current.time > store.deadline then
|
||||||
|
(failwith "Deadline passed." : return) // Annotation
|
||||||
|
else
|
||||||
|
match Map.find_opt sender store.backers with
|
||||||
|
None ->
|
||||||
|
let backers = Map.update sender (Some amount) store.backers
|
||||||
|
in no_op, {store with backers=backers}
|
||||||
|
| Some (x) -> no_op, store
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=d
|
||||||
|
type parameter = | Back | Claim | Withdraw;
|
||||||
|
|
||||||
|
type storage = {
|
||||||
|
owner : address,
|
||||||
|
goal : tez,
|
||||||
|
deadline : timestamp,
|
||||||
|
backers : map (address, tez),
|
||||||
|
funded : bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
type return = (list (operation), storage);
|
||||||
|
|
||||||
|
let back = ((param, store) : (unit, storage)) : return => {
|
||||||
|
let no_op : list (operation) = [];
|
||||||
|
if (Current.time > store.deadline) {
|
||||||
|
(failwith ("Deadline passed.") : return); // Annotation
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (Map.find_opt (sender, store.backers)) {
|
||||||
|
| None => {
|
||||||
|
let backers = Map.update (sender, Some (amount), store.backers);
|
||||||
|
(no_op, {...store, backers:backers}) }
|
||||||
|
| Some (x) => (no_op, store)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,110 +3,156 @@ id: unit-option-pattern-matching
|
|||||||
title: Unit, Option, Pattern matching
|
title: Unit, Option, Pattern matching
|
||||||
---
|
---
|
||||||
|
|
||||||
Optionals are a programing pattern seen in OCaml. Since Michelson and LIGO are both inspired by OCaml, you'll have the *option* to use them in LIGO as well.
|
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
|
||||||
|
features it as well. Both the option type and the unit types are
|
||||||
|
instances of a more general kind of types: *variant types* (sometimes
|
||||||
|
called *sum types*).
|
||||||
|
|
||||||
## Type unit
|
## The unit Type
|
||||||
|
|
||||||
Units in Michelson or LIGO represent *for the lack of better words* - an empty/useless/not needed value.
|
The `unit` type in Michelson or LIGO is a predefined type that
|
||||||
|
contains only one value that carries no information. It is used when
|
||||||
Here's how they're defined:
|
no relevant information is required or produced. Here is how it used.
|
||||||
|
|
||||||
> 💡 Units come in handy when we try pattern matching on custom variants below.
|
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
|
||||||
const n: unit = Unit;
|
In PascaLIGO, the unique value of the `unit` type is `Unit`.
|
||||||
|
```pascaligo group=a
|
||||||
|
const n : unit = Unit // Note the capital letter
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
|
||||||
|
In CameLIGO, the unique value of the `unit` type is `()`, following
|
||||||
|
the OCaml convention.
|
||||||
|
```cameligo group=a
|
||||||
let n : unit = ()
|
let n : unit = ()
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
|
||||||
|
In ReasonLIGO, the unique value of the `unit` type is `()`, following
|
||||||
|
the OCaml convention.
|
||||||
|
```reasonligo group=a
|
||||||
let n : unit = ();
|
let n : unit = ();
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
## Variants
|
## Variant types
|
||||||
|
|
||||||
Variant is a user-defined or a built-in type (in case of optionals) that can be compared to Enum (from javascript).
|
A variant type is a user-defined or a built-in type (in case of
|
||||||
|
options) that defines a type by cases, so a value of a variant type is
|
||||||
|
either this, or that or... The simplest variant type is equivalent to
|
||||||
|
the enumerated types found in Java, C++, JavaScript etc.
|
||||||
|
|
||||||
Here's how to define a new variant type:
|
Here is how we define a coin as being either head or tail (and nothing
|
||||||
|
else):
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=b
|
||||||
type id is nat
|
type coin is Head | Tail
|
||||||
type user is
|
const head : coin = Head (Unit) // Unit needed for now.
|
||||||
| Admin of id
|
const tail : coin = Tail (Unit) // Unit needed for now.
|
||||||
| Manager of id
|
|
||||||
| Guest;
|
|
||||||
|
|
||||||
const u: user = Admin(1000n);
|
|
||||||
const g: user = Guest(Unit);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=b
|
||||||
type id = nat
|
type coin = Head | Tail
|
||||||
type user =
|
let head : coin = Head
|
||||||
| Admin of id
|
let tail : coin = Tail
|
||||||
| Manager of id
|
|
||||||
| Guest of unit
|
|
||||||
|
|
||||||
let u: user = Admin 1000n
|
|
||||||
let g: user = Guest ()
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=b
|
||||||
|
type coin = | Head | Tail;
|
||||||
|
let head : coin = Head;
|
||||||
|
let tail : coin = Tail;
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
The names `Head` and `Tail` in the definition of the type `coin` are
|
||||||
|
called *data constructors*, or *variants*.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo group=c
|
||||||
|
type id is nat
|
||||||
|
|
||||||
|
type user is
|
||||||
|
Admin of id
|
||||||
|
| Manager of id
|
||||||
|
| Guest
|
||||||
|
|
||||||
|
const u : user = Admin (1000n)
|
||||||
|
const g : user = Guest (Unit) // Unit needed because of a bug
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=c
|
||||||
|
type id = nat
|
||||||
|
|
||||||
|
type user =
|
||||||
|
Admin of id
|
||||||
|
| Manager of id
|
||||||
|
| Guest
|
||||||
|
|
||||||
|
let u : user = Admin 1000n
|
||||||
|
let g : user = Guest
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=c
|
||||||
type id = nat;
|
type id = nat;
|
||||||
|
|
||||||
type user =
|
type user =
|
||||||
| Admin (id)
|
| Admin (id)
|
||||||
| Manager (id)
|
| Manager (id)
|
||||||
| Guest(unit);
|
| Guest;
|
||||||
|
|
||||||
let u : user = Admin (1000n);
|
let u : user = Admin (1000n);
|
||||||
let g: user = Guest();
|
let g : user = Guest;
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
Defining a varient can be extremely useful for building semantically appealing contracts. We'll learn how to use variants for 'logic purposes' shortly.
|
|
||||||
|
|
||||||
## Optional values
|
## Optional values
|
||||||
|
|
||||||
Optionals are a type of built-in variant that can be used to determine if a variable holds a certain value or not. This is especially useful when (for example) your program's state allows for a certain variable value to be empty, like this:
|
The `option` type is a predefined variant type that is used to express
|
||||||
|
whether there is a value of some type or none. This is especially
|
||||||
|
useful when calling a *partial function*, that is, a function that is
|
||||||
|
not defined for some inputs. In that case, the value of the `option`
|
||||||
|
type would be `None`, otherwise `Some (v)`, where `v` is some
|
||||||
|
meaningful value *of any type*. An example in arithmetic is the
|
||||||
|
division operation:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=d
|
||||||
type dinner is option(string);
|
function div (const a : nat; const b : nat) : option (nat) is
|
||||||
|
if b = 0n then (None: option (nat)) else Some (a/b)
|
||||||
// stay hungry
|
|
||||||
const p1: dinner = None;
|
|
||||||
// have some hamburgers
|
|
||||||
const p2: dinner = Some("Hamburgers")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=d
|
||||||
type dinner = string option
|
let div (a, b : nat * nat) : nat option =
|
||||||
|
if b = 0n then (None: nat option) else Some (a/b)
|
||||||
let p1: dinner = None
|
|
||||||
let p2: dinner = Some "Hamburgers"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=d
|
||||||
type dinner = option(string);
|
let div = ((a, b) : (nat, nat)) : option (nat) =>
|
||||||
|
if (b == 0n) { (None: option (nat)); } else { Some (a/b); };
|
||||||
let p1: dinner = None;
|
|
||||||
let p2: dinner = Some("Hamburgers");
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
@ -114,38 +160,66 @@ let p2: dinner = Some("Hamburgers");
|
|||||||
|
|
||||||
## Pattern matching
|
## Pattern matching
|
||||||
|
|
||||||
Pattern matching is very similiar to e.g. `switch` in Javascript, and can be used to re-route the program's flow based on a value of a variant.
|
*Pattern matching* is similiar to the `switch` construct in
|
||||||
|
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.
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=e
|
||||||
type dinner is option(string);
|
type coin is Head | Tail
|
||||||
function is_hungry(const dinner: dinner): bool is block { skip }
|
|
||||||
with (
|
function flip (const c : coin) : coin is
|
||||||
case dinner of
|
case c of
|
||||||
| None -> True
|
Head -> Tail (Unit) // Unit needed because of a bug
|
||||||
| Some(d) -> False
|
| Tail -> Head (Unit) // Unit needed because of a bug
|
||||||
end
|
end
|
||||||
)
|
```
|
||||||
|
|
||||||
|
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))"
|
||||||
|
# Outputs: Tail(Unit)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=e
|
||||||
type dinner = string option
|
type coin = Head | Tail
|
||||||
let is_hungry (d: dinner) : bool =
|
|
||||||
match d with
|
let flip (c : coin) : coin =
|
||||||
| None -> true
|
match c with
|
||||||
| Some s -> false
|
Head -> Tail
|
||||||
|
| Tail -> Head
|
||||||
|
```
|
||||||
|
|
||||||
|
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.mligo
|
||||||
|
flip Head
|
||||||
|
# Outputs: Tail(Unit)
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=e
|
||||||
type dinner = option(string);
|
type coin = | Head | Tail;
|
||||||
let is_hungry = (d: dinner): bool =>
|
|
||||||
switch (d) {
|
let flip = (c : coin) : coin =>
|
||||||
| None => true
|
switch (c) {
|
||||||
| Some(s) => false
|
| Head => Tail
|
||||||
|
| Tail => Head
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
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.religo
|
||||||
|
flip Head
|
||||||
|
# Outputs: Tail(Unit)
|
||||||
|
```
|
||||||
|
|
||||||
<!--END_DOCUSAURUS_CODE_TABS-->
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
@ -3,43 +3,48 @@ id: constants-and-variables
|
|||||||
title: Constants & Variables
|
title: Constants & Variables
|
||||||
---
|
---
|
||||||
|
|
||||||
The next building block after types are constants and variables.
|
The next building block after types are *constants* and *variables*.
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
Constants are immutable by design, which means their values can't be reassigned.
|
Constants are immutable by design, which means their values cannot be
|
||||||
When defining a constant you need to provide a `name`, `type` and a `value`:
|
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`:
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
```pascaligo
|
```pascaligo group=a
|
||||||
const age : int = 25;
|
const age : int = 25
|
||||||
```
|
```
|
||||||
|
|
||||||
You can evaluate the constant definition above using the following CLI command:
|
You can evaluate the constant definition above using the following CLI
|
||||||
|
command:
|
||||||
```shell
|
```shell
|
||||||
ligo evaluate-value -s pascaligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.ligo age
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.ligo age
|
||||||
# Outputs: 25
|
# Outputs: 25
|
||||||
```
|
```
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
```cameligo
|
```cameligo group=a
|
||||||
let age : int = 25
|
let age : int = 25
|
||||||
```
|
```
|
||||||
|
|
||||||
You can evaluate the constant definition above using the following CLI command:
|
You can evaluate the constant definition above using the following CLI
|
||||||
|
command:
|
||||||
```shell
|
```shell
|
||||||
ligo evaluate-value -s cameligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo age
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.mligo age
|
||||||
# Outputs: 25
|
# Outputs: 25
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
```reasonligo
|
```reasonligo group=a
|
||||||
let age : int = 25;
|
let age : int = 25;
|
||||||
```
|
```
|
||||||
|
|
||||||
You can evaluate the constant definition above using the following CLI command:
|
You can evaluate the constant definition above using the following CLI
|
||||||
|
command:
|
||||||
```shell
|
```shell
|
||||||
ligo evaluate-value -s reasonligo gitlab-pages/docs/language-basics/src/variables-and-constants/const.religo age
|
ligo evaluate-value gitlab-pages/docs/language-basics/src/variables-and-constants/const.religo age
|
||||||
# Outputs: 25
|
# Outputs: 25
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -48,76 +53,72 @@ ligo evaluate-value -s reasonligo gitlab-pages/docs/language-basics/src/variable
|
|||||||
## Variables
|
## Variables
|
||||||
|
|
||||||
<!--DOCUSAURUS_CODE_TABS-->
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
<!--Pascaligo-->
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
Variables, unlike constants, are mutable. They can't be used in a *global scope*, but they can be used within functions, or function arguments.
|
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.
|
||||||
|
|
||||||
> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs.
|
> ⚠️ Please be wary that mutation only works within the function scope
|
||||||
|
> itself, values outside of the function scope will not be affected.
|
||||||
> ⚠️ Please be wary that mutation only works within the function scope itself, values outside of the function scope will not be affected.
|
|
||||||
|
|
||||||
|
|
||||||
|
```pascaligo group=b
|
||||||
```pascaligo
|
// The following is invalid: use `const` for global values instead.
|
||||||
// won't work, use const for global values instead
|
// var four : int := 4
|
||||||
// var four: int = 4;
|
|
||||||
|
|
||||||
function add (const a : int; const b : int) : int is
|
function add (const a : int; const b : int) : int is
|
||||||
block {
|
block {
|
||||||
var c : int := a + b;
|
var c : int := a + 2*b;
|
||||||
|
c := c - b
|
||||||
} with c
|
} with c
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> ⚠️ Notice the assignment operator `:=` for `var`, instead of `=` for
|
||||||
|
> constants.
|
||||||
|
|
||||||
> ⚠️ Notice the different assignment operator `:=`
|
You can run the `add` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
You can run the `add` function defined above using the LIGO compiler like this:
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
ligo run-function -s pascaligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo add '(1,1)'
|
ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.ligo add '(1,1)'
|
||||||
# Outputs: 2
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--CameLIGO-->
|
<!--CameLIGO-->
|
||||||
|
|
||||||
As expected from a functional language, CameLIGO uses value-binding
|
As expected in the pure subset of a functional language, CameLIGO only
|
||||||
for variables rather than assignment. Variables are changed by replacement,
|
features *constant values*: once they are declared, the value cannot
|
||||||
with a new value being bound in place of the old one.
|
be changed (or "mutated").
|
||||||
|
|
||||||
> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs.
|
|
||||||
|
|
||||||
```cameligo
|
|
||||||
|
|
||||||
|
```cameligo group=c
|
||||||
let add (a : int) (b : int) : int =
|
let add (a : int) (b : int) : int =
|
||||||
let c : int = a + b in c
|
let c : int = a + b in c
|
||||||
```
|
```
|
||||||
|
|
||||||
You can run the `add` function defined above using the LIGO compiler like this:
|
You can run the `add` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
```shell
|
```shell
|
||||||
ligo run-function -s cameligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo add '(1,1)'
|
ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.mligo add '(1,1)'
|
||||||
# Outputs: 2
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
<!--ReasonLIGO-->
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
As expected from a functional language, ReasonLIGO uses value-binding
|
As expected in the pure subset of a functional language, ReasonLIGO
|
||||||
for variables rather than assignment. Variables are changed by replacement,
|
only features *constant values*: once they are declared, the value
|
||||||
with a new value being bound in place of the old one.
|
cannot be changed (or "mutated").
|
||||||
|
|
||||||
> 💡 Don't worry if you don't understand the function syntax yet. We'll get to it in upcoming sections of the docs.
|
|
||||||
|
|
||||||
```reasonligo
|
|
||||||
|
|
||||||
|
```reasonligo group=c
|
||||||
let add = ((a, b): (int, int)): int => {
|
let add = ((a, b): (int, int)): int => {
|
||||||
let c : int = a + b;
|
let c : int = a + b;
|
||||||
c;
|
c;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
You can run the `add` function defined above using the LIGO compiler like this:
|
You can run the `add` function defined above using the LIGO compiler
|
||||||
|
like this:
|
||||||
```shell
|
```shell
|
||||||
ligo run-function -s reasonligo gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo add '(1,1)'
|
ligo run-function gitlab-pages/docs/language-basics/src/variables-and-constants/add.religo add '(1,1)'
|
||||||
# Outputs: 2
|
# Outputs: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
268
gitlab-pages/docs/reference/big_map.md
Normal file
268
gitlab-pages/docs/reference/big_map.md
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
---
|
||||||
|
id: big-map-reference
|
||||||
|
title: Big Map
|
||||||
|
---
|
||||||
|
|
||||||
|
## Defining A Big Map Type
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
type move is (int * int)
|
||||||
|
type moveset is big_map (address, move)
|
||||||
|
type foo is big_map (int, int)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
type move = int * int
|
||||||
|
type moveset = (address, move) big_map
|
||||||
|
type foo = (int, int) big_map
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
type move = (int, int);
|
||||||
|
type moveset = big_map(address, move);
|
||||||
|
type foo = big_map(int, int);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Creating A Map
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
const moves: moveset =
|
||||||
|
big_map
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1,2);
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0,3);
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let moves: moveset =
|
||||||
|
Big_map.literal [
|
||||||
|
(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1,2));
|
||||||
|
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0,3));
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let moves: moveset =
|
||||||
|
Big_map.literal ([
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1,2)),
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0,3)),
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
const my_balance : option(move) =
|
||||||
|
moves [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let my_balance : move option =
|
||||||
|
Big_map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let my_balance : option(move) =
|
||||||
|
Big_map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
const my_balance : move =
|
||||||
|
get_force (("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let my_balance : move =
|
||||||
|
Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let my_balance : move =
|
||||||
|
Big_map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
The values of a PascaLIGO big map can be updated using the ordinary
|
||||||
|
assignment syntax:
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
|
||||||
|
function set_ (var m : moveset) : moveset is
|
||||||
|
block {
|
||||||
|
m [("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9);
|
||||||
|
} with m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--Cameligo-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let updated_map : moveset =
|
||||||
|
Big_map.update ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) (Some (4,9)) moves
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--Reasonligo-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let updated_map : moveset =
|
||||||
|
Big_map.update(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), Some((4,9)), moves);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function set_ (var n : int ; var m : foo) : foo is block {
|
||||||
|
m[23] := n ;
|
||||||
|
} with m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let add (n,m : int * foo) : foo = Big_map.add 23 n m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let add = ((n,m): (int, foo)): foo => Big_map.add(23, n, m);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function rm (var m : foo) : foo is block {
|
||||||
|
remove 42 from map m;
|
||||||
|
} with m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let rm (m : foo) : foo = Big_map.remove 42 m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let rm = (m: foo): foo => Big_map.remove(42, m);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
const moves: moveset =
|
||||||
|
big_map
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1,2);
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0,3);
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let moves: moveset =
|
||||||
|
Big_map.literal [
|
||||||
|
(("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1,2));
|
||||||
|
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0,3));
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let moves: moveset =
|
||||||
|
Big_map.literal ([
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1,2)),
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0,3)),
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Big_map.empty() : (a', b') big_map
|
||||||
|
|
||||||
|
Create an empty big map.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
const empty_big_map : big_map(int,int) = big_map end
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let empty_map : foo = Big_map.empty
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let empty_map: foo = Big_map.empty;
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
128
gitlab-pages/docs/reference/bytes.md
Normal file
128
gitlab-pages/docs/reference/bytes.md
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
---
|
||||||
|
id: bytes-reference
|
||||||
|
title: Bytes
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bytes.concat(b1: bytes, b2: bytes) : bytes
|
||||||
|
|
||||||
|
Concatenate together two `bytes` arguments and return the result.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
function concat_op (const s : bytes) : bytes is
|
||||||
|
begin skip end with bytes_concat(s , 0x7070)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let concat_op (s : bytes) : bytes =
|
||||||
|
Bytes.concat s 0x7070
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let concat_op = (s: bytes): bytes => Bytes.concat(s, 0x7070);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
function slice_op (const s : bytes) : bytes is
|
||||||
|
begin skip end with bytes_slice(1n , 2n , s)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let slice_op (s : bytes) : bytes =
|
||||||
|
Bytes.slice 1n 2n s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```
|
||||||
|
let slice_op = (s: bytes): bytes => Bytes.slice(1n, 2n, s);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
It would return "7a7a" rather than "ff7a" or "ff" or "7a".
|
||||||
|
|
||||||
|
## Bytes.pack(data: a') : bytes
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function id_string (const p : string) : option(string) is block {
|
||||||
|
const packed : bytes = bytes_pack(p) ;
|
||||||
|
} with (bytes_unpack(packed): option(string))
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let id_string (p: string) : string option =
|
||||||
|
let packed: bytes = Bytes.pack p in
|
||||||
|
((Bytes.unpack packed): string option)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let id_string = (p: string) : option(string) => {
|
||||||
|
let packed : bytes = Bytes.pack(p);
|
||||||
|
((Bytes.unpack(packed)): option(string));
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Bytes.unpack(packed: bytes) : a'
|
||||||
|
|
||||||
|
Reverses the result of using `unpack` on data, going from Michelson's binary
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function id_string (const p : string) : option(string) is block {
|
||||||
|
const packed : bytes = bytes_pack(p) ;
|
||||||
|
} with (bytes_unpack(packed): option(string))
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let id_string (p: string) : string option =
|
||||||
|
let packed: bytes = Bytes.pack p in
|
||||||
|
((Bytes.unpack packed): string option)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let id_string = (p: string) : option(string) => {
|
||||||
|
let packed : bytes = Bytes.pack(p);
|
||||||
|
((Bytes.unpack(packed)): option(string));
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
155
gitlab-pages/docs/reference/crypto.md
Normal file
155
gitlab-pages/docs/reference/crypto.md
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
---
|
||||||
|
id: crypto-reference
|
||||||
|
title: Crypto
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
function hasherman_blake (const s: bytes) : bytes is blake2b(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let hasherman_blake (s: bytes) : bytes = Crypto.blake2b s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let hasherman_blake = (s: bytes) => Crypto.blake2b(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function hasherman (const s : bytes) : bytes is
|
||||||
|
begin skip end with sha_256(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let hasherman (s : bytes) : bytes =
|
||||||
|
Crypto.sha256 s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let hasherman = (s: bytes): bytes => Crypto.sha256(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
function hasherman512 (const s: bytes) : bytes is sha_512(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let hasherman512 (s: bytes) : bytes = Crypto.sha512 s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let hasherman512 = (s: bytes) => Crypto.sha512(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Crypto.hash_key(k: key) : key_hash
|
||||||
|
|
||||||
|
Hashes a key for easy comparison and storage.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function check_hash_key (const kh1 : key_hash; const k2 : key) : bool * key_hash is block {
|
||||||
|
var ret : bool := False ;
|
||||||
|
var kh2 : key_hash := crypto_hash_key(k2) ;
|
||||||
|
if kh1 = kh2 then ret := True else skip;
|
||||||
|
} with (ret, kh2)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
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-->
|
||||||
|
```reasonligo
|
||||||
|
let check_hash_key = ((kh1, k2): (key_hash, key)) : (bool, key_hash) => {
|
||||||
|
let kh2 : key_hash = Crypto.hash_key(k2);
|
||||||
|
if (kh1 == kh2) {
|
||||||
|
(true, kh2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(false, kh2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Crypto.check(pk: key, signed: signature, data: bytes) : bool
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function check_signature
|
||||||
|
(const pk: key;
|
||||||
|
const signed: signature;
|
||||||
|
const msg: bytes) : bool
|
||||||
|
is crypto_check(pk, signed, msg)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let check_signature (pk, signed, msg: key * signature * bytes) : bool =
|
||||||
|
Crypto.check pk signed msg
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let check_signature = ((pk, signed, msg): (key, signature, bytes)) : bool => {
|
||||||
|
Crypto.check(pk, signed, msg);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
140
gitlab-pages/docs/reference/list.md
Normal file
140
gitlab-pages/docs/reference/list.md
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
---
|
||||||
|
id: list-reference
|
||||||
|
title: List
|
||||||
|
---
|
||||||
|
|
||||||
|
## List.size(lst: a' list) : nat
|
||||||
|
|
||||||
|
Get the number of elements in a list.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function size_ (const m : list(int)) : nat is size(m)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let size_ (s: int list) : nat = List.size s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let size_ = (s: list(int)): nat => List.size(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## List.length(lst: a' list) : nat
|
||||||
|
|
||||||
|
Alias of `List.size`.
|
||||||
|
|
||||||
|
## List.map(map_function: a' -> b', lst: a' list) : 'b list
|
||||||
|
|
||||||
|
Apply an operation defined by `map_function` to each element of a list and return
|
||||||
|
a list of the modified elements.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```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 );
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```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]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```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]);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let iter_op (s : int list) : unit =
|
||||||
|
let do_nothing = fun (_: int) -> unit
|
||||||
|
in List.iter do_nothing s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let iter_op = (s: list(int)): unit => {
|
||||||
|
let do_nothing = (z: int) => unit;
|
||||||
|
List.iter(do_nothing, s);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```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);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo group=b
|
||||||
|
let sum = ((result, i): (int, int)): int => result + i;
|
||||||
|
let sum_of_a_list: int = List.fold(sum, [1, 2, 3], 0);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
392
gitlab-pages/docs/reference/map.md
Normal file
392
gitlab-pages/docs/reference/map.md
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
---
|
||||||
|
id: map-reference
|
||||||
|
title: Map
|
||||||
|
---
|
||||||
|
|
||||||
|
## Defining A Map Type
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
type move is int * int
|
||||||
|
type moveset is map(address, move)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
type move = int * int
|
||||||
|
type moveset = (address, move) map
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
type move = (int, int);
|
||||||
|
type moveset = map(address, move);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Creating A Map
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
const moves: moveset = map
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2);
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3);
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let moves: moveset = Map.literal
|
||||||
|
[ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ;
|
||||||
|
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ;
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let moves : moveset =
|
||||||
|
Map.literal([
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)),
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)),
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
const my_balance : option(move) = moves[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)];
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let my_balance : move option = Map.find_opt ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let my_balance : option(move) =
|
||||||
|
Map.find_opt("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
const my_balance : move = get_force(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), moves);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let my_balance : move = Map.find ("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) moves
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let my_balance : move =
|
||||||
|
Map.find("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, moves);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Map.update(k: a', v: b', m: (a', b') map) : (a', b') map
|
||||||
|
|
||||||
|
Change the value associated with a particular key, if that value doesn't already
|
||||||
|
exist add it.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
The values of a PascaLIGO map can be updated using the ordinary assignment syntax:
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
|
||||||
|
function set_ (var m: moveset) : moveset is
|
||||||
|
block {
|
||||||
|
m[("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address)] := (4,9);
|
||||||
|
} with m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--Cameligo-->
|
||||||
|
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--Reasonligo-->
|
||||||
|
|
||||||
|
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);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Map.add(k: a', v: b', m: (a', b') map) : (a', b') map
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function set_ (var n : int ; var m : map(int, int)) : map(int, int) is block {
|
||||||
|
m[23] := n ;
|
||||||
|
} with m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let add (n,m: int * (int, int) map) : foobar = Map.add 23 n m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let add = (n: int, m: map(int, int)) : foobar => Map.add(23, n, m);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Map.remove(k: a', m: (a', b') map) : (a', b') map
|
||||||
|
|
||||||
|
Remove a key and its associated value from the map.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function rm (var m : map(int, int)) : map(int, int) is block {
|
||||||
|
remove 42 from map m
|
||||||
|
} with m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let rm (m: (int, int) map) : (int, int) map = Map.remove 42 m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let rm = (m: map(int, int)): map(int, int) => Map.remove(42, m);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
function iter_op (const m : moveset) : unit 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);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let iter_op = (m: moveset): unit => {
|
||||||
|
let assert_eq = ((i,j): (address, move)) => assert (j[0] > 1);
|
||||||
|
Map.iter(assert_eq, m);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Map.map(mapping_function: (a', b') -> b', m: (a', b') map) : (a', b') map
|
||||||
|
|
||||||
|
Update the values associated with every key in the map according to some update
|
||||||
|
rule `mapping_function`.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
function map_op (const m : moveset) : moveset is
|
||||||
|
block {
|
||||||
|
function increment (const i : address ; const j : move) : move is (j.0, j.1 + 1);
|
||||||
|
} with map_map (increment, m);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let map_op (m : moveset) : moveset =
|
||||||
|
let increment = fun (i,j: address * move) -> (j.0, j.1 + 1)
|
||||||
|
in Map.map increment m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let map_op = (m: moveset): moveset => {
|
||||||
|
let increment = ((i,j): (address, move)) => (j[0], j[1] + 1);
|
||||||
|
Map.map(increment, m);
|
||||||
|
};
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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`.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo
|
||||||
|
function fold_op (const m : moveset) : 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)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let fold_op = (m: moveset): moveset => {
|
||||||
|
let aggregate = ((i,j): (int, (address, (int,int)))) => i + j[1][1];
|
||||||
|
Map.fold(aggregate, m, 5);
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Map.mem(k: a', m: (a', b') map) : bool
|
||||||
|
|
||||||
|
Test whether a particular key `k` exists in a given map `m`.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function mem (const k: int; const m: map(int, int)) : bool is map_mem(k, m)
|
||||||
|
```
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let mem (k,m: int * (int, int) map) : bool = Map.mem k m
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let mem = ((k,m): (int, map(int,int))): bool => Map.mem(k, m);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Map.empty() : (a', b') map
|
||||||
|
|
||||||
|
Create an empty map.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
const empty_map : map(int, int) = map end
|
||||||
|
```
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let empty_map : (int, int) map = Map.empty
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let empty_map: map(int, int) = Map.empty;
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Map.literal(key_value_pair_list: (a', b') list) : (a', b') map
|
||||||
|
|
||||||
|
Constructs a map from a list of key-value pair tuples.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--Pascaligo-->
|
||||||
|
|
||||||
|
```pascaligo
|
||||||
|
const moves: moveset = map
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address) -> (1, 2);
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address) -> (0, 3);
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo
|
||||||
|
let moves: moveset = Map.literal
|
||||||
|
[ (("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address), (1, 2)) ;
|
||||||
|
(("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address), (0, 3)) ;
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo
|
||||||
|
let moves : moveset =
|
||||||
|
Map.literal([
|
||||||
|
("tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx": address, (1, 2)),
|
||||||
|
("tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN": address, (0, 3)),
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Map.size(m: (a', b') map) : nat
|
||||||
|
|
||||||
|
Get the size of a given map `m`.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function size_ (const m : map(int, int)) : nat is
|
||||||
|
block {skip} with (size(m))
|
||||||
|
```
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let size_ (m: (int, int) map) : nat = Map.size m
|
||||||
|
```
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let size_ = (m: map(int, int)): nat => Map.size(m);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
201
gitlab-pages/docs/reference/set.md
Normal file
201
gitlab-pages/docs/reference/set.md
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
---
|
||||||
|
id: set-reference
|
||||||
|
title: Set
|
||||||
|
---
|
||||||
|
|
||||||
|
## Defining a set
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
type int_set is set (int);
|
||||||
|
const my_set : int_set = set 1; 2; 3 end
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```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)))
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```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))));
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Set.mem(is_member: a', s: a' set) : bool
|
||||||
|
|
||||||
|
Check if a set `s` contains the element `is_member`.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
const contains_three : bool = my_set contains 3
|
||||||
|
// or alternatively
|
||||||
|
const contains_three_fn: bool = set_mem (3, my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=a
|
||||||
|
let contains_three: bool = Set.mem 3 my_set
|
||||||
|
```
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=a
|
||||||
|
let contains_three: bool = Set.mem(3, my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## Set.empty() : a' set
|
||||||
|
|
||||||
|
Create a new empty set. Needs to be annotated with the set type.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
const my_set: int_set = set end
|
||||||
|
const my_set_2: int_set = set_empty
|
||||||
|
```
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=a
|
||||||
|
let my_set: int_set = (Set.empty: int set)
|
||||||
|
```
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=a
|
||||||
|
let my_set: int_set = (Set.empty: set (int));
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
const s_fb : set(string) = set [
|
||||||
|
"foo" ;
|
||||||
|
"bar" ;
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let literal_op (p: unit) : string set =
|
||||||
|
Set.literal ["foo"; "bar"; "foobar"]
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let literal_op = (p: unit) : set(string) => Set.literal(["foo", "bar", "foobar"]);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Set.add(addition: a', s: a' set) : a' set
|
||||||
|
|
||||||
|
Add the element `addition` to a set `s`.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
function add_op (const s : set(string)) : set(string) is
|
||||||
|
begin skip end with set_add("foobar" , s)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```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)))
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```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))));
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Set.remove(removal: a', s: a' set) : a' set
|
||||||
|
|
||||||
|
Remove the element `removal` from a set `s`.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
const smaller_set: int_set = set_remove(3, my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
|
||||||
|
```cameligo group=a
|
||||||
|
let smaller_set: int_set = Set.remove 3 my_set
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
|
||||||
|
```reasonligo group=a
|
||||||
|
let smaller_set: int_set = Set.remove(3, my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```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-->
|
||||||
|
```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-->
|
||||||
|
```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);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## Set.size(s: a' set) : nat
|
||||||
|
|
||||||
|
Get the number of elements in a set.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--Pascaligo-->
|
||||||
|
```pascaligo group=a
|
||||||
|
const set_size: nat = size (my_set)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo group=a
|
||||||
|
let set_size: nat = Set.size my_set
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo group=a
|
||||||
|
let set_size: nat = Set.size (my_set);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
79
gitlab-pages/docs/reference/string.md
Normal file
79
gitlab-pages/docs/reference/string.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
---
|
||||||
|
id: string-reference
|
||||||
|
title: String
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function string_size (const s: string) : nat is size(s)
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let size_op (s: string) : nat = String.size s
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let size_op = (s: string): nat => String.size(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## String.length(s: string) : nat
|
||||||
|
|
||||||
|
Alias for `String.size`.
|
||||||
|
|
||||||
|
## String.slice(pos1: nat, pos2: nat, s: string) : string
|
||||||
|
|
||||||
|
Get the substring of `s` between `pos1` inclusive and `pos2` inclusive. For example
|
||||||
|
the string "tata" given to the function below would return "at".
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function slice_op (const s : string) : string is string_slice(1n , 2n , s)
|
||||||
|
```
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let slice_op (s: string) : string = String.slice 1n 2n s
|
||||||
|
```
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let slice_op = (s: string): string => String.slice(1n, 2n, s);
|
||||||
|
```
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
## String.sub(pos1: nat, pos2: nat, s: string) : string
|
||||||
|
|
||||||
|
Alias for `String.slice`.
|
||||||
|
|
||||||
|
## String.concat(s1: string, s2: string) : string
|
||||||
|
|
||||||
|
Concatenate two strings and return the result.
|
||||||
|
|
||||||
|
<!--DOCUSAURUS_CODE_TABS-->
|
||||||
|
|
||||||
|
<!--PascaLIGO-->
|
||||||
|
```pascaligo
|
||||||
|
function concat_op (const s : string) : string is s ^ "toto"
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--CameLIGO-->
|
||||||
|
```cameligo
|
||||||
|
let concat_syntax (s: string) = s ^ "test_literal"
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--ReasonLIGO-->
|
||||||
|
```reasonligo
|
||||||
|
let concat_syntax = (s: string) => s ++ "test_literal";
|
||||||
|
```
|
||||||
|
|
||||||
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
id: tezos-taco-shop-smart-contract
|
id: tezos-taco-shop-smart-contract
|
||||||
title: Taco shop smart-contract
|
title: Taco shop smart contract
|
||||||
---
|
---
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
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, the **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'll implement a smart contract, that will manage supply, pricing & sales of his tacos to the consumers.
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<img src="/img/tutorials/get-started/tezos-taco-shop-smart-contract/taco-stand.svg" width="50%" />
|
<img src="/img/tutorials/get-started/tezos-taco-shop-smart-contract/taco-stand.svg" width="50%" />
|
||||||
@ -68,7 +68,7 @@ The best way to install the dockerized LIGO is as a **global executable** throug
|
|||||||
|
|
||||||
> 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'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.
|
||||||
|
|
||||||
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's something to get us started and test our LIGO installation as well.
|
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.
|
||||||
|
|
||||||
### `taco-shop.ligo`
|
### `taco-shop.ligo`
|
||||||
```pascaligo group=a
|
```pascaligo group=a
|
||||||
@ -138,7 +138,7 @@ end
|
|||||||
type taco_shop_storage is map(nat, taco_supply);
|
type taco_shop_storage is map(nat, taco_supply);
|
||||||
```
|
```
|
||||||
|
|
||||||
Next step is to update the `main` entry point to include `taco_shop_storage` as 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` 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.
|
||||||
|
|
||||||
**`taco-shop.ligo`**
|
**`taco-shop.ligo`**
|
||||||
```pascaligo group=b+
|
```pascaligo group=b+
|
||||||
@ -154,7 +154,7 @@ function main (const parameter: unit ; const taco_shop_storage : taco_shop_stora
|
|||||||
|
|
||||||
### Populating our storage in a dry-run
|
### Populating our storage in a dry-run
|
||||||
|
|
||||||
When dry-running a contract, it's 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 following:
|
||||||
|
|
||||||
**Storage value**
|
**Storage value**
|
||||||
```zsh
|
```zsh
|
||||||
|
@ -8,7 +8,7 @@ author: Gabriel Alfour
|
|||||||
---
|
---
|
||||||
|
|
||||||
## A Refresher: What is LIGO?
|
## A Refresher: What is LIGO?
|
||||||
LIGO is a statically typed high-level smart-contract language that compiles down to Michelson. It seeks to be easy to use, extensible and safe.
|
LIGO is a statically typed high-level smart contract language that compiles down to Michelson. It seeks to be easy to use, extensible and safe.
|
||||||
|
|
||||||
The core language is being developed by The Marigold Project. George Dupéron and Christian Rinderknecht of Nomadic Labs help on the core language, and tooling for LIGO is being developed by Stove Labs (Granary, docs and infrastructure) and Brice Aldrich (syntax highlighting).
|
The core language is being developed by The Marigold Project. George Dupéron and Christian Rinderknecht of Nomadic Labs help on the core language, and tooling for LIGO is being developed by Stove Labs (Granary, docs and infrastructure) and Brice Aldrich (syntax highlighting).
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ We are looking to develop a Super Type System that has the following features:
|
|||||||
The current version explicitly excludes non-essential features which can produce unexpected explosions in gas costs. To alleviate this constraint, we plan to integrate gas benchmarks on all top-level declarations with some fuzzing. This will allow developers and users to estimate the cost of their contracts in real time.
|
The current version explicitly excludes non-essential features which can produce unexpected explosions in gas costs. To alleviate this constraint, we plan to integrate gas benchmarks on all top-level declarations with some fuzzing. This will allow developers and users to estimate the cost of their contracts in real time.
|
||||||
|
|
||||||
## Getting Started and Contact
|
## Getting Started and Contact
|
||||||
Come visit [our website](https://ligolang.org)! You can also join our [Discord](https://discord.gg/9rhYaEt), Riot (*#ligo-public:matrix.org*) or Telegram Chat (Ligo Public channel).
|
Come visit [our website](https://ligolang.org)! You can also join our [Discord](https://discord.gg/9rhYaEt), Riot (*#ligo-public:matrix.org*) or [Telegram Chat](https://t.me/LigoLang).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ author: Gabriel Alfour
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
It's been a few weeks since our last update. Since then, we've onboarded new collaborators to both LIGO and Marigold, rewritten much of the codebase, and we've begun some exciting new projects. Let's tell you all about it!
|
It has been a few weeks since our last update. Since then, we've onboarded new collaborators to both LIGO and Marigold, rewritten much of the codebase, and we've begun some exciting new projects. Let's tell you all about it!
|
||||||
|
|
||||||
# LIGO
|
# LIGO
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ The most brittle part of our code base is about to become its strongest part. We
|
|||||||
|
|
||||||
Concretely:
|
Concretely:
|
||||||
- Running LIGO-in-Browser will become much easier. Instead of having to dry-run it remotely or to rewrite a Michelson interpreter, we'll be able to **directly interpret** the LIGO program.
|
- Running LIGO-in-Browser will become much easier. Instead of having to dry-run it remotely or to rewrite a Michelson interpreter, we'll be able to **directly interpret** the LIGO program.
|
||||||
- It will be possible to prove the properties of Smart-Contracts written in LIGO directly, instead of having to prove the Michelson they produce.
|
- It will be possible to prove the properties of smart contracts written in LIGO directly, instead of having to prove the Michelson they produce.
|
||||||
- Fewer tests will ned to be written and testing will instead focus mostly on the developer-facing layers of the compiler (i.e. syntax, typing), rather than on the actual compiling part.
|
- Fewer tests will ned to be written and testing will instead focus mostly on the developer-facing layers of the compiler (i.e. syntax, typing), rather than on the actual compiling part.
|
||||||
|
|
||||||
# Marigold
|
# Marigold
|
||||||
@ -56,4 +56,4 @@ It is thus hard for newcomers (even CS researchers!) to dive into Plasma in a co
|
|||||||
|
|
||||||
# Contact
|
# Contact
|
||||||
|
|
||||||
If you have any question, feel free to visit [our website](ligolang.org) and to contact us :)
|
If you have any question, feel free to visit [our website](https://ligolang.org) and to contact us :)
|
||||||
|
@ -3,68 +3,83 @@ const React = require('react');
|
|||||||
const pre = '```';
|
const pre = '```';
|
||||||
|
|
||||||
const PASCALIGO_EXAMPLE = `${pre}pascaligo
|
const PASCALIGO_EXAMPLE = `${pre}pascaligo
|
||||||
// variant defining pseudo multi-entrypoint actions
|
type storage is int
|
||||||
type action is
|
|
||||||
| Increment of int
|
type parameter is
|
||||||
|
Increment of int
|
||||||
| Decrement of int
|
| Decrement of int
|
||||||
|
| Reset
|
||||||
|
|
||||||
function add (const a : int ; const b : int) : int is a + b
|
type return is list (operation) * storage
|
||||||
|
|
||||||
function subtract (const a : int ; const b : int) : int is a - b
|
// Two entrypoints
|
||||||
|
|
||||||
// real entrypoint that re-routes the flow based on the action provided
|
function add (const store : storage; const delta : int) : storage is store + delta
|
||||||
function main (const p : action ; const s : int) : (list(operation) * int) is
|
function sub (const store : storage; const delta : int) : storage is store - delta
|
||||||
((nil : list(operation)),
|
|
||||||
case p of
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
| Increment (n) -> add (s, n)
|
the smart contract parameter. *)
|
||||||
| Decrement (n) -> subtract (s, n)
|
|
||||||
|
function main (const action : parameter; const store : storage) : return is
|
||||||
|
((nil : list (operation)), // No operations
|
||||||
|
case action of
|
||||||
|
Increment (n) -> add (store, n)
|
||||||
|
| Decrement (n) -> sub (store, n)
|
||||||
|
| Reset -> 0
|
||||||
end)
|
end)
|
||||||
${pre}`;
|
${pre}`;
|
||||||
|
|
||||||
const CAMELIGO_EXAMPLE = `${pre}ocaml
|
const CAMELIGO_EXAMPLE = `${pre}ocaml
|
||||||
type storage = int
|
type storage = int
|
||||||
|
|
||||||
(* variant defining pseudo multi-entrypoint actions *)
|
type parameter =
|
||||||
|
Increment of int
|
||||||
type action =
|
|
||||||
| Increment of int
|
|
||||||
| Decrement of int
|
| Decrement of int
|
||||||
|
| Reset
|
||||||
|
|
||||||
let add (a,b: int * int) : int = a + b
|
type return = operation list * storage
|
||||||
let sub (a,b: int * int) : int = a - b
|
|
||||||
|
|
||||||
(* real entrypoint that re-routes the flow based on the action provided *)
|
// Two entrypoints
|
||||||
|
|
||||||
let main (p,s: action * storage) =
|
let add (store, delta : storage * int) : storage = store + delta
|
||||||
let storage =
|
let sub (store, delta : storage * int) : storage = store - delta
|
||||||
match p with
|
|
||||||
| Increment n -> add (s, n)
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
| Decrement n -> sub (s, n)
|
the smart contract parameter. *)
|
||||||
in ([] : operation list), storage
|
|
||||||
|
let main (action, store : parameter * storage) : return =
|
||||||
|
([] : operation list), // No operations
|
||||||
|
(match action with
|
||||||
|
Increment (n) -> add (store, n)
|
||||||
|
| Decrement (n) -> sub (store, n)
|
||||||
|
| Reset -> 0)
|
||||||
${pre}`;
|
${pre}`;
|
||||||
|
|
||||||
|
|
||||||
const REASONLIGO_EXAMPLE = `${pre}reasonligo
|
const REASONLIGO_EXAMPLE = `${pre}reasonligo
|
||||||
type storage = int;
|
type storage = int;
|
||||||
|
|
||||||
/* variant defining pseudo multi-entrypoint actions */
|
type parameter =
|
||||||
|
Increment (int)
|
||||||
|
| Decrement (int)
|
||||||
|
| Reset;
|
||||||
|
|
||||||
type action =
|
type return = (list (operation), storage);
|
||||||
| Increment(int)
|
|
||||||
| Decrement(int);
|
|
||||||
|
|
||||||
let add = ((a,b): (int, int)): int => a + b;
|
(* Two entrypoints *)
|
||||||
let sub = ((a,b): (int, int)): int => a - b;
|
|
||||||
|
|
||||||
/* real entrypoint that re-routes the flow based on the action provided */
|
let add = ((store, delta) : (storage, int)) : storage => store + delta;
|
||||||
|
let sub = ((store, delta) : (storage, int)) : storage => store - delta;
|
||||||
|
|
||||||
let main = ((p,storage): (action, storage)) => {
|
(* Main access point that dispatches to the entrypoints according to
|
||||||
let storage =
|
the smart contract parameter. *)
|
||||||
switch (p) {
|
|
||||||
| Increment(n) => add((storage, n))
|
let main = ((action, store) : (parameter, storage)) : return => {
|
||||||
| Decrement(n) => sub((storage, n))
|
(([] : list (operation)), // No operations
|
||||||
};
|
(switch (action) {
|
||||||
([]: list(operation), storage);
|
| Increment (n) => add ((store, n))
|
||||||
|
| Decrement (n) => sub ((store, n))
|
||||||
|
| Reset => 0}))
|
||||||
};
|
};
|
||||||
${pre}`;
|
${pre}`;
|
||||||
|
|
||||||
|
30
gitlab-pages/website/package-lock.json
generated
30
gitlab-pages/website/package-lock.json
generated
@ -5624,15 +5624,6 @@
|
|||||||
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"klaw-sync": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"graceful-fs": "^4.1.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lazy-cache": {
|
"lazy-cache": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz",
|
||||||
@ -7412,15 +7403,6 @@
|
|||||||
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
|
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"preprocess": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/preprocess/-/preprocess-3.1.0.tgz",
|
|
||||||
"integrity": "sha1-pE5c3Vu7WlTwrSiaru2AmV19k4o=",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"xregexp": "3.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"prismjs": {
|
"prismjs": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz",
|
||||||
@ -7750,6 +7732,12 @@
|
|||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reason-highlightjs": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/reason-highlightjs/-/reason-highlightjs-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-DWWPtfeQjwKgHj2OOieEIAB544uAVjwOAIAg2Yu09CobdUe41Yah0Z67GEvmVtpYCGG/+3CZvDRM1hMVr1zN3A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"rechoir": {
|
"rechoir": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
|
||||||
@ -9433,12 +9421,6 @@
|
|||||||
"integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==",
|
"integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"xregexp": {
|
|
||||||
"version": "3.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-3.1.0.tgz",
|
|
||||||
"integrity": "sha1-FNhGHgvdOCJL/uUDmgiY/EL80zY=",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
@ -47,8 +47,8 @@ const TEAM = [
|
|||||||
|
|
||||||
const COMMUNICATION_CHANNELS = [
|
const COMMUNICATION_CHANNELS = [
|
||||||
{
|
{
|
||||||
link: 'https://discord.gg/9rhYaEt',
|
link: 'https://t.me/LigoLang',
|
||||||
icon: 'img/discord.svg',
|
icon: 'img/telegram.svg',
|
||||||
description: "We're hear to help. Ask us anything"
|
description: "We're hear to help. Ask us anything"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -6,14 +6,14 @@ const docUrl = require(`${process.cwd()}/core/UrlUtils`).docUrl;
|
|||||||
const FEATURES = [
|
const FEATURES = [
|
||||||
{
|
{
|
||||||
image: 'img/strong-type-system.svg',
|
image: 'img/strong-type-system.svg',
|
||||||
title: 'Strong Type System',
|
title: 'Strong, Static Type System',
|
||||||
content: 'Write types, then code. Benefit from the safety of type systems.'
|
content: 'Write types, then code. Benefit from the safety of type systems.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'img/syntax-agnostic.svg',
|
image: 'img/syntax-agnostic.svg',
|
||||||
title: 'Syntax Agnostic',
|
title: 'Polyglot',
|
||||||
content:
|
content:
|
||||||
'Code in your language. Write PascaLIGO, CameLIGO, or add your own syntax.'
|
'Code in your language. Write PascaLIGO, CameLIGO, ReasonLIGO or add your own syntax.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
image: 'img/easy-integration.svg',
|
image: 'img/easy-integration.svg',
|
||||||
@ -77,7 +77,7 @@ module.exports = props => {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div id="preview">
|
<div id="preview">
|
||||||
<h1>A friendly smart-contract language for Tezos</h1>
|
<h1>A friendly Smart Contract Language for Tezos</h1>
|
||||||
<p>Michelson was never so easy</p>
|
<p>Michelson was never so easy</p>
|
||||||
<CodeExamples MarkdownBlock={MarkdownBlock}></CodeExamples>
|
<CodeExamples MarkdownBlock={MarkdownBlock}></CodeExamples>
|
||||||
</div>
|
</div>
|
||||||
|
@ -102,7 +102,7 @@ function Versions(props) {
|
|||||||
</table>
|
</table>
|
||||||
<p>
|
<p>
|
||||||
You can find past versions of this project on{' '}
|
You can find past versions of this project on{' '}
|
||||||
<a href={repoUrl}>Gitlab</a>.
|
<a href={repoUrl}>GitLab</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
@ -4,7 +4,7 @@ let reasonHighlightJs = require('reason-highlightjs');
|
|||||||
|
|
||||||
const siteConfig = {
|
const siteConfig = {
|
||||||
title: 'LIGO', // Title for your website.
|
title: 'LIGO', // Title for your website.
|
||||||
tagline: 'LIGO is a friendly smart-contract language for Tezos',
|
tagline: 'LIGO is a friendly smart contract language for Tezos',
|
||||||
taglineSub: 'Michelson was never so easy',
|
taglineSub: 'Michelson was never so easy',
|
||||||
url: 'https://ligolang.org', // Your website URL
|
url: 'https://ligolang.org', // Your website URL
|
||||||
baseUrl: '/', // Base URL for your project */
|
baseUrl: '/', // Base URL for your project */
|
||||||
@ -14,7 +14,7 @@ const siteConfig = {
|
|||||||
|
|
||||||
// Used for publishing and more
|
// Used for publishing and more
|
||||||
projectName: 'ligo',
|
projectName: 'ligo',
|
||||||
organizationName: 'marigold',
|
organizationName: 'TBN',
|
||||||
// For top-level user or org sites, the organization is still the same.
|
// 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...
|
// e.g., for the https://JoelMarcey.github.io site, it would be set like...
|
||||||
// organizationName: 'JoelMarcey'
|
// organizationName: 'JoelMarcey'
|
||||||
@ -29,7 +29,7 @@ const siteConfig = {
|
|||||||
label: 'Tutorials'
|
label: 'Tutorials'
|
||||||
},
|
},
|
||||||
{ blog: true, label: 'Blog' },
|
{ blog: true, label: 'Blog' },
|
||||||
// TODO: { href: "/odoc", label: "Api" },
|
// TODO: { href: "/odoc", label: "API" },
|
||||||
// { doc: 'contributors/origin', label: 'Contribute' },
|
// { doc: 'contributors/origin', label: 'Contribute' },
|
||||||
{ href: '/contact', label: 'Ask Questions' },
|
{ href: '/contact', label: 'Ask Questions' },
|
||||||
{ search: true }
|
{ search: true }
|
||||||
@ -40,14 +40,24 @@ const siteConfig = {
|
|||||||
{ doc: 'intro/installation', label: 'Install' },
|
{ doc: 'intro/installation', label: 'Install' },
|
||||||
{ doc: 'api/cli-commands', label: 'CLI Commands' },
|
{ doc: 'api/cli-commands', label: 'CLI Commands' },
|
||||||
{ doc: 'contributors/origin', label: 'Contribute' },
|
{ doc: 'contributors/origin', label: 'Contribute' },
|
||||||
{ href: '/odoc', label: 'Api Documentation' }
|
{ href: '/odoc', label: 'API Documentation' }
|
||||||
],
|
],
|
||||||
community: [
|
community: [
|
||||||
|
{
|
||||||
|
href: 'https://forum.tezosagora.org/tag/ligo',
|
||||||
|
label: 'Tezos Agora Forum',
|
||||||
|
blankTarget: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
href: 'https://tezos.stackexchange.com/questions/tagged/ligo',
|
href: 'https://tezos.stackexchange.com/questions/tagged/ligo',
|
||||||
label: 'Tezos Stack Exchange',
|
label: 'Tezos Stack Exchange',
|
||||||
blankTarget: true
|
blankTarget: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
href: 'https://t.me/LigoLang',
|
||||||
|
label: 'Telegram',
|
||||||
|
blankTarget: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
href: 'https://discord.gg/9rhYaEt',
|
href: 'https://discord.gg/9rhYaEt',
|
||||||
label: 'Discord',
|
label: 'Discord',
|
||||||
@ -59,7 +69,7 @@ const siteConfig = {
|
|||||||
doc: 'tutorials/get-started/tezos-taco-shop-smart-contract',
|
doc: 'tutorials/get-started/tezos-taco-shop-smart-contract',
|
||||||
label: 'Tutorials'
|
label: 'Tutorials'
|
||||||
},
|
},
|
||||||
{ href: repoUrl, label: 'Gitlab' }
|
{ href: repoUrl, label: 'GitLab' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -87,10 +97,11 @@ const siteConfig = {
|
|||||||
beginKeywords: '',
|
beginKeywords: '',
|
||||||
keywords: {
|
keywords: {
|
||||||
keyword:
|
keyword:
|
||||||
'and begin block case const contains down else end fail for ' +
|
'and attributes begin big_map block case const contains else'
|
||||||
'from function if in is list map mod nil not of or patch ' +
|
+ ' end False for from function if in is list map mod nil'
|
||||||
'procedure record remove set skip step then to type var while with',
|
+ ' not of or patch record remove set skip then to True type'
|
||||||
literal: 'true false unit int string some none bool nat list'
|
+ ' var while with',
|
||||||
|
literal: 'true false unit int string Some None bool nat list'
|
||||||
},
|
},
|
||||||
lexemes: '[a-zA-Z][a-zA-Z0-9_]*',
|
lexemes: '[a-zA-Z][a-zA-Z0-9_]*',
|
||||||
contains: [
|
contains: [
|
||||||
@ -139,6 +150,7 @@ const siteConfig = {
|
|||||||
indexName: 'ligolang',
|
indexName: 'ligolang',
|
||||||
algoliaOptions: {} // Optional, if provided by Algolia
|
algoliaOptions: {} // Optional, if provided by Algolia
|
||||||
},
|
},
|
||||||
|
docsSideNavCollapsible: true,
|
||||||
gaTrackingId: 'UA-153751765-1',
|
gaTrackingId: 'UA-153751765-1',
|
||||||
gaGtag: true
|
gaGtag: true
|
||||||
};
|
};
|
||||||
|
18
gitlab-pages/website/static/img/telegram.svg
Normal file
18
gitlab-pages/website/static/img/telegram.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="telegram-a" x1="66.7%" x2="41.7%" y1="16.7%" y2="75%">
|
||||||
|
<stop offset="0%" stop-color="#37AEE2"/>
|
||||||
|
<stop offset="100%" stop-color="#1E96C8"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="telegram-b" x1="66%" x2="85.1%" y1="43.065%" y2="83.244%">
|
||||||
|
<stop offset="0%" stop-color="#EFF7FC"/>
|
||||||
|
<stop offset="100%" stop-color="#FFF"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" transform="translate(1 1)">
|
||||||
|
<circle cx="23.5" cy="23.5" r="23.5" fill="url(#telegram-a)"/>
|
||||||
|
<path fill="#C8DAEA" d="M19.2727273,35 C18.4774545,35 18.6126591,34.7064 18.3383636,33.966 L16,26.4414 L34,16"/>
|
||||||
|
<path fill="#A9C9DD" d="M19,35 C19.6818182,35 19.9829545,34.7309188 20.3636364,34.4116301 L24,31.3603439 L19.4640909,29"/>
|
||||||
|
<path fill="url(#telegram-b)" d="M19.7939067,28.5186178 L29.058792,35.7003368 C30.1161307,36.312398 30.8790086,35.9954126 31.1424333,34.6705866 L34.9137167,16.0247303 C35.2997536,14.4006073 34.3236454,13.6637218 33.3120947,14.1455316 L11.1671796,23.1045396 C9.65560092,23.7407214 9.66460526,24.6255468 10.8916853,25.0197183 L16.5745698,26.8808265 L29.7310517,18.1722711 C30.3521592,17.7770947 30.922306,17.9893563 30.4544638,18.4251358"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -4,8 +4,8 @@ title: Origin
|
|||||||
original_id: origin
|
original_id: origin
|
||||||
---
|
---
|
||||||
|
|
||||||
LIGO is a programming language that aims to provide developers with an uncomplicated and safer way to implement smart-contracts. LIGO is currently being implemented for the Tezos blockchain and as a result, it compiles down to Michelson - the native smart-contract language of Tezos.
|
LIGO is a programming language that aims to provide developers with an uncomplicated and safe way to implement smart contracts. Since it is being implemented for the Tezos blockchain LIGO compiles to Michelson—the native smart contract language of Tezos.
|
||||||
|
|
||||||
> Smart-contracts are programs that run within a blockchain network.
|
> Smart contracts are programs that run within a blockchain network.
|
||||||
|
|
||||||
LIGO was initially meant to be a language for developing Marigold, on top of a hacky framework called Meta-Michelson. However, due to the attention received by the Tezos community, a decision has been put into action to develop LIGO as a standalone language that will support Tezos directly as well.
|
LIGO was meant to be a language for developing Marigold on top of a hacky framework called Meta-Michelson. However, due to the attention received by the Tezos community, LIGO is now a standalone language being developed to support Tezos directly.
|
@ -4,23 +4,22 @@ title: Philosophy
|
|||||||
original_id: philosophy
|
original_id: philosophy
|
||||||
---
|
---
|
||||||
|
|
||||||
To understand LIGO’s design choices, it’s important to get its philosophy. There are two main concerns that we have in mind when building LIGO.
|
To understand LIGO’s design choices it’s important to understand its philosophy. We have two main concerns in mind while building LIGO.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Safety
|
## Safety
|
||||||
Once a smart-contract is deployed, it will likely be impossible to change it. You must get it right on the first try, and LIGO should help as much as possible. There are multiple ways to make LIGO a safer language for smart-contracts.
|
Once a smart contract is deployed, it will likely be impossible to change it. You must get it right on the first try, and LIGO should help as much as possible. There are multiple ways to make LIGO a safer language for smart contracts.
|
||||||
|
|
||||||
### Automated Testing
|
### Automated Testing
|
||||||
Automated Testing is the process through which a program will run some other program, and check that this other program behaves correctly.
|
Automated Testing is the process through which a program runs another program, and checks that this other program behaves correctly.
|
||||||
|
|
||||||
There already is a testing library for LIGO programs written in OCaml that is used to test LIGO itself. Making it accessible to users will greatly improve safety. A way to do so would be to make it accessible from within LIGO.
|
There already is a testing library for LIGO programs written in OCaml that is used to test LIGO itself. Making it accessible to users will greatly improve safety. A way to do so would be to make it accessible from within LIGO.
|
||||||
|
|
||||||
### Static Analysis
|
### Static Analysis
|
||||||
Static analysis is the process of having a program analyze another one.
|
Static analysis is the process of having a program analyze another one.
|
||||||
For instance, type systems are a kind of static analysis through which it is possible to find lots of bugs. There is already a fairly simple type system in LIGO, and we plan to make it much stronger.
|
For instance, type systems are a kind of static analysis through which it is possible to find lots of bugs. LIGO already has a simple type system, and we plan to make it much stronger.
|
||||||
|
|
||||||
### Conciseness
|
### Conciseness
|
||||||
Writing less code gives you less room to introduce errors and that's why LIGO encourages writing lean rather than chunky smart-contracts.
|
Writing less code gives you less room to introduce errors. That's why LIGO encourages writing lean rather than chunky smart contracts.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -6,15 +6,27 @@
|
|||||||
"version-next-intro/editor-support"
|
"version-next-intro/editor-support"
|
||||||
],
|
],
|
||||||
"Language Basics": [
|
"Language Basics": [
|
||||||
"version-next-language-basics/cheat-sheet",
|
|
||||||
"version-next-language-basics/types",
|
"version-next-language-basics/types",
|
||||||
"version-next-language-basics/variables",
|
"version-next-language-basics/constants-and-variables",
|
||||||
|
"version-next-language-basics/math-numbers-tez",
|
||||||
|
"version-next-language-basics/strings",
|
||||||
"version-next-language-basics/functions",
|
"version-next-language-basics/functions",
|
||||||
"version-next-language-basics/entrypoints",
|
"version-next-language-basics/boolean-if-else",
|
||||||
"version-next-language-basics/operators"
|
"version-next-language-basics/loops",
|
||||||
|
"version-next-language-basics/unit-option-pattern-matching",
|
||||||
|
"version-next-language-basics/maps-records",
|
||||||
|
"version-next-language-basics/sets-lists-tuples",
|
||||||
|
"version-next-language-basics/tezos-specific"
|
||||||
|
],
|
||||||
|
"Advanced": [
|
||||||
|
"version-next-advanced/timestamps-addresses",
|
||||||
|
"version-next-advanced/entrypoints-contracts",
|
||||||
|
"version-next-advanced/include",
|
||||||
|
"version-next-advanced/first-contract"
|
||||||
],
|
],
|
||||||
"API": [
|
"API": [
|
||||||
"version-next-api-cli-commands"
|
"version-next-api/cli-commands",
|
||||||
|
"version-next-api/cheat-sheet"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"version-next-contributors-docs": {
|
"version-next-contributors-docs": {
|
||||||
|
@ -4,7 +4,7 @@ maintainer: "ligolang@gmail.com"
|
|||||||
authors: [ "Galfour" ]
|
authors: [ "Galfour" ]
|
||||||
homepage: "https://gitlab.com/ligolang/tezos"
|
homepage: "https://gitlab.com/ligolang/tezos"
|
||||||
bug-reports: "https://gitlab.com/ligolang/tezos/issues"
|
bug-reports: "https://gitlab.com/ligolang/tezos/issues"
|
||||||
synopsis: "A higher-level language which compiles to Michelson"
|
synopsis: "A high-level language which compiles to Michelson"
|
||||||
dev-repo: "git+https://gitlab.com/ligolang/tezos.git"
|
dev-repo: "git+https://gitlab.com/ligolang/tezos.git"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
depends: [
|
depends: [
|
||||||
@ -21,6 +21,8 @@ depends: [
|
|||||||
"yojson"
|
"yojson"
|
||||||
"alcotest" { with-test }
|
"alcotest" { with-test }
|
||||||
"getopt"
|
"getopt"
|
||||||
|
"terminal_size"
|
||||||
|
"pprint"
|
||||||
# work around upstream in-place update
|
# work around upstream in-place update
|
||||||
"ocaml-migrate-parsetree" { = "1.4.0" }
|
"ocaml-migrate-parsetree" { = "1.4.0" }
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# This script accepts three arguments, os family, os and it's version,
|
# This script accepts three arguments, os family, os and its version,
|
||||||
# which are subsequently used to fetch the respective docker
|
# which are subsequently used to fetch the respective docker
|
||||||
# image from the ocaml/infrastructure project.
|
# image from the ocaml/infrastructure project.
|
||||||
#
|
#
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user