Skip to content

Commit 9731062

Browse files
Add tutorial sections for tables, symbols, begin
1 parent c2036b3 commit 9731062

File tree

4 files changed

+172
-76
lines changed

4 files changed

+172
-76
lines changed

content/learn.md

Lines changed: 94 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ title = 'Learn Cognate'
1111
## Install
1212

1313

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.
1515

1616
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.
1717

@@ -33,7 +33,7 @@ Alternatively, you can use the interactive web playground [here](https://cognate
3333
Print "Hello world!";
3434
```
3535

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.
3737

3838
```cognate
3939
Print + 2 3;
@@ -59,7 +59,7 @@ Let's have a more complex example, this subtracts 12 from 15 and then multiplies
5959
Print * 2 - 12 15;
6060
```
6161

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.
6363

6464
- Place 15 on top of the stack
6565
- Place 12 on top of the stack
@@ -69,7 +69,7 @@ By now you may have realised that Cognate is evaluating our programs backwards -
6969
- Remove the top number from the stack, print it
7070

7171

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.
7373

7474
```cognate
7575
Print * Twin 8;
@@ -93,7 +93,7 @@ Print the Square of 8;
9393
```
9494

9595

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.
9797

9898
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.
9999

@@ -125,7 +125,7 @@ Let X be 4;
125125
Print X;
126126
```
127127

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.
129129

130130
```cognate
131131
Def Square as (
@@ -162,7 +162,7 @@ While (True) (
162162
);
163163
```
164164

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.
166166

167167
```cognate
168168
Do (
@@ -171,7 +171,7 @@ Do (
171171
```
172172

173173

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.
175175

176176
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.
177177

@@ -211,61 +211,44 @@ A more general version of this function, `Times` can be defined using recursion.
211211
Def Times (
212212
Let N number of repetitions;
213213
Def F function to repeat;
214-
Unless Zero? N ( F ; Times - 1 N (F) );
214+
Unless Zero? N ( F ; Times -- 1 N (F) );
215215
);
216216
217217
Times 5 (
218218
Print "wow!";
219219
);
220220
```
221221

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.
223223

224224
```cognate
225225
Def Times (
226-
Let N is Integer! number of repetitions;
226+
Let N be Of (Integer?) number of repetitions;
227227
Def F function to repeat;
228-
Unless Zero? N ( F ; Times - 1 N (F) );
228+
Unless Zero? N ( F ; Times -- 1 N (F) );
229229
);
230230
```
231231

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.
249233

250234

251235
## Lists
252236

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.
254238

255239
```cognate
256240
Print List (1 2 3 4 5);
257241
```
258242

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?
260244

261245
```cognate
262246
Print List ( Times 100 (1) );
263247
```
264248

265-
266249
The three most fundamental list functions are `Push`, `First`, and `Rest`.
267250

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.
269252

270253

271254
```cognate
@@ -274,7 +257,7 @@ Print First element of L;
274257
Print Rest of L;
275258
```
276259

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.
278261

279262
```cognate
280263
Def Square as (* Twin);
@@ -288,15 +271,15 @@ Let Evens be Map (* 2) over Range 1 to 50;
288271
Print Evens;
289272
```
290273

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.
292275

293276
```cognate
294277
Let Even? be (Zero? Modulo 2);
295278
Let Evens be Filter (Even?) over Range 1 to 100;
296279
Print Evens;
297280
```
298281

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.
300283

301284
```cognate
302285
Def Factorial as (
@@ -307,13 +290,22 @@ Def Factorial as (
307290
Print Factorial 10;
308291
```
309292

293+
For the sake of convenience Cognate *does* provide `Fold`, which has the order of parameters swapped:
310294

311-
## Boxes
295+
```cognate
296+
Def Factorial as (
297+
Let N be Integer!;
298+
Fold (*) from 1 over Range 1 to N;
299+
);
312300
301+
Print Factorial 10;
302+
```
313303

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.
315307

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.
317309

318310

319311
```cognate
@@ -337,7 +329,7 @@ For each in L (
337329
Print L;
338330
```
339331

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:
341333

342334
```cognate
343335
Def Box-list as ( Map (Box) );
@@ -355,8 +347,68 @@ Inplace-map (* 2) over L;
355347
Print L;
356348
```
357349

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+
```
358409

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.
360411

361-
This tutorial isn't finished yet!
412+
## End
362413

414+
Nope, there isn't an `End` function. This is just the end of the tutorial.

public/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<!doctype html>
33
<html>
44
<head>
5-
<meta name="generator" content="Hugo 0.133.1">
5+
<meta name="generator" content="Hugo 0.130.0">
66
<meta charset="UTF-8" />
77
<link rel="stylesheet" href="/main.min.css" />
88
<title>Cognate: Readable and concise concatenative programming</title>

public/index.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<link>https://cognate-lang.github.io/learn/</link>
1313
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
1414
<guid>https://cognate-lang.github.io/learn/</guid>
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 - native Windows support is planned.&#xA;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 &amp;ndash; native Windows support is planned.&#xA;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>
1616
</item>
1717
</channel>
1818
</rss>

0 commit comments

Comments
 (0)