├── .gitpod.yml ├── .gitpod └── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── lib ├── common │ └── todo.jsligo ├── k01_native_types.jsligo ├── k02_numerical_types.jsligo ├── k03_string_type.jsligo ├── k04_bytes_type.jsligo ├── k05_conditional.jsligo ├── k06_function.jsligo ├── k07_tuple_types.jsligo ├── k08_record_types.jsligo ├── k09_variant_types.jsligo ├── k10_option.jsligo ├── k11_list_type.jsligo ├── k12_map_type.jsligo ├── k13_set_type.jsligo ├── k14_exception.jsligo ├── k15_imperative.jsligo ├── k16_recursive_function.jsligo └── k17_polymorphism.jsligo ├── solution ├── common │ └── todo.jsligo ├── k01_native_types.jsligo ├── k02_numerical_types.jsligo ├── k03_string_type.jsligo ├── k04_bytes_type.jsligo ├── k05_conditional.jsligo ├── k06_function.jsligo ├── k07_tuple_types.jsligo ├── k08_record_types.jsligo ├── k09_variant_types.jsligo ├── k10_option.jsligo ├── k11_list_type.jsligo ├── k12_map_type.jsligo ├── k13_set_type.jsligo ├── k14_exception.jsligo ├── k15_imperative.jsligo ├── k16_recursive_function.jsligo └── k17_polymorphism.jsligo └── test ├── common └── check.jsligo ├── k01.jsligo ├── k02.jsligo ├── k03.jsligo ├── k04.jsligo ├── k05.jsligo ├── k06.jsligo ├── k07.jsligo ├── k08.jsligo ├── k09.jsligo ├── k10.jsligo ├── k11.jsligo ├── k12.jsligo ├── k13.jsligo ├── k14.jsligo ├── k15.jsligo ├── k16.jsligo └── k17.jsligo /.gitpod.yml: -------------------------------------------------------------------------------- 1 | image: 2 | file: .gitpod/Dockerfile 3 | github: 4 | prebuilds: 5 | # enable for the master/default branch (defaults to true) 6 | master: true 7 | # enable for all branches in this repo (defaults to false) 8 | branches: false 9 | # enable for pull requests coming from this repo (defaults to true) 10 | pullRequests: true 11 | # enable for pull requests coming from forks (defaults to false) 12 | pullRequestsFromForks: false 13 | # add a check to pull requests (defaults to true) 14 | addCheck: true 15 | # add a "Review in Gitpod" button as a comment to pull requests (defaults to false) 16 | addComment: false 17 | # add a "Review in Gitpod" button to the pull request's description (defaults to false) 18 | addBadge: false 19 | # add a label once the prebuild is ready to pull requests (defaults to false) 20 | addLabel: false 21 | vscode: 22 | extensions: 23 | - ligolang-publish.ligo-vscode@0.4.11 24 | - baking-bad.michelson@0.1.0 25 | - serokell-io.michelson-debugger@0.1.1 26 | tasks: 27 | - openMode: tab-after 28 | command: | 29 | until open README.md ; do sleep 1; done 30 | clear 31 | -------------------------------------------------------------------------------- /.gitpod/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ligolang/ligo:stable as ligo 2 | 3 | FROM gitpod/workspace-full:latest 4 | 5 | RUN npm i -g @esy-nightly/esy 6 | COPY --from=ligo /root/ligo /usr/local/bin/ligo 7 | 8 | RUN sudo add-apt-repository ppa:serokell/tezos && sudo apt-get update 9 | RUN sudo apt-get install -y apt-transport-https 10 | RUN sudo touch /.containerenv 11 | RUN sudo apt-get install -y tezos-client 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 ligolang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ligo_compiler=docker run --rm -v "$$PWD":"$$PWD" -w "$$PWD" ligolang/ligo:stable 2 | 3 | help: 4 | @echo 'Usage:' 5 | @echo ' all - Execute all tests' 6 | @echo ' k01 - Native types and expressions' 7 | @echo ' k02 - numerical types' 8 | @echo ' k03 - string type' 9 | @echo ' k04 - bytes type' 10 | @echo ' k05 - conditional type' 11 | @echo ' k06 - function type' 12 | @echo ' k07 - tuple types' 13 | @echo ' k08 - record types' 14 | @echo ' k09 - variant types' 15 | @echo ' k10 - option type' 16 | @echo ' k11 - list type' 17 | @echo ' k12 - map type' 18 | @echo ' k13 - set type' 19 | @echo ' k14 - exception types' 20 | @echo ' k15 - Imperative style' 21 | @echo ' k16 - Functional style' 22 | @echo '' 23 | 24 | all: k01 k02 k03 k04 k05 k06 k07 k08 \ 25 | k09 k10 k11 k12 k13 k14 k15 k16 26 | 27 | k01: test/k01.jsligo 28 | @echo "[Testing] $^" 29 | @$(ligo_compiler) run test $^ 30 | 31 | k02: test/k02.jsligo 32 | @echo "[Testing] $^" 33 | @$(ligo_compiler) run test $^ 34 | 35 | k03: test/k03.jsligo 36 | @echo "[Testing] $^" 37 | @$(ligo_compiler) run test $^ 38 | 39 | k04: test/k04.jsligo 40 | @echo "[Testing] $^" 41 | @$(ligo_compiler) run test $^ 42 | 43 | k05: test/k05.jsligo 44 | @echo "[Testing] $^" 45 | @$(ligo_compiler) run test $^ 46 | 47 | k06: test/k06.jsligo 48 | @echo "[Testing] $^" 49 | @$(ligo_compiler) run test $^ 50 | 51 | k07: test/k07.jsligo 52 | @echo "[Testing] $^" 53 | @$(ligo_compiler) run test $^ 54 | 55 | k08: test/k08.jsligo 56 | @echo "[Testing] $^" 57 | @$(ligo_compiler) run test $^ 58 | 59 | k09: test/k09.jsligo 60 | @echo "[Testing] $^" 61 | @$(ligo_compiler) run test $^ 62 | 63 | k10: test/k10.jsligo 64 | @echo "[Testing] $^" 65 | @$(ligo_compiler) run test $^ 66 | 67 | k11: test/k11.jsligo 68 | @echo "[Testing] $^" 69 | @$(ligo_compiler) run test $^ 70 | 71 | k12: test/k12.jsligo 72 | @echo "[Testing] $^" 73 | @$(ligo_compiler) run test $^ 74 | 75 | k13: test/k13.jsligo 76 | @echo "[Testing] $^" 77 | @$(ligo_compiler) run test $^ 78 | 79 | k14: test/k14.jsligo 80 | @echo "[Testing] $^" 81 | @$(ligo_compiler) run test $^ 82 | 83 | k15: test/k15.jsligo 84 | @echo "[Testing] $^" 85 | @$(ligo_compiler) run test $^ 86 | 87 | k16: test/k16.jsligo 88 | @echo "[Testing] $^" 89 | @$(ligo_compiler) run test $^ 90 | 91 | k17: test/k17.jsligo 92 | @echo "[Testing] $^" 93 | @$(ligo_compiler) run test $^ 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: JsLIGO Koans 3 | tags: Training 4 | description: Set of koans dedicated to JsLIGO syntax 5 | --- 6 | 7 | # LIGO koans 8 | 9 | # Prerequisites 10 | 11 | ## Remote execution 12 | 13 | [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/ligolang/ligo-koans) 14 | 15 | ## Local execution 16 | 17 | Please install this software first on your machine or use online alternative : 18 | 19 | - [ ] [VS Code](https://code.visualstudio.com/download) : as text editor 20 | - [ ] [ligo](https://ligolang.org/docs/intro/installation/) : high level language that's transpile to michelson low level language and provide lot of development support for Tezos 21 | 22 | Then after you can perform a git clone of `https://github.com/ligolang/ligo-koans` 23 | 24 | # What's a Koan 25 | 26 | A koans are essentially a simple problem where programmer is asked to 27 | complete the code. And this is done thanks to unit testing. 28 | 29 | ## Howto ? 30 | 31 | The koans are in files from k01 to k16 in the lib directory. Each one can be executed independantly thanks to the `make` command: 32 | 33 | ```sh 34 | jsligo-koans ➤ make k01 35 | [Testing] test/k01.jsligo 36 | 37 | Test failed with "Should return \"Hello\"" 38 | Trace: 39 | File "test/../lib/common/todo.jsligo", line 2, characters 15-32: 40 | 41 | File "test/../lib/k01_native_types.jsligo", line 5, characters 4-37 , 42 | File "test/k01.jsligo", line 4, characters 28-40 43 | make: *** [k01] Error 44 | ``` 45 | 46 | Open the file `k01_native_types` and fix the code. For instance the previous one can be solved replacing the `K.todo(...)` by the response. 47 | 48 | ### Before 49 | 50 | ```javascript 51 | export const hello = () : string => 52 | K.todo("Should return \"Hello\""); 53 | ``` 54 | 55 | #### After 56 | 57 | ```javascript 58 | export const hello = () : string => 59 | "Hello"; 60 | ``` 61 | 62 | ```sh 63 | jsligo-koans ➤ make k01 64 | [Testing] test/k01.jsligo 65 | ("[SUCCESS]" , {expected = "Hello"}) 66 | 67 | Test failed with "Should return 1" 68 | Trace: 69 | File "test/../lib/common/todo.jsligo", line 2, characters 15-32: 70 | 71 | File "test/../lib/k01_native_types.jsligo", line 9, characters 4-29 , 72 | File "test/k01.jsligo", line 5, characters 28-38 73 | make: *** [k01] Error 1 74 | ``` 75 | 76 | Then solves each exercise step by step and finally for k01 you should 77 | have the following execution trace. 78 | 79 | ``` 80 | jsligo-koans ➤ make all 81 | [Testing] test/k01.jsligo 82 | ("[SUCCESS]" , {expected = "Hello"}) 83 | ("[SUCCESS]" , {expected = 1}) 84 | ("[SUCCESS]" , {expected = 1n}) 85 | ("[SUCCESS]" , {expected = 1000000mutez}) 86 | ("[SUCCESS]" , {expected = true}) 87 | ("[SUCCESS]" , {expected = 2n}) 88 | ``` 89 | 90 | Once `k01` is solved you can do the same for`k02`, `k03`, ..., `k15` and finally `k16`. Each Koan covers a 91 | specific topic of the language. Finally, `k17` introduces polymorphism. 92 | 93 | # License 94 | 95 | MIT License 96 | 97 | Copyright (c) 2022 ligolang 98 | 99 | Permission is hereby granted, free of charge, to any person obtaining a copy 100 | of this software and associated documentation files (the "Software"), to deal 101 | in the Software without restriction, including without limitation the rights 102 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 103 | copies of the Software, and to permit persons to whom the Software is 104 | furnished to do so, subject to the following conditions: 105 | 106 | The above copyright notice and this permission notice shall be included in all 107 | copies or substantial portions of the Software. 108 | -------------------------------------------------------------------------------- /lib/common/todo.jsligo: -------------------------------------------------------------------------------- 1 | export const todo : ((message:string) => T) = 2 | message => failwith(message); 3 | -------------------------------------------------------------------------------- /lib/k01_native_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | LIGO is strongly and statically typed. This means that the compiler checks 5 | how your contract processes data, ensuring that each function's expectations 6 | are met. If it passes the test, your contract will not fail at run-time due 7 | to some inconsistent assumptions on your data. This is called type checking. 8 | 9 | In addition, LIGO types are built on top of Michelson's type system. 10 | */ 11 | 12 | export const hello = () : string => 13 | K.todo("Should return \"Hello\""); 14 | 15 | export const one = () : int => 16 | K.todo("Should return 1"); 17 | 18 | export const one_nat = () : nat => 19 | K.todo("Should return 1 nat"); 20 | 21 | export const one_tez = () : tez => 22 | K.todo("Should return 1 tez"); 23 | 24 | export const true_value = () : bool => 25 | K.todo("Should return true"); 26 | 27 | /* 28 | Reference: https://ligolang.org/docs/language-basics/math-numbers-tez 29 | */ -------------------------------------------------------------------------------- /lib/k02_numerical_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | LIGO offers three built-in numerical types: 5 | - int are integers, such as 10, -6 and 0. But there is only one canonical zero: 0 6 | (so, for instance, -0 and 00 are invalid). 7 | - nat are natural numbers (integral numbers greater than or equal to zero). They are 8 | followed by the annotation as nat such as 3 as nat, 12 as nat and 0 as nat for the 9 | natural zero. The same restriction on zero as integers applies: 0 as nat is the 10 | only way to specify the natural zero. 11 | - tez are units of measure of Tezos tokens. They can be decimals and are followed by 12 | annotation tez such as 3 as tez. You can also type units of millionth of tez, using 13 | the annotation as mutez after a natural literal, such as 10000 as mutez or 0 as mutez. 14 | */ 15 | 16 | /* 17 | Addition in LIGO is accomplished by means of the + infix operator. Some type constraints 18 | apply, for example you cannot add a value of type tez to a value of type nat. 19 | */ 20 | 21 | export const add_int = (n1:int,n2:int) : int => 22 | K.todo("Should add two integers"); 23 | 24 | export const add_int_and_nat = (n1:int,n2:nat) : int => 25 | K.todo("Should add an integer and a natural"); 26 | 27 | export const add_nat = (n1:nat,n2:nat) : nat => 28 | K.todo("Should add two naturals"); 29 | 30 | export const add_tez = (n1:tez,n2:tez) : tez => 31 | K.todo("Should add two tez values"); 32 | 33 | /* 34 | Subtractions follow the same principles. 35 | Warning: when subtracting two nats, the result is an int 36 | */ 37 | 38 | export const substract_int = (n1:int,n2:int) : int => 39 | K.todo("Should substract two integers"); 40 | 41 | export const substract_int_and_nat = (n1:int,n2:nat) : int => 42 | K.todo("Should substract and integer and a nat"); 43 | 44 | export const substract_nat = (n1:nat,n2:nat) : int => 45 | K.todo("Should substract two naturals"); 46 | 47 | export const substract_tez = (n1:tez,n2:tez) : tez => 48 | K.todo("Should substract two tez values"); 49 | 50 | /* 51 | Numerical conversion can be done from int to nat and vice versa 52 | */ 53 | 54 | export const int_from_nat = (n1:nat) : int => 55 | K.todo("Should cast a nat to an int"); 56 | 57 | export const nat_from_int = (n1:int) : nat => 58 | K.todo("Should cast an int to a nat"); 59 | 60 | /* 61 | Reference: https://ligolang.org/docs/language-basics/math-numbers-tez 62 | */ 63 | -------------------------------------------------------------------------------- /lib/k03_string_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Strings can be concatenated using the + operator. 5 | */ 6 | 7 | export const concat = (n1:string,n2:string) : string => 8 | K.todo("Should return concat n1 and n2"); 9 | 10 | /* 11 | Strings can be sliced using a built-in function String.sub which takes three parameters: 12 | - an offset describing the index of first character that will be copied 13 | - the length describing the number of characters that will be copied (starting from the given offset) 14 | - the string being sliced 15 | */ 16 | 17 | export const first = (n: string) : string => 18 | // We assume `n` is never the empty string here 19 | K.todo("Should return The first character of n"); 20 | 21 | /* 22 | The length of a string can be found using a built-in function String.length 23 | */ 24 | 25 | export const length = (n: string) : nat => 26 | K.todo("Should return The length of n"); 27 | 28 | /* 29 | References: 30 | https://ligolang.org/docs/language-basics/strings-bytes 31 | https://ligolang.org/docs/reference/string-reference 32 | 33 | */ -------------------------------------------------------------------------------- /lib/k04_bytes_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Bytes can be concatenated using the + operator. 5 | */ 6 | 7 | export const concat = (n1:bytes,n2:bytes) : bytes => 8 | K.todo("Should return concat n1 and n2"); 9 | 10 | /* 11 | Bytes can be sliced using a built-in function bytes.sub which takes three parameters: 12 | - an offset describing the index of first character that will be copied 13 | - the length describing the number of characters that will be copied (starting from the given offset) 14 | - the bytes being sliced 15 | */ 16 | 17 | export const first = (n: bytes) : bytes => 18 | K.todo("Should return The first byte of n"); 19 | 20 | /* 21 | The length of a bytes can be found using a built-in function bytes.length 22 | */ 23 | 24 | export const length = (n1: bytes) : nat => 25 | K.todo("Should return The length of n1"); 26 | 27 | /* 28 | References: 29 | https://ligolang.org/docs/language-basics/strings-bytes 30 | https://ligolang.org/docs/reference/bytes-reference 31 | */ -------------------------------------------------------------------------------- /lib/k05_conditional.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | The type of a boolean value is bool. Here is how to define a boolean value: 5 | */ 6 | 7 | const a: bool = true; 8 | const b: bool = false; 9 | 10 | /* 11 | Comparing values 12 | */ 13 | 14 | const compare_string = (s1:string,s2:string): bool => { 15 | K.todo("Should compare two strings"); 16 | }; 17 | 18 | const boolean_and = (b1:bool,b2:bool): bool => { 19 | K.todo("Should return the boolean and of b1, b2 "); 20 | }; 21 | 22 | const boolean_or = (b1:bool,b2:bool): bool => { 23 | K.todo("Should return the boolean or of b1, b2"); 24 | }; 25 | 26 | /* 27 | Conditional selection 28 | */ 29 | 30 | const selection = (i1:int,i2:int): string => { 31 | K.todo("Should return a string denoting the comparison i.e. 'i1 < i2' or 'i1 = i2' or 'i1 > i2'"); 32 | }; 33 | 34 | /* 35 | Reference: https://ligolang.org/docs/language-basics/boolean-if-else 36 | */ -------------------------------------------------------------------------------- /lib/k06_function.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Functions in JsLIGO are defined using the let or const keyword, like other values. 5 | The difference is that parameters are provided after the value name, with its type, 6 | then followed by the return type. 7 | 8 | Here is how you define a basic function that sums two integers: 9 | 10 | const add = (a:int, b:int): int => a + b; 11 | 12 | This syntax is equivalent to 13 | 14 | const add = ([a, b]: [int, int]): int => a + b; 15 | */ 16 | 17 | /* 18 | Write the function which computes the maximum of two integers 19 | 20 | max(1,2) returns 2 21 | */ 22 | 23 | const max = (a:int,b:int): int => 24 | K.todo("Should return the function computing the max of two integers"); 25 | 26 | /* 27 | Write a function waiting for a string which returns a function 28 | waiting for another string and returns the string concatenation. 29 | 30 | concat("a")("b") = "ab" 31 | */ 32 | 33 | export const concat = (a:string) : ((b:string) => string) => { 34 | K.todo("Should provide a function performing a concatenation"); 35 | }; 36 | 37 | /* 38 | JsLIGO offers the capability to receive a functions as a parameter. 39 | */ 40 | 41 | export const handle = (f:((b:string) => string), a:string) : string => { 42 | K.todo("Should return the application of `f` on `a`"); 43 | }; 44 | 45 | /* ------------------------------------------------------------------------- 46 | | 47 | | Optional section .... 48 | | 49 | +------------------------------------------------------------------------ */ 50 | 51 | export const do_optional_part = true; 52 | 53 | /* 54 | Finally we can write functions which return another function. On typical 55 | one is the capability to transform a function waiting for two parameters 56 | to a function waiting for a parameter returning another function waiting 57 | also for one parameter. Such transformation is called Currying. 58 | 59 | Reference: https://en.wikipedia.org/wiki/Currying 60 | */ 61 | 62 | type uncurried = (a:int, b:int) => int; 63 | type curried = (a:int) => ((b:int) => int); 64 | 65 | /* 66 | Write the fonction able to do Uncurrying i.e. transform a function from 67 | curried type to uncurried type. 68 | */ 69 | 70 | export const uncurry = (f:curried): uncurried => { 71 | K.todo("Should return uncurried version of the parametric function"); 72 | }; 73 | 74 | /* 75 | Write the fonction able to do Currying i.e. transform a function from 76 | uncurried to curried. 77 | */ 78 | 79 | export const curry = (f:uncurried): curried => { 80 | K.todo("Should return curried version of the parametric function"); 81 | }; 82 | 83 | /* 84 | Reference: https://ligolang.org/docs/language-basics/functions 85 | */ 86 | 87 | -------------------------------------------------------------------------------- /lib/k07_tuple_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Tuples gather a given number of values in a specific order and those values, 5 | called components, can be retrieved by their index (position). 6 | */ 7 | 8 | type point = [int,int]; 9 | 10 | export const new_point = (x:int,y:int): point => { 11 | K.todo("Should return the a point"); 12 | }; 13 | 14 | export const abscissa = (p:point): int => { 15 | K.todo("Should return the first component of the point"); 16 | }; 17 | 18 | export const ordinate = (p:point): int => { 19 | K.todo("Should return the second component of the point"); 20 | } 21 | 22 | /* 23 | Reference: https://ligolang.org/docs/language-basics/sets-lists-tuples#tuples 24 | */ -------------------------------------------------------------------------------- /lib/k08_record_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Records are one-way data of different types can be packed into a single type. A record is made of a set of fields, 5 | which are made of a field name and a field type. 6 | */ 7 | 8 | type account_data = { 9 | balance: tez, 10 | transactions: nat 11 | }; 12 | 13 | export let account = () : account_data => { 14 | K.todo("Should return an account data with a balance of 5 tez and 3 transactions"); 15 | }; 16 | 17 | /* 18 | If we want the contents of a given field, we use the (.) infix operator 19 | */ 20 | 21 | const balance : tez = (account()).balance; 22 | 23 | /* 24 | Given a record value, it is a common design pattern to update only a small number of its fields. 25 | Instead of copying the fields that are unchanged, LIGO offers a way to only update the fields that are modified. 26 | 27 | One way to understand the update of record values is the functional update. The idea is to have an expression 28 | whose value is the updated record. 29 | 30 | Reference; https://ligolang.org/docs/language-basics/maps-records#functional-updates 31 | */ 32 | 33 | export contents modify_account = (account: account_data) : account_data => { 34 | K.todo("Should return an account data with 5 transactions"); 35 | } 36 | 37 | /* 38 | Reference: https://ligolang.org/docs/language-basics/maps-records 39 | */ 40 | -------------------------------------------------------------------------------- /lib/k09_variant_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | A variant type is a type that defines a type by cases. For instance we 5 | can define a type reflecting what can be played during a shifumi game: 6 | */ 7 | 8 | type play = | ["Paper"] | ["Scissor"] | ["Stone"]; 9 | 10 | /* 11 | Here `Paper`, `Cisor` and `Stone` are called data constructors. Then each 12 | data can be created thanks to its corresponding constructor. 13 | */ 14 | 15 | const paper : play = Paper(); 16 | const scissor : play = Scissor(); 17 | const stone : play = Stone(); 18 | 19 | /* 20 | Define a coin flipping type with `Heads` and `Tails`. 21 | */ 22 | 23 | export type coin = 24 | | ["Heads"] | ["Tails"]; 25 | 26 | export const new_heads = (): coin => 27 | K.todo("Should return a heads data"); 28 | 29 | export const new_tails = (): coin => 30 | K.todo("Should return a tails data"); 31 | 32 | /* 33 | Pattern matching is similar to the switch construct in JavaScript, and can be used 34 | to route the program's control flow based on the value of a variant, record, tuple, 35 | or list. 36 | 37 | A component of a pattern can be discarded by using a wildcard _ instead of a variable name. 38 | 39 | Note: LIGO will warn about unused variables bound in patterns in the same way that function 40 | arguments are warned about. Variable names beginning with _ can be used as a binder to 41 | prevent warnings. 42 | 43 | Back to the shifumi variant type the pattern matching is expressed thanks to a dedicated `match`` 44 | control structure. 45 | */ 46 | 47 | export const is_paper = (p:play): bool => { 48 | match(p, { 49 | Paper: () => true, 50 | Scissor: () => false, 51 | Stone: () => false 52 | }); 53 | }; 54 | 55 | /* 56 | In this example we can see how the switch case is performed thanks to the recognition of all 57 | data constructors. 58 | */ 59 | 60 | /* 61 | Define a function flipping a coins. Then if it's 62 | a Tails it returns a Heads and vice-versa. 63 | */ 64 | 65 | export const flip_coin = (c:coin): coin => { 66 | K.todo("Should return a flipped coin. Heads should be transformed to Tails and Tails should be transformed to Heads."); 67 | }; 68 | 69 | /* 70 | The previous variants are equivalent to the enumerated types found in Java, C++, JavaScript etc. because each data constructor 71 | does not hold any value! 72 | 73 | In fact, JsLIGO variant can be more complex and can hold values. 74 | 75 | For instance a point can be expressed thanks to its absissa and ordinate or thanks to its radius and angles. 76 | */ 77 | 78 | type coordinate = { absissa : int, ordinate: int}; 79 | type polar = { radius: int, angle: int}; 80 | 81 | type point = | ["Coordinate", coordinate] | ["Polar", polar]; 82 | 83 | /* 84 | Then the pattern matching recognize and deconstruct data in order to retrieve intrnale values. 85 | */ 86 | 87 | export const is_coordinate = (p:point): bool => { 88 | match(p,{ 89 | Coordinate: _c => true, // p can be used as usual since it's a record, 90 | Polar: _p => false, // p can be used as usual since it's a record, 91 | }); 92 | }; 93 | 94 | /* 95 | Back to the `coin` we can elaborate a simple game. For this purpose we define first 96 | the game structure and a functional type for the coin 97 | */ 98 | 99 | type game = { heads: nat, tails: nat }; 100 | type flip = ((u:unit) => coin); 101 | 102 | /* 103 | Define a type variant with Play containing a flip function and a Reset. Then after 104 | define a function play which manages heads and tails of a game each time a Play and 105 | a Reset is received. 106 | */ 107 | 108 | export type turn = | ["Play", flip] | ["Reset"]; 109 | 110 | export const play = (t:turn,g:game): game => { 111 | K.todo("Should return a new game reflecting the turn played."); 112 | }; 113 | 114 | /* 115 | Reference: https://ligolang.org/docs/language-basics/unit-option-pattern-matching#variant-types 116 | */ -------------------------------------------------------------------------------- /lib/k10_option.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | The option type is a predefined variant type that is used to express 5 | whether there is a value of some type or none. This is especially useful 6 | when calling a partial function, that is, a function that is not defined 7 | for some inputs. In that case, the value of the option type would be None, 8 | otherwise Some (v), where v is some meaningful value of any type. 9 | */ 10 | 11 | /* 12 | Write the divide operation returning `None` when we try to divide by `0` or 13 | the division in a `Some`. 14 | */ 15 | 16 | export const div = (a:int, b: int): option => { 17 | K.todo("Should divide and return the result in a Some, or Return None when it's not possible"); 18 | }; 19 | 20 | /* 21 | Given a function which acts on int, write the function with an option and 22 | a function which performs the paramtric function when it's a Some and do 23 | nothing when it's a None. 24 | */ 25 | 26 | export const map = (f:((n:int) => int), s: option): option => { 27 | K.todo("Should apply the function on the value in a Some and return an option with the new value, Return None otherwise"); 28 | }; 29 | 30 | /* 31 | Write the function returning the first character as a string or nothing. The string can be the empty one i.e. "". 32 | */ 33 | 34 | export const first = (n: string) : option => { 35 | K.todo("Should return The first character of n"); 36 | } 37 | 38 | /* 39 | Reference: https://ligolang.org/docs/language-basics/unit-option-pattern-matching#optional-values 40 | */ -------------------------------------------------------------------------------- /lib/k11_list_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Lists are linear collections of elements of the same type. Linear means that, 5 | in order to reach an element in a list, we must visit all the elements before 6 | (sequential access). Elements can be repeated, as only their order in the collection 7 | matters. The first element is called the head, and the sub-list after the head is 8 | called the tail. 9 | */ 10 | 11 | /* 12 | Defining lists can be done using literal expressions. 13 | */ 14 | 15 | export const no_ints = (): list => list([]); 16 | export const single_int = (p:int): list => list([p]); 17 | 18 | /* 19 | In JsLIGO, the cons operator is infix and noted , .... It is not symmetric: on the left 20 | lies the element to cons, and, on the right, a list on which to cons. 21 | */ 22 | 23 | export const cons = (h:int, t:list): list => { 24 | list([ h, ...t ]); 25 | }; 26 | 27 | /* 28 | Pattern matching for lists has its own syntax. 29 | 30 | match(my_int_list, list([ 31 | ([] : list) => ..., 32 | ([hd, ...tl] : list) => ... 33 | ])); 34 | */ 35 | 36 | /* 37 | Write a predicate checking the emptiness of a given list. 38 | */ 39 | 40 | export const is_empty = (v : list) : bool => { 41 | K.todo("Should return the head of the parametric list in a Some; None otherwise"); 42 | }; 43 | 44 | /* 45 | Given a list of int write the function which returns the first element. For this purpose we use the 46 | option type for the result. 47 | 48 | head(list([1,2])) = Some(1) 49 | head(list([])) = None 50 | */ 51 | 52 | export const head = (l:list): option => { 53 | K.todo("Should return the head of the parametric list in a Some; None otherwise"); 54 | }; 55 | 56 | /* 57 | We may want to change all the elements of a given list by applying to them a function. 58 | This is called a map operation, not to be confused with the map data structure. The 59 | predefined functional iterator implementing the mapped operation over lists is called 60 | List.map and is used as follows. 61 | */ 62 | 63 | /* 64 | Write a function taking a list of integer and return a list where all elements have been 65 | added to one. 66 | 67 | increment(list([1,2])) = list([2,3]) 68 | */ 69 | 70 | export const increment = (l:list) : list => { 71 | K.todo("Should return a list where all elements have been incremented by 1"); 72 | }; 73 | 74 | /* 75 | Folded operation over Lists is the most general of iterations. The folded function takes 76 | two arguments: an accumulator and the structure element at hand, with which it then produces 77 | a new accumulator. This enables having a partial result that becomes complete when the 78 | traversal of the data structure is over. Folding can be done in two ways, labelled with 79 | the directions left and right. 80 | 81 | Take for example a function f, a list [1; 2; 3], and an accumulator that's just an 82 | empty list. A rough approximation of the result of a left fold would look like 83 | f(f(f([], 1), 2), 3), while a right fold would instead look like f(1, f(2, f(3, []))). 84 | */ 85 | 86 | /* 87 | Write a function waiting for a list of integers and returning the addition of all elements. 88 | 89 | add_all(list([1,2,3])) = 1 + 2 + 3 // 6 90 | */ 91 | 92 | export const add_all = (l:list): int => { 93 | K.todo("Should return the addition of all element"); 94 | }; 95 | 96 | /* 97 | Write the fonction reverse for integer lists. 98 | 99 | reverse(list([1,2,3])) = list([3,2,1]) 100 | */ 101 | 102 | export const reverse = (l:list): list => { 103 | K.todo("Should return the reversed list"); 104 | }; 105 | 106 | /* 107 | References: https://ligolang.org/docs/language-basics/sets-lists-tuples#lists 108 | https://ligolang.org/docs/reference/list-reference 109 | */ -------------------------------------------------------------------------------- /lib/k12_map_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Maps are a data structure which associate values of the same type to 5 | values of the same type. The former are called key and the latter values. 6 | Together they make up a binding. An additional requirement is that the 7 | type of the keys must be comparable, in the Michelson sense. 8 | */ 9 | 10 | /* 11 | Here is how to create an empty map. 12 | */ 13 | 14 | const empty: map = Map.empty; 15 | 16 | /* 17 | And here is how to create a non-empty map value: 18 | */ 19 | 20 | export const transpose : map = Map.literal(list([ [0, "Zero"], [1, "One"] ])); 21 | 22 | /* 23 | Access is done thanks to `Map.find_opt`. Propose a function which tries to find 24 | a value using a key and if it does not exist 25 | */ 26 | 27 | export const find = (m : map, k:int, d:string) : string => { 28 | K.todo("Should return the value associated to the key; The default value otherwise"); 29 | }; 30 | 31 | /* 32 | Modification can done thanks to `Map.update`. Propose a function which updates a value using a key 33 | and it return the new map and the previous value as an option 34 | */ 35 | 36 | export const update = (m : map, k:int, v:string) : [option,map] => { 37 | K.todo("Should return the value associated to the key; The default value otherwise"); 38 | }; 39 | 40 | /* 41 | Folded operations over maps takes two arguments: an accumulator and the structure element 42 | at hand, with which it then produces a new accumulator. This enables having a partial result 43 | that becomes complete when the traversal of the data structure is over. 44 | 45 | The predefined functional iterator implementing the folded operation over maps is called 46 | Map.fold and is used as follows. 47 | */ 48 | 49 | /* 50 | Give a function which acts on int, write the function with a map and 51 | a function which perform the parametric function. 52 | */ 53 | 54 | export const map = (f:((k:int,v:string) => string), m: map): map => { 55 | K.todo("Should apply the function on all pairs of key value and return the transformed map "); 56 | }; 57 | 58 | /* 59 | Reference: https://ligolang.org/docs/language-basics/maps-records#maps 60 | https://ligolang.org/docs/reference/map-reference 61 | */ 62 | -------------------------------------------------------------------------------- /lib/k13_set_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Sets are unordered collections of values of the same type, 5 | like lists are ordered collections. Like the mathematical 6 | sets and lists, sets can be empty and, if not, elements of 7 | sets in LIGO are unique, whereas they can be repeated in a 8 | list. 9 | */ 10 | 11 | /* 12 | In JsLIGO, the empty set is denoted by the predefined value Set.empty. 13 | */ 14 | 15 | const my_empty_set: set = Set.empty; 16 | 17 | /* 18 | In JsLIGO, you can define a non-empty set using the Set.literal function 19 | which takes a list of elements & returns a set. 20 | */ 21 | 22 | const my_set: set = Set.literal(list([3, 2, 2, 1])); 23 | 24 | /* 25 | A set can be altered thanks to Set.add, tested using Set.mem. 26 | 27 | Set.mem(3,Set.add(3,my_empty_set)) = true 28 | */ 29 | 30 | /* 31 | A folded operation is the most general of iterations. The folded function takes 32 | two arguments: an accumulator and the structure element at hand, with which it 33 | then produces a new accumulator. This enables having a partial result that becomes 34 | complete when the traversal of the data structure is over. 35 | 36 | The predefined fold over sets is called Set.fold, however an additional function, 37 | Set.fold_right, has been added. 38 | */ 39 | 40 | /* 41 | Write a function waiting for a list of integers and returning the addition of all elements. 42 | 43 | add_all(Set.litera(list([1,2,3]))) = 1 + 2 + 3 // 6 44 | */ 45 | 46 | export const add_all = (l:set): int => { 47 | K.todo("Should return the addition of all element"); 48 | }; 49 | 50 | /* 51 | We may want to change all the elements of a given list by applying to them a function. 52 | Rewrite the map function of a set. 53 | 54 | map(f,Set.literal(list([1,2]))) = Set.literal(list([f(1),f(2)])) 55 | */ 56 | 57 | export const map = (f: (i:int) => int, l:set) : set => { 58 | K.todo("Should perform the map over a set"); 59 | }; 60 | -------------------------------------------------------------------------------- /lib/k14_exception.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | In some cases it is necessary to interrupt the flow of 5 | execution with a failure: this is where the predefined 6 | function failwith comes in. 7 | 8 | This is important when a contract has not the required 9 | context or properties it should abort quickly in order 10 | to consume the less gaz as possible. 11 | */ 12 | 13 | /* 14 | Write a function throwing an error if the parameter is a 15 | negative number; unit otherwise 16 | */ 17 | 18 | export const assert_not_negative = (i:int): unit => { 19 | assert_with_error(i >= 0, "Negative number") 20 | // K.todo("Should fails if the parameter is negative"); 21 | }; 22 | 23 | /* 24 | Contract entry point for the tests 25 | */ 26 | export const main = (p:int, _s:unit): [list, unit] => { 27 | [list([]) as list, assert_not_negative(p)]; 28 | }; 29 | 30 | /* 31 | Reference: https://ligolang.org/docs/language-basics/exceptions 32 | */ 33 | -------------------------------------------------------------------------------- /lib/k15_imperative.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | JsLIGO loops can iterate through the contents of a collection, that is, a list, a set or a map. 5 | This is done with a loop of the form: 6 | 7 | for (const of ) . 8 | 9 | */ 10 | 11 | export const sum_all = (l:list) : nat => { 12 | // K.todo("Should compute the sum of all elements using for-loop control structure") 13 | }; 14 | 15 | /* 16 | JsLIGO also supports iteration through while loops. This control structure is used when data is not 17 | list, set or map. 18 | */ 19 | 20 | export const factorial = (n:nat) : nat => { 21 | // K.todo("Should compute factorial using while-loop control structure") 22 | }; 23 | 24 | /* 25 | Reference: https://ligolang.org/docs/language-basics/loops 26 | */ 27 | -------------------------------------------------------------------------------- /lib/k16_recursive_function.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Write a function able to compute the factorial. 5 | This function should be recursive and don't use 6 | */ 7 | 8 | export const factorial = (n:nat) : nat => { 9 | K.todo("Should compute factorial using a recursive function"); 10 | }; 11 | 12 | /* 13 | Write a function able to sum all element of a given list of integers. 14 | This function should be recursive and don't use 15 | */ 16 | 17 | export const sum_all = (l:list) : int => { 18 | K.todo("Should sum all elements of the list using a recursive function"); 19 | }; 20 | 21 | /* 22 | Reference: https://ligolang.org/docs/language-basics/functions#recursive-function 23 | */ -------------------------------------------------------------------------------- /lib/k17_polymorphism.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | In JsLIGO data structures like list, map, set, option etc. are called parametric 5 | types. 6 | 7 | Its also possible to define parametric types. For instances we can specify a 8 | type reflecting a result which can be a success or a failure. 9 | */ 10 | 11 | type result = | ["Success", t] | ["Failure", string]; 12 | 13 | /* 14 | Then after polymorphic functions can be designed in order to manipulate such data 15 | type. For this purpose the function type and the function expressions are separated. 16 | 17 | For instance, is_success can be expressed as follow. 18 | */ 19 | 20 | export const is_success : ((t:result)=>bool) = t => { 21 | match(t, { 22 | Failure: r => false, 23 | Success: r => true 24 | }); 25 | }; 26 | 27 | /* 28 | Finally the map function dedicated to the result data type can be easily define. 29 | */ 30 | 31 | export const map : ((f:(a:A)=>B, r:result) => result) = (f:(a:A)=>B, r:result) => { 32 | match(r, { 33 | Failure: m => Failure(m), 34 | Success: a => Success(f(a)) 35 | }); 36 | }; 37 | 38 | /* 39 | Reference: https://ligolang.org/docs/advanced/polymorphism 40 | */ -------------------------------------------------------------------------------- /solution/common/todo.jsligo: -------------------------------------------------------------------------------- 1 | export const todo : ((message:string) => T) = 2 | message => failwith(message); 3 | -------------------------------------------------------------------------------- /solution/k01_native_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | LIGO is strongly and statically typed. This means that the compiler checks 5 | how your contract processes data, ensuring that each function's expectations 6 | are met. If it passes the test, your contract will not fail at run-time due 7 | to some inconsistent assumptions on your data. This is called type checking. 8 | 9 | In addition, LIGO types are built on top of Michelson's type system. 10 | */ 11 | 12 | export const hello = () : string => 13 | "Hello"; 14 | // K.todo("Should return \"Hello\""); 15 | 16 | export const one = () : int => 17 | 1; 18 | // K.todo("Should return 1"); 19 | 20 | export const one_nat = () : nat => 21 | 1 as nat; 22 | // K.todo("Should return 1 nat"); 23 | 24 | export const one_tez = () : tez => 25 | 1 as tez; 26 | // K.todo("Should return 1 tez"); 27 | 28 | export const true_value = () : bool => 29 | true; 30 | // K.todo("Should return true"); 31 | 32 | /* 33 | Reference: https://ligolang.org/docs/language-basics/math-numbers-tez 34 | */ -------------------------------------------------------------------------------- /solution/k02_numerical_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | LIGO offers three built-in numerical types: 5 | - int are integers, such as 10, -6 and 0. But there is only one canonical zero: 0 6 | (so, for instance, -0 and 00 are invalid). 7 | - nat are natural numbers (integral numbers greater than or equal to zero). They are 8 | followed by the annotation as nat such as 3 as nat, 12 as nat and 0 as nat for the 9 | natural zero. The same restriction on zero as integers applies: 0 as nat is the 10 | only way to specify the natural zero. 11 | - tez are units of measure of Tezos tokens. They can be decimals and are followed by 12 | annotation tez such as 3 as tez. You can also type units of millionth of tez, using 13 | the annotation as mutez after a natural literal, such as 10000 as mutez or 0 as mutez. 14 | */ 15 | 16 | /* 17 | Addition in LIGO is accomplished by means of the + infix operator. Some type constraints 18 | apply, for example you cannot add a value of type tez to a value of type nat. 19 | */ 20 | 21 | export const add_int = (n1:int,n2:int) : int => 22 | n1 + n2; 23 | // K.todo("Should add two integers"); 24 | 25 | export const add_int_and_nat = (n1:int,n2:nat) : int => 26 | n1 + n2; 27 | // K.todo("Should add an integer and a natural"); 28 | 29 | export const add_nat = (n1:nat,n2:nat) : nat => 30 | n1 + n2; 31 | // K.todo("Should add two naturals"); 32 | 33 | export const add_tez = (n1:tez,n2:tez) : tez => 34 | n1 + n2; 35 | // K.todo("Should add two tez values"); 36 | 37 | /* 38 | Subtractions follow the same principles. 39 | Warning: when subtracting two nats, the result is an int 40 | */ 41 | 42 | export const substract_int = (n1:int,n2:int) : int => 43 | n1 - n2; 44 | // K.todo("Should substract two integers"); 45 | 46 | export const substract_int_and_nat = (n1:int,n2:nat) : int => 47 | n1 - n2; 48 | // K.todo("Should substract and integer and a nat"); 49 | 50 | export const substract_nat = (n1:nat,n2:nat) : int => 51 | n1 - n2; 52 | // K.todo("Should substract two naturals"); 53 | 54 | export const substract_tez = (n1:tez,n2:tez) : option => 55 | n1 - n2; 56 | // K.todo("Should substract two tez values"); 57 | 58 | /* 59 | Numerical conversion can be done from int to nat and vice versa 60 | */ 61 | 62 | export const int_from_nat = (n1:nat) : int => 63 | int(n1); 64 | // K.todo("Should cast a nat to an int"); 65 | 66 | export const nat_from_int = (n1:int) : nat => 67 | abs(n1); 68 | // K.todo("Should cast an int to a nat"); 69 | 70 | /* 71 | Reference: https://ligolang.org/docs/language-basics/math-numbers-tez 72 | */ -------------------------------------------------------------------------------- /solution/k03_string_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Strings can be concatenated using the + operator. 5 | */ 6 | 7 | export const concat = (n1:string,n2:string) : string => 8 | n1 + n2; 9 | // K.todo("Should return concat n1 and n2"); 10 | 11 | /* 12 | Strings can be sliced using a built-in function String.sub which takes three parameters: 13 | - an offset describing the index of first character that will be copied 14 | - the length describing the number of characters that will be copied (starting from the given offset) 15 | - the string being sliced 16 | */ 17 | 18 | export const first = (n: string) : string => 19 | // We assume `n` is never the empty string here 20 | String.sub(0 as nat, 1 as nat, n); 21 | // K.todo("Should return The first character of n1"); 22 | 23 | /* 24 | The length of a string can be found using a built-in function String.length 25 | */ 26 | 27 | export const length = (n: string) : nat => 28 | String.length(n); 29 | // K.todo("Should return The length of n"); 30 | 31 | /* 32 | References: 33 | https://ligolang.org/docs/language-basics/strings-bytes 34 | https://ligolang.org/docs/reference/string-reference 35 | 36 | */ -------------------------------------------------------------------------------- /solution/k04_bytes_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Bytes can be concatenated using the + operator. 5 | */ 6 | 7 | export const concat = (n1:bytes,n2:bytes) : bytes => 8 | Bytes.concat(n1, n2); 9 | // K.todo("Should return concat n1 and n2"); 10 | 11 | /* 12 | Bytes can be sliced using a built-in function bytes.sub which takes three parameters: 13 | - an offset describing the index of first character that will be copied 14 | - the length describing the number of characters that will be copied (starting from the given offset) 15 | - the bytes being sliced 16 | */ 17 | 18 | export const first = (n: bytes) : bytes => 19 | Bytes.sub(0 as nat, 1 as nat, n); 20 | // K.todo("Should return The first byte of n"); 21 | 22 | /* 23 | The length of a bytes can be found using a built-in function bytes.length 24 | */ 25 | 26 | export const length = (n1:bytes) : nat => 27 | Bytes.length(n1); 28 | // K.todo("Should return The length of n1"); 29 | 30 | /* 31 | References: 32 | https://ligolang.org/docs/language-basics/strings-bytes 33 | https://ligolang.org/docs/reference/bytes-reference 34 | */ -------------------------------------------------------------------------------- /solution/k05_conditional.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | The type of a boolean value is bool. Here is how to define a boolean value: 5 | */ 6 | 7 | const a: bool = true; 8 | const b: bool = false; 9 | 10 | /* 11 | Comparing values 12 | */ 13 | 14 | const compare_string = (s1:string,s2:string): bool => { 15 | (s1 == s2); 16 | // K.todo("Should compare two strings"); 17 | }; 18 | 19 | const boolean_and = (b1:bool,b2:bool): bool => { 20 | (b1 && b2); 21 | // K.todo("Should return the boolean and of b1, b2 "); 22 | }; 23 | 24 | const boolean_or = (b1:bool,b2:bool): bool => { 25 | (b1 || b2); 26 | // K.todo("Should return the boolean or of b1, b2"); 27 | }; 28 | 29 | /* 30 | Conditional selection 31 | */ 32 | 33 | const selection = (i1:int,i2:int): string => { 34 | if (i1 < i2) { 35 | return "i1 < i2"; 36 | } else if (i1 == i2) { 37 | return "i1 = i2"; 38 | } else { 39 | return "i1 > i2"; 40 | }; 41 | 42 | // K.todo("Should return a string denoting the comparison i.e. 'i1 < i2' or 'i1 = i2' or 'i1 > i2'"); 43 | }; 44 | 45 | /* 46 | Reference: https://ligolang.org/docs/language-basics/boolean-if-else 47 | */ -------------------------------------------------------------------------------- /solution/k06_function.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Functions in JsLIGO are defined using the let or const keyword, like other values. 5 | The difference is that parameters are provided after the value name, with its type, 6 | then followed by the return type. 7 | 8 | Here is how you define a basic function that sums two integers: 9 | 10 | const add = (a:int, b:int): int => a + b; 11 | 12 | This syntax is equivalent to 13 | 14 | const add = ([a, b]: [int, int]): int => a + b; 15 | */ 16 | 17 | /* 18 | Write the function which computes the maximum of two integers 19 | 20 | max(1,2) returns 2 21 | */ 22 | 23 | const max = (a:int,b:int): int => { 24 | if (a < b) { 25 | return b; 26 | } else { 27 | return a; 28 | } 29 | }; 30 | // K.todo("Should return the function computing the max of two integers"); 31 | 32 | /* 33 | Write a function waiting for a string which returns a function 34 | waiting for another string and returns the string concatenation. 35 | 36 | concat("a")("b") = "ab" 37 | */ 38 | 39 | export const concat = (a:string) : ((b:string) => string) => { 40 | const do_concat = (b:string): string => (a + b); 41 | // K.todo("Should provide a function performing a concatenation"); 42 | return do_concat; 43 | }; 44 | 45 | /* 46 | JsLIGO offers the capability to receive a functions as a parameter. 47 | */ 48 | 49 | export const handle = (f:((b:string) => string), a:string) : string => { 50 | return f(a); 51 | // K.todo("Should return the application of `f` on `a`"); 52 | }; 53 | 54 | /* ------------------------------------------------------------------------- 55 | | 56 | | Optional section .... 57 | | 58 | +------------------------------------------------------------------------ */ 59 | 60 | export const do_optional_part = true; 61 | 62 | /* 63 | Finally we can write functions which return another function. On typical 64 | one is the capability to transform a function waiting for two parameters 65 | to a function waiting for a parameter returning another function waiting 66 | also for one parameter. Such transformation is called Currying. 67 | 68 | Reference: https://en.wikipedia.org/wiki/Currying 69 | */ 70 | 71 | type uncurried = (a:int, b:int) => int; 72 | type curried = (a:int) => ((b:int) => int); 73 | 74 | /* 75 | Write the fonction able to do Uncurrying i.e. transform a function from 76 | curried form to uncurried form. 77 | */ 78 | 79 | export const uncurry = (f:curried): uncurried => { 80 | const uncurried : uncurried = (a: int,b:int): int => f(a)(b); 81 | return uncurried; 82 | // K.todo("Should return uncurried version of the parametric function"); 83 | }; 84 | 85 | /* 86 | Write the fonction able to do Currying i.e. transform a function from 87 | uncurried to curried. 88 | */ 89 | 90 | export const curry = (f:uncurried): curried => { 91 | const curried : curried = (a: int): ((b:int) => int) => (b => f(a,b)); 92 | return curried; 93 | // K.todo("Should return curried version of the parametric function"); 94 | }; 95 | 96 | /* 97 | Reference: https://ligolang.org/docs/language-basics/functions 98 | */ 99 | 100 | -------------------------------------------------------------------------------- /solution/k07_tuple_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Tuples gather a given number of values in a specific order and those values, 5 | called components, can be retrieved by their index (position). 6 | */ 7 | 8 | type point = [int,int]; 9 | 10 | export const new_point = (x:int,y:int): point => [x,y]; 11 | 12 | const abscissa = (p:point): int => p[0]; 13 | 14 | const ordinate = (p:point): int => p[1]; 15 | 16 | /* 17 | Reference: https://ligolang.org/docs/language-basics/sets-lists-tuples#tuples 18 | */ -------------------------------------------------------------------------------- /solution/k08_record_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Records are one-way data of different types can be packed into a single type. A record is made of a set of fields, 5 | which are made of a field name and a field type. 6 | 7 | Note: Record expressions should be in parenthesis 8 | */ 9 | 10 | type account_data = { 11 | balance: tez, 12 | transactions: nat 13 | }; 14 | 15 | export const account = () : account_data => 16 | ({ balance: 5 as tez, transactions: 3 as nat }); 17 | // K.todo("Should return an account data with a balance of 5 tez and 3 transactions"); 18 | 19 | /* 20 | If we want the contents of a given field, we use the (.) infix operator 21 | */ 22 | 23 | export const balance : tez = (account()).balance; 24 | 25 | /* 26 | Given a record value, it is a common design pattern to update only a small number of its fields. 27 | Instead of copying the fields that are unchanged, LIGO offers a way to only update the fields that are modified. 28 | 29 | One way to understand the update of record values is the functional update. The idea is to have an expression 30 | whose value is the updated record. 31 | 32 | Reference; https://ligolang.org/docs/language-basics/maps-records#functional-updates 33 | */ 34 | 35 | export const modify_account = (account: account_data) : account_data => 36 | ({ ...account, transactions: 5 as nat }); 37 | // K.todo("Should return an account data with 5 transactions"); 38 | 39 | /* 40 | Reference: https://ligolang.org/docs/language-basics/maps-records 41 | */ 42 | -------------------------------------------------------------------------------- /solution/k09_variant_types.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | A variant type is a type that defines a type by cases. For instance we 5 | can define a type reflecting what can be played during a shifumi game: 6 | */ 7 | 8 | type play = | ["Paper"] | ["Scissor"] | ["Stone"]; 9 | 10 | /* 11 | Here `Paper`, `Scissor` and `Stone` are called data constructors. Then each 12 | data can be created thanks to its corresponding constructor. 13 | */ 14 | 15 | const paper : play = Paper(); 16 | const scissor : play = Scissor(); 17 | const stone : play = Stone(); 18 | 19 | /* 20 | Define a coin flipping type with `Heads` and `Tails`. 21 | */ 22 | 23 | export type coin = 24 | | ["Heads"] | ["Tails"]; 25 | // Definition the type replacing unit type 26 | 27 | export const new_heads = (): coin => 28 | Heads(); 29 | // K.todo("Should return a heads data"); 30 | 31 | export const new_tails = (): coin => 32 | Tails(); 33 | // K.todo("Should return a tails data"); 34 | 35 | /* 36 | Pattern matching is similar to the switch construct in JavaScript, and can be used 37 | to route the program's control flow based on the value of a variant, record, tuple, 38 | or list. 39 | 40 | A component of a pattern can be discarded by using a wildcard _ instead of a variable name. 41 | 42 | Note: LIGO will warn about unused variables bound in patterns in the same way that function 43 | arguments are warned about. Variable names beginning with _ can be used as a binder to 44 | prevent warnings. 45 | 46 | Back to the shifumi variant type the pattern matching is expressed thanks to a dedicated `match`` 47 | control structure. 48 | */ 49 | 50 | export const is_paper = (p:play): bool => { 51 | match(p, { 52 | Paper: () => true, 53 | Scissor: () => false, 54 | Stone: () => false 55 | }); 56 | }; 57 | 58 | /* 59 | In this example we can see how the switch case is performed thanks to the recognition of all 60 | data constructors. 61 | */ 62 | 63 | /* 64 | Define a function flipping a coins. Then if it's 65 | a Tails it returns a Heads and vice-versa. 66 | */ 67 | 68 | export const flip_coin = (c:coin): coin => { 69 | match(c,{ 70 | Heads: () => new_tails(), 71 | Tails: () => new_heads(), 72 | }); 73 | // K.todo("Should return a flipped coin. Heads should be transformed to Tails and Tails should be transformed to Heads."); 74 | }; 75 | 76 | /* 77 | The previous variants are equivalent to the enumerated types found in Java, C++, JavaScript etc. because each data constructor 78 | does not hold any value! 79 | 80 | In fact, JsLIGO variant can be more complex and can hold values. 81 | 82 | For instance a point can be expressed thanks to its absissa and ordinate or thanks to its radius and angles. 83 | */ 84 | 85 | type coordinate = { absissa : int, ordinate: int}; 86 | type polar = { radius: int, angle: int}; 87 | 88 | type point = | ["Coordinate", coordinate] | ["Polar", polar]; 89 | 90 | /* 91 | Then the pattern matching recognize and deconstruct data in order to retrieve intrnale values. 92 | */ 93 | 94 | export const is_coordinate = (p:point): bool => { 95 | match(p,{ 96 | Coordinate : _c => true, // p can be used as usual since it's a record, 97 | Polar : _p => false, // p can be used as usual since it's a record, 98 | }); 99 | }; 100 | 101 | /* 102 | Back to the `coin` we can elaborate a simple game. For this purpose we define first 103 | the game structure and a functional type for the coin 104 | */ 105 | 106 | type game = { heads: nat, tails: nat }; 107 | type flip = ((u:unit) => coin); 108 | 109 | /* 110 | Define a type variant with Play containing a flip function and a Reset. Then after 111 | define a function play which manages heads and tails of a game each time a Play and 112 | a Reset is received. 113 | */ 114 | 115 | export type turn = | ["Play", flip] | ["Reset"]; 116 | 117 | export const play = (t:turn,g:game): game => { 118 | match(t, { 119 | Play: f => { 120 | match(f(), { 121 | Heads: () => ({ ...g, heads: g.heads + (1 as nat) }), 122 | Tails: () => ({ ...g, tails: g.tails + (1 as nat) }), 123 | }); 124 | }, 125 | Reset: () => ({ heads:0 as nat, tails:0 as nat}) 126 | }); 127 | // K.todo("Should return game reflecting the turn played."); 128 | }; 129 | 130 | /* 131 | Reference: https://ligolang.org/docs/language-basics/unit-option-pattern-matching#variant-types 132 | */ -------------------------------------------------------------------------------- /solution/k10_option.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | The option type is a predefined variant type that is used to express 5 | whether there is a value of some type or none. This is especially useful 6 | when calling a partial function, that is, a function that is not defined 7 | for some inputs. In that case, the value of the option type would be None, 8 | otherwise Some (v), where v is some meaningful value of any type. 9 | */ 10 | 11 | /* 12 | Write the divide operation returning `None` when we try to divide by `0` or 13 | the division in a `Some`. 14 | */ 15 | 16 | export const div = (a:int, b: int): option => { 17 | if (b == 0) { 18 | return None(); 19 | } else { 20 | return Some(a/b); 21 | }; 22 | // K.todo("Should divide and return the result in a Some, or Return None when it's not possible"); 23 | }; 24 | 25 | /* 26 | Give a function which acts on int, write the function with an option and 27 | a function which perform the paramtric function when it's a some and do 28 | nothing when it's a None. 29 | */ 30 | 31 | export const map = (f:((n:int) => int), s: option): option => { 32 | match(s, { 33 | Some : v => Some(f(v)), 34 | None : () => None() 35 | }); 36 | // K.todo("Should apply the function on the value in a Some and return an option with the new value, Return None otherwise"); 37 | }; 38 | 39 | /* 40 | Write the function returning the first character as a string or nothing. 41 | */ 42 | 43 | export const first = (n: string) : option => { 44 | if (String.length(n) == (0 as nat)) { 45 | return None(); 46 | } else { 47 | return Some (String.sub(0 as nat, 1 as nat, n)); 48 | } 49 | // K.todo("Should return The first character of n"); 50 | } 51 | 52 | /* 53 | Reference: https://ligolang.org/docs/language-basics/unit-option-pattern-matching#optional-values 54 | */ -------------------------------------------------------------------------------- /solution/k11_list_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Lists are linear collections of elements of the same type. Linear means that, 5 | in order to reach an element in a list, we must visit all the elements before 6 | (sequential access). Elements can be repeated, as only their order in the collection 7 | matters. The first element is called the head, and the sub-list after the head is 8 | called the tail. 9 | */ 10 | 11 | /* 12 | Defining lists can be done using literal expressions. 13 | */ 14 | 15 | export const no_ints = (): list => list([]); 16 | export const single_int = (p:int): list => list([p]); 17 | 18 | /* 19 | In JsLIGO, the cons operator is infix and noted , .... It is not symmetric: on the left 20 | lies the element to cons, and, on the right, a list on which to cons. 21 | */ 22 | 23 | export const cons = (h:int, t:list): list => { 24 | list([ h, ...t ]); 25 | }; 26 | 27 | /* 28 | Pattern matching for lists has its own syntax. 29 | 30 | match(my_int_list, list([ 31 | ([] : list) => ..., 32 | ([hd, ...tl] : list) => ... 33 | ])); 34 | */ 35 | 36 | /* 37 | Write a predicate checking the emptiness of a given list. 38 | */ 39 | 40 | export const is_empty = (v : list) : bool => { 41 | match(v, list([ 42 | ([] : list) => true, 43 | ([_hd, ..._tl] : list) => false 44 | ])); 45 | // K.todo("Should return the head of the parametric list in a Some; None otherwise"); 46 | }; 47 | 48 | /* 49 | Given a list of int write the function which returns the first element. For this purpose we use the 50 | option type for the result. 51 | 52 | head(list([1,2])) = Some(1) 53 | head(list([])) = None 54 | */ 55 | 56 | export const head = (l:list): option => { 57 | match(l, list([ 58 | ([]: list) => None(), 59 | ([h, ..._t]: list) => Some(h) 60 | ])); 61 | // K.todo("Should return the head of the parametric list in a Some; None otherwise"); 62 | }; 63 | 64 | /* 65 | We may want to change all the elements of a given list by applying to them a function. 66 | This is called a map operation, not to be confused with the map data structure. The 67 | predefined functional iterator implementing the mapped operation over lists is called 68 | List.map and is used as follows. 69 | */ 70 | 71 | /* 72 | Write a function taking a list of integer and return a list where all elements have been 73 | added to one. 74 | 75 | increment(list([1,2])) = list([2,3]) 76 | */ 77 | 78 | export const increment = (l:list) : list => { 79 | List.map((i:int):int => i + 1, l); 80 | // K.todo("Should return a list where all elements have been incremented by 1"); 81 | }; 82 | 83 | /* 84 | Folded operation over Lists is the most general of iterations. The folded function takes 85 | two arguments: an accumulator and the structure element at hand, with which it then produces 86 | a new accumulator. This enables having a partial result that becomes complete when the 87 | traversal of the data structure is over. Folding can be done in two ways, labelled with 88 | the directions left and right. 89 | 90 | Take for example a function f, a list [1; 2; 3], and an accumulator that's just an 91 | empty list. A rough approximation of the result of a left fold would look like 92 | f(f(f([], 1), 2), 3), while a right fold would instead look like f(1, f(2, f(3, []))). 93 | */ 94 | 95 | /* 96 | Write a function waiting for a list of integers and returning the addition of all elements. 97 | 98 | add_all(list([1,2,3])) = 1 + 2 + 3 // 6 99 | */ 100 | 101 | export const add_all = (l:list): int => { 102 | List.fold_right((e:int,r:int):int => e + r, l, 0); 103 | // K.todo("Should return the addition of all element"); 104 | }; 105 | 106 | /* 107 | Write the fonction reverse for integer lists. 108 | 109 | reverse(list([1,2,3])) = list([3,2,1]) 110 | */ 111 | 112 | export const reverse = (l:list): list => { 113 | List.fold_left((r:list,e:int):list => list([e,...r]), list([]) as list, l); 114 | // K.todo("Should return the reversed list"); 115 | }; 116 | 117 | /* 118 | References: https://ligolang.org/docs/language-basics/sets-lists-tuples#lists 119 | https://ligolang.org/docs/reference/list-reference 120 | */ -------------------------------------------------------------------------------- /solution/k12_map_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Maps are a data structure which associate values of the same type to 5 | values of the same type. The former are called key and the latter values. 6 | Together they make up a binding. An additional requirement is that the 7 | type of the keys must be comparable, in the Michelson sense. 8 | */ 9 | 10 | /* 11 | Here is how to create an empty map. 12 | */ 13 | 14 | const empty: map = Map.empty; 15 | 16 | /* 17 | And here is how to create a non-empty map value: 18 | */ 19 | 20 | export const transpose : map = Map.literal(list([ [0, "Zero"], [1, "One"] ])); 21 | 22 | /* 23 | Access is done thanks to `Map.find_opt`. Propose a function which tries to find 24 | a value using a key and if it does not exist 25 | */ 26 | 27 | export const find = (m : map, k:int, d:string) : string => { 28 | match(Map.find_opt(k,m), { 29 | Some: s => s, 30 | None: () => d 31 | }); 32 | // K.todo("Should return the value associated to the key; The default value otherwise"); 33 | }; 34 | 35 | /* 36 | Modification can done thanks to `Map.update`. Propose a function which updates a value using a key 37 | and it return the new map and the previous value as an option 38 | */ 39 | 40 | export const update = (m : map, k:int, v:string) : [option,map] => { 41 | const previous = Map.find_opt(k,m); 42 | const updated = Map.add(k,v,m); 43 | [previous, updated] 44 | // K.todo("Should return the value associated to the key; The default value otherwise"); 45 | }; 46 | 47 | /* 48 | Folded operations over maps takes two arguments: an accumulator and the structure element 49 | at hand, with which it then produces a new accumulator. This enables having a partial result 50 | that becomes complete when the traversal of the data structure is over. 51 | 52 | The predefined functional iterator implementing the folded operation over maps is called 53 | Map.fold and is used as follows. 54 | */ 55 | 56 | /* 57 | Give a function which acts on int, write the function with a map and 58 | a function which perform the parametric function. 59 | */ 60 | 61 | export const map = (f:((k:int,v:string) => string), m: map): map => { 62 | Map.map(f, m); 63 | // K.todo("Should apply the function on all pairs of key value and return the transformed map "); 64 | }; 65 | 66 | /* 67 | Reference: https://ligolang.org/docs/language-basics/maps-records#maps 68 | https://ligolang.org/docs/reference/map-reference 69 | */ -------------------------------------------------------------------------------- /solution/k13_set_type.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Sets are unordered collections of values of the same type, 5 | like lists are ordered collections. Like the mathematical 6 | sets and lists, sets can be empty and, if not, elements of 7 | sets in LIGO are unique, whereas they can be repeated in a 8 | list. 9 | */ 10 | 11 | /* 12 | In JsLIGO, the empty set is denoted by the predefined value Set.empty. 13 | */ 14 | 15 | const my_empty_set: set = Set.empty; 16 | 17 | /* 18 | In JsLIGO, you can define a non-empty set using the Set.literal function 19 | which takes a list of elements & returns a set. 20 | */ 21 | 22 | const my_set: set = Set.literal(list([3, 2, 2, 1])); 23 | 24 | /* 25 | A set can be altered thanks to Set.add, tested using Set.mem. 26 | 27 | Set.mem(3,Set.add(3,my_empty_set)) = true 28 | */ 29 | 30 | /* 31 | A folded operation is the most general of iterations. The folded function takes 32 | two arguments: an accumulator and the structure element at hand, with which it 33 | then produces a new accumulator. This enables having a partial result that becomes 34 | complete when the traversal of the data structure is over. 35 | 36 | The predefined fold over sets is called Set.fold, however an additional function, 37 | Set.fold_right, has been added. 38 | */ 39 | 40 | /* 41 | Write a function waiting for a list of integers and returning the addition of all elements. 42 | 43 | add_all(Set.litera(list([1,2,3]))) = 1 + 2 + 3 // 6 44 | */ 45 | 46 | export const add_all = (l:set): int => { 47 | Set.fold((e:int,r:int):int => e + r, l, 0); 48 | // K.todo("Should return the addition of all element"); 49 | }; 50 | 51 | /* 52 | We may want to change all the elements of a given list by applying to them a function. 53 | Rewrite the map function of a set. 54 | 55 | map(f,Set.literal(list([1,2]))) = Set.literal(list([f(1),f(2)])) 56 | */ 57 | 58 | export const map = (f: (i:int) => int, l:set) : set => { 59 | Set.fold((r:set,e:int):set => Set.add(f(e),r), l, Set.empty as set); 60 | // K.todo("Should perform the map over a set"); 61 | }; 62 | -------------------------------------------------------------------------------- /solution/k14_exception.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | In some cases it is necessary to interrupt the flow of 5 | execution with a failure: this is where the predefined 6 | function failwith comes in. 7 | 8 | This is important when a contract has not the required 9 | context or properties it should abort quickly in order 10 | to consume the less gaz as possible. 11 | */ 12 | 13 | /* 14 | Write a function throwing an error if the parameter is a 15 | negative number; return unit otherwise 16 | */ 17 | 18 | export const assert_not_negative = (i:int): unit => { 19 | assert_with_error(i >= 0, "Negative number"); 20 | // K.todo("Should fails if the parameter is negative"); 21 | }; 22 | 23 | /* 24 | Contract entry point for the tests 25 | */ 26 | export const main = (p:int, _s:unit): [list, unit] => { 27 | [list([]) as list, assert_not_negative(p)]; 28 | }; 29 | 30 | /* 31 | Reference: https://ligolang.org/docs/language-basics/exceptions 32 | */ 33 | -------------------------------------------------------------------------------- /solution/k15_imperative.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | JsLIGO loops can iterate through the contents of a collection, that is, a list, a set or a map. 5 | This is done with a loop of the form: 6 | 7 | for (const of ) . 8 | 9 | */ 10 | 11 | export const sum_all = (l:list) : nat => { 12 | let result = 0 as nat; 13 | for (const e of l) { 14 | result = result + e; 15 | }; 16 | return result; 17 | 18 | // K.todo("Should compute the sum of all elements using for-loop control structure") 19 | }; 20 | 21 | /* 22 | JsLIGO also supports iteration through while loops. This control structure is used when data is not 23 | list, set or map. 24 | */ 25 | 26 | export const factorial = (n:nat) : nat => { 27 | 28 | // variable used for accumulation 29 | let value = 1 as nat; 30 | 31 | let index = 0 as nat; 32 | while(index < n) { 33 | index = index + (1 as nat); 34 | value = value * index; 35 | }; 36 | 37 | // K.todo("Should compute factorial using while-loop control structure") 38 | 39 | return value; 40 | } 41 | 42 | /* 43 | Reference: https://ligolang.org/docs/language-basics/loops 44 | */ -------------------------------------------------------------------------------- /solution/k16_recursive_function.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | Write a function able to compute the factorial. 5 | This function should be recursive and don't use 6 | */ 7 | 8 | export const factorial = (n:nat) : nat => { 9 | const factorial_terminal = (acc:nat,max:nat,n:nat) : nat => { 10 | if (n > max) { 11 | return acc; 12 | } else { 13 | return factorial_terminal([acc*n,max,n + (1 as nat)]); 14 | } 15 | }; 16 | 17 | factorial_terminal(1 as nat,n,1 as nat); 18 | // K.todo("Should compute factorial using a recursive function"); 19 | }; 20 | 21 | /* 22 | Write a function able to sum all element of a given list of integers. 23 | This function should be recursive and don't use 24 | */ 25 | 26 | export const sum_all = (l:list) : int => { 27 | const sum_all_terminal = (acc:int,l:list) : int => { 28 | match(l, list([ 29 | ([]:list) => acc, 30 | ([h,...t]:list) => sum_all_terminal(acc + h,t) 31 | ])); 32 | }; 33 | 34 | sum_all_terminal(0,l); 35 | // K.todo("Should sum all elements of the list using a recursive function"); 36 | }; 37 | -------------------------------------------------------------------------------- /solution/k17_polymorphism.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/todo.jsligo" "K" 2 | 3 | /* 4 | In JsLIGO data structures like list, map, set, option etc. are called parametric 5 | types. 6 | 7 | Its also possible to define parametric types. For instances we can specify a 8 | type reflecting a result which can be a success or a failure. 9 | */ 10 | 11 | type result = | ["Success", t] | ["Failure", string]; 12 | 13 | /* 14 | Then after polymorphic functions can be designed in order to manipulate such data 15 | type. For this purpose the function type and the function expressions are separated. 16 | 17 | For instance, is_success can be expressed as follow. 18 | */ 19 | 20 | export const is_success : ((t:result)=>bool) = t => { 21 | match(t, { 22 | Failure: r => false, 23 | Success: r => true 24 | }); 25 | }; 26 | 27 | /* 28 | Finally the map function dedicated to the result data type can be easily define. 29 | */ 30 | 31 | export const map : ((f:(a:A)=>B, r:result) => result) = (f:(a:A)=>B, r:result) => { 32 | match(r, { 33 | Failure: m => Failure(m), 34 | Success: a => Success(f(a)) 35 | }); 36 | }; 37 | 38 | /* 39 | Reference: https://ligolang.org/docs/advanced/polymorphism 40 | */ -------------------------------------------------------------------------------- /test/common/check.jsligo: -------------------------------------------------------------------------------- 1 | 2 | const prefix = Option.unopt(Test.chr(27 as nat)); 3 | const reset = prefix + "[0m"; 4 | const red = prefix + "[31m" 5 | const green = prefix + "[32m" 6 | const to_green = (s:string) : string => green + s + reset; 7 | const to_red = (s:string) : string => red + s + reset; 8 | 9 | type error = | ["ERROR", {received:a} ]; 10 | 11 | const equal : ((a:T, b:T) => bool) = p => { 12 | Bytes.pack(p[0]) == Bytes.pack(p[1]); 13 | }; 14 | 15 | const status = (b:bool) : string => { 16 | if (b) { 17 | return to_green( "[SUCCESS]"); 18 | } else { 19 | return to_red("[FAILURE]"); 20 | } 21 | }; 22 | 23 | const assert_equal : ((result:T, expected:T) => unit) = p => { 24 | const result = equal(p[0], p[1]); 25 | 26 | Test.println(status(result) + " expected: " + Test.to_string(p[1])); 27 | 28 | if (result == false) { 29 | Test.println(" received: " + Test.to_string(p[0])); 30 | }; 31 | }; 32 | 33 | const cases = (l:list<(u:unit)=>unit>):unit => { 34 | List.iter((f:(u:unit)=>unit):unit => f(unit), l); 35 | }; 36 | -------------------------------------------------------------------------------- /test/k01.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k01_native_types.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.hello(), "Hello"); 5 | const case02 = ():unit => Check.assert_equal(Koan.one(), 1); 6 | const case03 = ():unit => Check.assert_equal(Koan.one_nat(), 1 as nat); 7 | const case04 = ():unit => Check.assert_equal(Koan.one_tez(), 1 as tez); 8 | const case05 = ():unit => Check.assert_equal(Koan.true_value(), true); 9 | 10 | const test = Check.cases(list([ 11 | case01, case02, case03, case04, case05 12 | ])); 13 | -------------------------------------------------------------------------------- /test/k02.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k02_numerical_types.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.add_int(5, 3), 8); 5 | const case02 = ():unit => Check.assert_equal(Koan.add_int_and_nat(5, 4 as nat), 9); 6 | const case03 = ():unit => Check.assert_equal(Koan.add_nat(5 as nat, 3 as nat), 8 as nat); 7 | const case04 = ():unit => Check.assert_equal(Koan.add_tez(5 as tez, 3 as tez), 8 as tez); 8 | const case05 = ():unit => Check.assert_equal(Koan.substract_int(5, 3), 2); 9 | const case06 = ():unit => Check.assert_equal(Koan.substract_int_and_nat(5, 3 as nat), 2); 10 | const case07 = ():unit => Check.assert_equal(Koan.substract_nat(5 as nat, 3 as nat), 2); 11 | const case08 = ():unit => Check.assert_equal(Koan.substract_tez(5 as tez, 3 as tez), Some(2 as tez)); 12 | const case09 = ():unit => Check.assert_equal(Koan.substract_tez(3 as tez, 5 as tez), None() as option); 13 | const case10 = ():unit => Check.assert_equal(Koan.int_from_nat(5 as nat), 5); 14 | const case11 = ():unit => Check.assert_equal(Koan.nat_from_int(5), 5 as nat); 15 | 16 | const test = Check.cases(list([ 17 | case01, case02, case03, case04, case05, case06, case07, case08, case09, case10, case11 18 | ])); 19 | -------------------------------------------------------------------------------- /test/k03.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k03_string_type.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.concat(["Hello", " World!"]), "Hello World!"); 5 | const case02 = ():unit => Check.assert_equal(Koan.first("Hello"), "H"); 6 | const case03 = ():unit => Check.assert_equal(Koan.length("Hello"), 5 as nat); 7 | 8 | const test = Check.cases(list([ 9 | case01, case02, case03 10 | ])); 11 | -------------------------------------------------------------------------------- /test/k04.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k04_bytes_type.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.concat([0x1234, 0x5678]), 0x12345678); 5 | const case02 = ():unit => Check.assert_equal(Koan.first(0x1224), 0x12); 6 | const case03 = ():unit => Check.assert_equal(Koan.length(0x123456), 3 as nat); 7 | 8 | const test = Check.cases(list([ 9 | case01, case02, case03 10 | ])); 11 | -------------------------------------------------------------------------------- /test/k05.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k05_conditional.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.compare_string(["a","a"]), true); 5 | const case02 = ():unit => Check.assert_equal(Koan.compare_string(["a","b"]), false); 6 | 7 | const case03 = ():unit => Check.assert_equal(Koan.boolean_and([true,true]), true); 8 | const case04 = ():unit => Check.assert_equal(Koan.boolean_and([true,false]), false); 9 | const case05 = ():unit => Check.assert_equal(Koan.boolean_and([false,true]), false); 10 | const case06 = ():unit => Check.assert_equal(Koan.boolean_and([false,false]), false); 11 | 12 | const case07 = ():unit => Check.assert_equal(Koan.boolean_or([true,true]), true); 13 | const case08 = ():unit => Check.assert_equal(Koan.boolean_or([true,false]), true); 14 | const case09 = ():unit => Check.assert_equal(Koan.boolean_or([false,true]), true); 15 | const case10 = ():unit => Check.assert_equal(Koan.boolean_or([false,false]), false); 16 | 17 | const case11 = ():unit => Check.assert_equal(Koan.selection([1,2]), "i1 < i2"); 18 | const case12 = ():unit => Check.assert_equal(Koan.selection([2,2]), "i1 = i2"); 19 | const case13 = ():unit => Check.assert_equal(Koan.selection([2,1]), "i1 > i2"); 20 | 21 | const test = Check.cases(list([ 22 | case01, case02, case03, case04, case05, case06, case07, 23 | case08, case09, case10, case11, case12, case13 24 | ])); 25 | -------------------------------------------------------------------------------- /test/k06.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k06_function.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.max([1,2]), 2); 5 | const case02 = ():unit => Check.assert_equal(Koan.concat("a")("b"), "ab"); 6 | const case03 = ():unit => Check.assert_equal(Koan.handle((b:string): string => ("a" + b),"b"), "ab"); 7 | const case04 = ():unit => { if (Koan.do_optional_part) Check.assert_equal(Koan.curry((a:int, b:int): int => a + b)(1)(2), 3) }; 8 | const case05 = ():unit => { if (Koan.do_optional_part) Check.assert_equal(Koan.uncurry(Koan.curry((a:int, b:int): int => a + b))([1,2]), 3) }; 9 | 10 | const test = Check.cases(list([ 11 | case01, case02, case03, case04, case05 12 | ])); -------------------------------------------------------------------------------- /test/k07.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k07_tuple_types.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.new_point(1,2), [1,2]); 5 | const case02 = ():unit => Check.assert_equal(Koan.abscissa(Koan.new_point(1,2)), 1); 6 | const case03 = ():unit => Check.assert_equal(Koan.ordinate(Koan.new_point(1,2)), 2); 7 | 8 | const test = Check.cases(list([ 9 | case01, case02, case03 10 | ])); -------------------------------------------------------------------------------- /test/k08.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k08_record_types.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.account (), ({ balance: 5 as tez, transactions: 3 as nat }) ); 5 | const case02 = ():unit => Check.assert_equal(Koan.modify_account (Koan.account ()), ({ balance: 5 as tez, transactions: 5 as nat }) ); 6 | 7 | const test = Check.cases(list([ 8 | case01, case02 9 | ])); -------------------------------------------------------------------------------- /test/k09.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k09_variant_types.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.new_heads(), Heads()); 5 | const case02 = ():unit => Check.assert_equal(Koan.new_tails(), Tails()); 6 | const case03 = ():unit => Check.assert_equal(Koan.flip_coin(Koan.new_heads()), Tails()); 7 | const case04 = ():unit => Check.assert_equal(Koan.flip_coin(Koan.new_tails()), Heads()); 8 | const case05 = ():unit => Check.assert_equal( 9 | Koan.play(Play((_u:unit):Koan.coin => Heads()), { heads: (0 as nat), tails:(0 as nat)}) 10 | , 11 | ({ heads: (1 as nat), tails:(0 as nat)}) 12 | ); 13 | const case06 = ():unit => Check.assert_equal( 14 | Koan.play(Play((_u:unit):Koan.coin => Tails()), { heads: (0 as nat), tails:(0 as nat)}) 15 | , 16 | ({ heads: (0 as nat), tails:(1 as nat)}) 17 | ); 18 | const case07 = ():unit => Check.assert_equal( 19 | Koan.play(Reset(), { heads: (1 as nat), tails:(1 as nat)}) 20 | , 21 | ({ heads: (0 as nat), tails:(0 as nat)}) 22 | ); 23 | 24 | const test = Check.cases(list([ 25 | case01, case02, case03, case04, case05, case06, case07 26 | ])); -------------------------------------------------------------------------------- /test/k10.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k10_option.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.div(10,2), Some(5)); 5 | const case02 = ():unit => Check.assert_equal(Koan.div(10,0), None() as option); 6 | const case03 = ():unit => Check.assert_equal(Koan.map((n:int):int => n + 1, Some(3)), Some(4)); 7 | const case04 = ():unit => Check.assert_equal(Koan.map((n:int):int => n + 1, None() as option), None() as option); 8 | const case05 = ():unit => Check.assert_equal(Koan.first("Hello"), Some("H")); 9 | const case06 = ():unit => Check.assert_equal(Koan.first(""), None() as option); 10 | 11 | const test = Check.cases(list([ 12 | case01, case02, case03, case04, case05, case06 13 | ])); -------------------------------------------------------------------------------- /test/k11.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k11_list_type.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.is_empty(list([]) as list), true); 5 | const case02 = ():unit => Check.assert_equal(Koan.is_empty(list([1,2])), false); 6 | const case03 = ():unit => Check.assert_equal(Koan.head(Koan.no_ints()), (None() as option)); 7 | const case04 = ():unit => Check.assert_equal(Koan.head(Koan.cons(2, Koan.no_ints())), Some(2)); 8 | const case05 = ():unit => Check.assert_equal(Koan.increment(Koan.cons(1,Koan.cons(2, Koan.no_ints()))), list([2,3])); 9 | const case06 = ():unit => Check.assert_equal(Koan.add_all(Koan.cons(1,Koan.cons(2, Koan.no_ints()))), 3); 10 | const case07 = ():unit => Check.assert_equal(Koan.reverse(Koan.cons(1,Koan.cons(2, Koan.no_ints()))), list([2,1])); 11 | 12 | const test = Check.cases(list([ 13 | case01, case02, case03, case04, case05, case06, case07 14 | ])); -------------------------------------------------------------------------------- /test/k12.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k12_map_type.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.find(Koan.transpose, 1, "?"), "One"); 5 | const case02 = ():unit => Check.assert_equal(Koan.find(Koan.transpose, 2, "?"), "?"); 6 | const case03 = ():unit => Check.assert_equal(((Koan.update(Koan.transpose, 1, "Un"))[0]), Some("One")); 7 | const case04 = ():unit => Check.assert_equal(Koan.map( 8 | (_k:int,v:string): string => "(" + v + ")", Koan.transpose) 9 | , 10 | Map.literal(list([ [0, "(Zero)"], [1, "(One)"] ])) 11 | ); 12 | 13 | const test = Check.cases(list([ 14 | case01, case02, case03, case04 15 | ])); -------------------------------------------------------------------------------- /test/k13.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k13_set_type.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.add_all(Set.literal(list([1,2]))), 3); 5 | const case02 = ():unit => Check.assert_equal(Koan.map((i:int):int => i + 1, Set.literal(list([1,2]))), Set.literal(list([2,3]))); 6 | 7 | const test = Check.cases(list([ 8 | case01, case02 9 | ])); -------------------------------------------------------------------------------- /test/k14.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k14_exception.jsligo" "Koan" 3 | 4 | const case01_ = (): bool => { 5 | const [taddr, _, _] = Test.originate(Koan.main, unit, 0 as tez); 6 | const contract = Test.to_contract(taddr); 7 | 8 | match(Test.transfer_to_contract(contract, 1, 1 as mutez), { 9 | Success: _g => true, 10 | Fail: _e => false, 11 | }); 12 | }; 13 | 14 | const case02_ = (): bool => { 15 | const [taddr, _, _] = Test.originate(Koan.main, unit, 0 as tez); 16 | const contract = Test.to_contract(taddr); 17 | 18 | match(Test.transfer_to_contract(contract, -1, 1 as mutez), { 19 | Success: _g => false, 20 | Fail: e => match(e, { 21 | Rejected: _p => true, 22 | Balance_too_low: _r => false, 23 | Other: _r => false 24 | }), 25 | }); 26 | }; 27 | 28 | const case01 = ():unit => Check.assert_equal(case01_(), true); 29 | const case02 = ():unit => Check.assert_equal(case02_(), true); 30 | 31 | const test = Check.cases(list([ 32 | case01, case02 33 | ])); -------------------------------------------------------------------------------- /test/k15.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k15_imperative.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.sum_all(list([4 as nat,3 as nat])), 7 as nat); 5 | const case02 = ():unit => Check.assert_equal(Koan.factorial(4 as nat), 24 as nat); 6 | 7 | const test = Check.cases(list([ 8 | case01, case02 9 | ])); -------------------------------------------------------------------------------- /test/k16.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k16_recursive_function.jsligo" "Koan" 3 | 4 | const case01 = ():unit => Check.assert_equal(Koan.sum_all(list([4,3])), 7); 5 | const case02 = ():unit => Check.assert_equal(Koan.factorial(4 as nat), 24 as nat); 6 | 7 | const test = Check.cases(list([ 8 | case01, case02 9 | ])); -------------------------------------------------------------------------------- /test/k17.jsligo: -------------------------------------------------------------------------------- 1 | #import "common/check.jsligo" "Check" 2 | #import "../lib/k17_polymorphism.jsligo" "Koan" 3 | 4 | const test = "No tests!"; 5 | --------------------------------------------------------------------------------