You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/learn.md
+94-42Lines changed: 94 additions & 42 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,7 @@ title = 'Learn Cognate'
11
11
## Install
12
12
13
13
14
-
First install `CognaC` the Cognate Compiler from here using the provided instructions. Currently `CognaC` will run on recent Linux or Mac systems. Windows users can install it onto the Windows Subsystem for Linux - native Windows support is planned.
14
+
First install `CognaC` the Cognate Compiler from here using the provided instructions. Currently `CognaC` will run on recent Linux or Mac systems. Windows users can install it onto the Windows Subsystem for Linux -- native Windows support is planned.
15
15
16
16
Invoking `CognaC` is simple. If you have a file named `foo.cog` containing a Cognate program, it can be compiled into an executable named `foo` with the following command.
17
17
@@ -33,7 +33,7 @@ Alternatively, you can use the interactive web playground [here](https://cognate
33
33
Print "Hello world!";
34
34
```
35
35
36
-
Fairly simple right? This example calls the `Print` function with one parameter - the string `"Hello world!"`. Now let's do another simple example, adding two numbers.
36
+
Fairly simple right? This example calls the `Print` function with one parameter -- the string `"Hello world!"`. Now let's do another simple example, adding two numbers.
37
37
38
38
```cognate
39
39
Print + 2 3;
@@ -59,7 +59,7 @@ Let's have a more complex example, this subtracts 12 from 15 and then multiplies
59
59
Print * 2 - 12 15;
60
60
```
61
61
62
-
By now you may have realised that Cognate is evaluating our programs backwards - right to left. The subtraction is being performed before the multiplication above. This is being done using a stack, as explained here.
62
+
By now you may have realised that Cognate is evaluating our programs backwards -- right to left. The subtraction is being performed before the multiplication above. This is being done using a stack, as explained here.
63
63
64
64
- Place 15 on top of the stack
65
65
- Place 12 on top of the stack
@@ -69,7 +69,7 @@ By now you may have realised that Cognate is evaluating our programs backwards -
69
69
- Remove the top number from the stack, print it
70
70
71
71
72
-
Cognate comes with functions to manipulate the stack. The simplest of these is `Twin`, which takes the top element from the stack, and puts it back on again - twice. The below snippet uses `Twin` to square a number by multiplying it by itself.
72
+
Cognate comes with functions to manipulate the stack. The simplest of these is `Twin`, which takes the top element from the stack, and puts it back on again -- twice. The below snippet uses `Twin` to square a number by multiplying it by itself.
73
73
74
74
```cognate
75
75
Print * Twin 8;
@@ -93,7 +93,7 @@ Print the Square of 8;
93
93
```
94
94
95
95
96
-
In this example the readability isn't really improved much, but in more complex programs this informal syntax can be invaluable.
96
+
In this example the readability isn't really improved much, but in more complex programs this 'informal syntax' can be invaluable.
97
97
98
98
By now you've probably noticed the semicolons. These delimit statements so that Cognate knows what order to evaluate functions in (remember that these are executed backwards). Definitions should also be terminated with semicolons. The stack persists between statements, letting us do things like this.
99
99
@@ -125,7 +125,7 @@ Let X be 4;
125
125
Print X;
126
126
```
127
127
128
-
We can use variables to define functions that take named parameters - here's an alternate version of the `Square` function.
128
+
We can use variables to define functions that take named parameters -- here's an alternate version of the `Square` function.
129
129
130
130
```cognate
131
131
Def Square as (
@@ -162,7 +162,7 @@ While (True) (
162
162
);
163
163
```
164
164
165
-
What's with the second set of brackets? `When` doesn't have them, so why should `While`? This is because brackets denote blocks! These prevent code being instantly evaluated and instead push a reference onto the stack. Blocks also control variable scopes. We can evaluate a block using the `Do` function - which is how our control flow functions are implemented.
165
+
What's with the second set of brackets? `When` doesn't have them, so why should `While`? This is because brackets denote blocks! These prevent code being instantly evaluated and instead push a reference onto the stack. Blocks also control variable scopes. We can evaluate a block using the `Do` function -- which is how our control flow functions are implemented.
166
166
167
167
```cognate
168
168
Do (
@@ -171,7 +171,7 @@ Do (
171
171
```
172
172
173
173
174
-
This explains the syntax for functions: `Def` simply binds a block to a name, much like `Let`. Blocks can be passed around the program like any other value - even if they reference variables that go out of scope.
174
+
This explains the syntax for functions: `Def` simply binds a block to a name, much like `Let`. Blocks can be passed around the program like any other value -- even if they reference variables that go out of scope.
175
175
176
176
Now that this (hopefully) makes some sense, we can finally introduce the `If` statement! `If` is a function that takes three parameters. The first is a boolean, if this is `True` then the second argument is returned. If not, the third argument is returned. We can chain `If`s together to have more complex control flow.
177
177
@@ -211,61 +211,44 @@ A more general version of this function, `Times` can be defined using recursion.
211
211
Def Times (
212
212
Let N number of repetitions;
213
213
Def F function to repeat;
214
-
Unless Zero? N ( F ; Times - 1 N (F) );
214
+
Unless Zero? N ( F ; Times -- 1 N (F) );
215
215
);
216
216
217
217
Times 5 (
218
218
Print "wow!";
219
219
);
220
220
```
221
221
222
-
Now you may see a small problem with this. If the user calls `Times` with a non-integer parameter it will loop forever - that won't do at all! We use the `Integer!` function to throw a type error if a decimal is given.
222
+
Now you may see a small problem with this. If the user calls `Times` with a non-integer parameter it will loop forever -- that won't do at all! We can use the `Of` function here -- it checks a value against a predicate (in this case `Integer?`) and throws an error if it fails.
223
223
224
224
```cognate
225
225
Def Times (
226
-
Let N is Integer! number of repetitions;
226
+
Let N be Of (Integer?) number of repetitions;
227
227
Def F function to repeat;
228
-
Unless Zero? N ( F ; Times - 1 N (F) );
228
+
Unless Zero? N ( F ; Times -- 1 N (F) );
229
229
);
230
230
```
231
231
232
-
233
-
We could also use the `Block!` function for `F` but we'll already get a type error when we use `Def` to bind anything that isn't a block, so there is no point.
234
-
235
-
The `Times` function is also our first loop. We don't need to define it every time though as it's also in the standard library. Another loop is `While`.
236
-
237
-
238
-
```cognate
239
-
While (!= "done" Twin Input) (
240
-
Print
241
-
);
242
-
Drop;
243
-
```
244
-
245
-
246
-
`While` takes two block parameters. The first one is the condition and is evaluated immediately, returning a boolean. If this is `True` then the second block (the loop body) is evaluated and then the loop repeats, if not the loop finishes. The example above shows the use of a `While` loop. The program simply echoes the user's inputs back to them until they write `"done"`.
247
-
248
-
This loop is most useful in imperative code where intermediary values are passed on the stack between iterations. It is less useful for iterating over data structures such as lists. Lists?
232
+
We could also use `Of (Block?)` for `F` but we'll already get a type error if we use `Def` to bind anything that isn't a block, so there is no point.
249
233
250
234
251
235
## Lists
252
236
253
-
In Cognate, lists are generated using - you guessed it - a function. The `List` function takes a block as a parameter. It evaluates this block *in a new stack* and then returns that stack as a list.
237
+
In Cognate, lists are generated using -- you guessed it -- a function. The `List` function takes a block as a parameter. It evaluates this block *in a new empty stack* and then returns that stack as a list.
254
238
255
239
```cognate
256
240
Print List (1 2 3 4 5);
257
241
```
258
242
259
-
This allows Cognate's list creation to be much more flexible than other languages - for example what if we wanted a list of 100 ones?
243
+
This allows Cognate's list creation to be much more flexible than other languages -- for example what if we wanted a list of 100 ones?
260
244
261
245
```cognate
262
246
Print List ( Times 100 (1) );
263
247
```
264
248
265
-
266
249
The three most fundamental list functions are `Push`, `First`, and `Rest`.
267
250
268
-
The `Push` function takes a value and a list as parameters, and returns the list with the value *pushed* to it's first element. `First` simply returns the first element of a list. `Rest` returns a list without its first element.
251
+
The `Push` function takes a value and a list as parameters, and returns a new list list with the value as it's first element and the list parameter providing following elements. `First` simply returns the first element of a list. `Rest` returns a list without its first element.
269
252
270
253
271
254
```cognate
@@ -274,7 +257,7 @@ Print First element of L;
274
257
Print Rest of L;
275
258
```
276
259
277
-
`Range` creates a list of numbers from a starting (inclusive) and an ending (exclusive) number. `For` is a higher order function that applied an operation to each element of a list - it is the loop for iterating over lists.
260
+
`Range` creates a list of numbers from a starting (inclusive) and an ending (exclusive) number. `For` is a higher order function that applied an operation to each element of a list -- it is the loop for iterating over lists.
278
261
279
262
```cognate
280
263
Def Square as (* Twin);
@@ -288,15 +271,15 @@ Let Evens be Map (* 2) over Range 1 to 50;
288
271
Print Evens;
289
272
```
290
273
291
-
`Filter` applies a function to each element of a list also. This function should return a boolean - if this is `False` then the function is removed from the returned list.
274
+
`Filter` applies a function to each element of a list also. This function should return a boolean -- if this is `False` then the function is removed from the returned list.
292
275
293
276
```cognate
294
277
Let Even? be (Zero? Modulo 2);
295
278
Let Evens be Filter (Even?) over Range 1 to 100;
296
279
Print Evens;
297
280
```
298
281
299
-
The functional programmers reading this are likely expecting a Fold or Reduce function next - which applies an operation to a list with an accumulator. However Cognate needs no fold function, as `For` can store intermediary values on the stack, acting like a fold.
282
+
The functional programmers reading this are likely expecting a Fold or Reduce function next -- which applies an operation to a list with an accumulator. However Cognate needs no fold function, as `For` can store intermediary values on the stack, acting like a fold.
300
283
301
284
```cognate
302
285
Def Factorial as (
@@ -307,13 +290,22 @@ Def Factorial as (
307
290
Print Factorial 10;
308
291
```
309
292
293
+
For the sake of convenience Cognate *does* provide `Fold`, which has the order of parameters swapped:
310
294
311
-
## Boxes
295
+
```cognate
296
+
Def Factorial as (
297
+
Let N be Integer!;
298
+
Fold (*) from 1 over Range 1 to N;
299
+
);
312
300
301
+
Print Factorial 10;
302
+
```
313
303
314
-
While storing state between loop iterations is very useful, in some cases you just need mutable variables. Cognate's boxes are references used to generalise the concept of mutation.
304
+
## Boxes
305
+
306
+
While storing state between loop iterations is very useful, in some cases you just need mutable variables. Cognate's box type implements a reference and generalises mutation.
315
307
316
-
The `Box` function takes a value and places it in a box. `Unbox` returns the item stored in a box. `Set` takes a box and a value as parameters and mutates the box to hold the value, updating all references to it.
308
+
The `Box` function takes a value and places it in a box. `Unbox` returns the item stored in a box. `Set` takes a box and a value as parameters and mutates the box to hold the value -- this affects all references to it.
317
309
318
310
319
311
```cognate
@@ -337,7 +329,7 @@ For each in L (
337
329
Print L;
338
330
```
339
331
340
-
While boxes may not seem as ergonomic as mutation in other languages, they are both more flexible than mutable variables and more predictable than implicit references. We can also easily extend mutation, like this:
332
+
While boxes may not seem as ergonomic as mutation in other languages, they're both more flexible than mutable variables and more predictable than implicit references. We can also easily extend mutation, like this:
341
333
342
334
```cognate
343
335
Def Box-list as ( Map (Box) );
@@ -355,8 +347,68 @@ Inplace-map (* 2) over L;
355
347
Print L;
356
348
```
357
349
350
+
## Tables
351
+
352
+
The table type provides an efficient, immutable, unordered mapping between keys and values, which can be used to implement many more complex data structures. `Table` creates a table in the same manner in which `List` creates a list, though taking key-value pairs. `.` is used to extract the value corresponding to a key.
353
+
354
+
```cognate
355
+
Let T be Table (
356
+
"foo" is 1;
357
+
"bar" is Range 2 to 10;
358
+
12 is 13; ~~ Keys can be of any type except box or block
359
+
);
360
+
361
+
Print . "foo" T;
362
+
Print . "bar" T;
363
+
```
364
+
365
+
`Insert` returns the table with an extra key-value pair, `Remove` returns the table without a specified key, and `Has` checks whether a key is in the table.
366
+
367
+
```cognate
368
+
Def Remove-baz (
369
+
Remove "baz"
370
+
from Of (Table?)
371
+
Of (Has "baz");
372
+
);
373
+
374
+
Insert "baz" is Range 11 to 100 into T;
375
+
Print Twin;
376
+
Remove-baz;
377
+
Print;
378
+
```
379
+
380
+
`Keys` and `Values` returns lists of the keys and values respectively in a table, in no particular order. Tables are implemented as self-balancing binary trees, and are optimised for fast immutable insertions.
381
+
382
+
## Symbols
383
+
384
+
Typically, you'd want to use a *symbol* as a key for your table. Symbols will be familiar to Lisp programmers and can be considered as either a limited string or an unlimited enum, depending how you like to think.
385
+
386
+
```cognate
387
+
\foo ~~ This is a symbol
388
+
```
389
+
390
+
Symbols can't be modified in any way, but can be compared very efficiently (which is why they're great keys for tables) and put into any data structure.
391
+
392
+
## Begin
393
+
394
+
Earlier when we defined loops, you may have noticed something missing -- the break statement (also continue, for that matter). Being a functional language, Cognate encourages avoiding control flow like this, but sometimes you've just gotta get out of a block early. Introducing `Begin` -- this function takes a block parameter and evaluates it, passing it *another* block on the stack. Evaluating *that* block will jump you out of the original block. Confused? Here's an example.
395
+
396
+
```cognate
397
+
~~ Let's print the numbers up to 100 in an unnecessarily complicated manner.
398
+
Let L be Box Range 1 to 100;
399
+
400
+
Begin (
401
+
Def Break;
402
+
While (True) (
403
+
Print First element of Unbox L;
404
+
Set L to Rest of Unbox L;
405
+
When Empty? Unbox L ( Break out of the begin );
406
+
)
407
+
)
408
+
```
358
409
359
-
## Todo
410
+
`Begin` can also be used to implement a return statement to break out of a function early. An advantage of `Begin` over traditional programming languages' break and return statements is that it gives fine-grained control over which block you break out of.
360
411
361
-
This tutorial isn't finished yet!
412
+
## End
362
413
414
+
Nope, there isn't an `End` function. This is just the end of the tutorial.
<description>Learn Cognate A brief introduction to the language Install First install CognaC the Cognate Compiler from here using the provided instructions. Currently CognaC will run on recent Linux or Mac systems. Windows users can install it onto the Windows Subsystem for Linux - native Windows support is planned.
Invoking CognaC is simple. If you have a file named foo.cog containing a Cognate program, it can be compiled into an executable named foo with the following command.</description>
15
+
<description>Learn Cognate A brief introduction to the language Install First install CognaC the Cognate Compiler from here using the provided instructions. Currently CognaC will run on recent Linux or Mac systems. Windows users can install it onto the Windows Subsystem for Linux &ndash; native Windows support is planned.
Invoking CognaC is simple. If you have a file named foo.cog containing a Cognate program, it can be compiled into an executable named foo with the following command.</description>
0 commit comments