documentation updates to reflect removal of the let keyword
				
					
				
			also some general cleanups and improvements
This commit is contained in:
		
							parent
							
								
									731046d1f7
								
							
						
					
					
						commit
						bc2df73487
					
				
					 2 changed files with 125 additions and 67 deletions
				
			
		
							
								
								
									
										150
									
								
								docs/rkgk.dj
									
										
									
									
									
								
							
							
						
						
									
										150
									
								
								docs/rkgk.dj
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -317,7 +317,7 @@ Once you define a name, its associated data stays the same throughout the entire
 | 
			
		|||
So we can define `thickness` to be `4`, and then use it in our scribbles.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
thickness = 4
 | 
			
		||||
thickness: 4
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
  [
 | 
			
		||||
| 
						 | 
				
			
			@ -327,10 +327,11 @@ withDotter \d ->
 | 
			
		|||
  ]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`name = data` is a special operator in haku that tells the language "whenever we say `name`, we mean `data`."
 | 
			
		||||
`name: data` is a special operator in haku that means "whenever we say `name`, we mean `data`."
 | 
			
		||||
We call this operator _def_, short for _definition_.
 | 
			
		||||
 | 
			
		||||
We cannot use it in arbitrary places in our program, because it wouldn't make sense.
 | 
			
		||||
What does it mean to have a stroke whose thickness is `meow = 5`?
 | 
			
		||||
What does it mean to have a stroke whose thickness is `meow: 5`?
 | 
			
		||||
 | 
			
		||||
To keep a consistent program structure, haku also forces all your defs to appear _before_ your scribble.
 | 
			
		||||
You can think of the defs as a list of ingredients for the final scribble.
 | 
			
		||||
| 
						 | 
				
			
			@ -348,8 +349,8 @@ We'll get to why soon!
 | 
			
		|||
Anyways, we can likewise replace our `2` constants with a def:
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
thickness = 4
 | 
			
		||||
xOffset = 2
 | 
			
		||||
thickness: 4
 | 
			
		||||
xOffset: 2
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
  [
 | 
			
		||||
| 
						 | 
				
			
			@ -380,9 +381,9 @@ But now there's a problem.
 | 
			
		|||
If we change our `thickness` back to `8`, our points will overlap!
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
thickness = 8
 | 
			
		||||
           ---
 | 
			
		||||
xOffset = 2
 | 
			
		||||
thickness: 8
 | 
			
		||||
          ---
 | 
			
		||||
xOffset: 2
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
  [
 | 
			
		||||
| 
						 | 
				
			
			@ -394,9 +395,9 @@ withDotter \d ->
 | 
			
		|||
So we'll make our `xOffset` calculated dynamically from the `thickness`, to not have to update it every time.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
thickness = 8
 | 
			
		||||
xOffset = thickness / 2
 | 
			
		||||
          -------------
 | 
			
		||||
thickness: 8
 | 
			
		||||
xOffset: thickness / 2
 | 
			
		||||
         -------------
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
  [
 | 
			
		||||
| 
						 | 
				
			
			@ -484,7 +485,7 @@ That'll need fixing!
 | 
			
		|||
Either way, let's define a function that'll make us those circles!
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
splat = \d, radius ->
 | 
			
		||||
splat: \d, radius ->
 | 
			
		||||
  fill #0001 (circle (d To) radius)
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
| 
						 | 
				
			
			@ -534,7 +535,7 @@ haku limits the use of overloading to system functions for simplicity---adding o
 | 
			
		|||
Since these transparent circles are so much easier to draw now, let's make a few more of them!
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
splat = \d, radius ->
 | 
			
		||||
splat: \d, radius ->
 | 
			
		||||
  fill #0001 (circle (d To) radius)
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
| 
						 | 
				
			
			@ -580,10 +581,10 @@ Until some threshold is reached, in which case we just make a single circle.
 | 
			
		|||
The first part is easy to do: haku allows us to define a function that calls itself without making any fuss.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
splat = \d, radius ->
 | 
			
		||||
splat: \d, radius ->
 | 
			
		||||
  fill #0001 (circle (d To) radius)
 | 
			
		||||
 | 
			
		||||
airbrush = \d, size ->
 | 
			
		||||
airbrush: \d, size ->
 | 
			
		||||
  [
 | 
			
		||||
    splat d size
 | 
			
		||||
    airbrush d (size - 8)
 | 
			
		||||
| 
						 | 
				
			
			@ -616,9 +617,9 @@ We call this act of switching execution paths _branching_.
 | 
			
		|||
Try this out---change the `radius`, and observe how your brush changes color once you set it beyond 16:
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
radius = 8
 | 
			
		||||
radius: 8
 | 
			
		||||
 | 
			
		||||
color =
 | 
			
		||||
color:
 | 
			
		||||
  if (radius < 16)
 | 
			
		||||
    #00F
 | 
			
		||||
  else
 | 
			
		||||
| 
						 | 
				
			
			@ -641,10 +642,10 @@ An `if` only calculates the argument it needs to produce the result.
 | 
			
		|||
This allows us to use it to prevent unbounded recursion in our `airbrush` example.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
splat = \d, radius ->
 | 
			
		||||
splat: \d, radius ->
 | 
			
		||||
  fill #0001 (circle (d To) radius)
 | 
			
		||||
 | 
			
		||||
airbrush = \d, size ->
 | 
			
		||||
airbrush: \d, size ->
 | 
			
		||||
  if (size > 0)
 | 
			
		||||
    [
 | 
			
		||||
      splat d size
 | 
			
		||||
| 
						 | 
				
			
			@ -667,10 +668,10 @@ But the airbrush still looks super primitive.
 | 
			
		|||
Let's try increasing the fidelity by doing smaller steps!
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
splat = \d, radius ->
 | 
			
		||||
splat: \d, radius ->
 | 
			
		||||
  fill #0001 (circle (d To) radius)
 | 
			
		||||
 | 
			
		||||
airbrush = \d, size ->
 | 
			
		||||
airbrush: \d, size ->
 | 
			
		||||
  if (size > 0)
 | 
			
		||||
    [
 | 
			
		||||
      splat d size
 | 
			
		||||
| 
						 | 
				
			
			@ -687,11 +688,11 @@ withDotter \d ->
 | 
			
		|||
Well... sure, that's just a black blob with a slight gradient on the outer edge, so let's decrease the opacity.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
splat = \d, radius ->
 | 
			
		||||
splat: \d, radius ->
 | 
			
		||||
  fill #00000004 (circle (d To) radius)
 | 
			
		||||
       ---------
 | 
			
		||||
 | 
			
		||||
airbrush = \d, size ->
 | 
			
		||||
airbrush: \d, size ->
 | 
			
		||||
  if (size > 0)
 | 
			
		||||
    [
 | 
			
		||||
      splat d size
 | 
			
		||||
| 
						 | 
				
			
			@ -740,7 +741,7 @@ Most commonly, colors are blended using _linear interpolation_---which is essent
 | 
			
		|||
Mathematically, linear interpolation is defined using this formula:
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
lerp = \a, b, t ->
 | 
			
		||||
lerp: \a, b, t ->
 | 
			
		||||
  a + (b - a) * t
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -806,12 +807,30 @@ For now you'll have to construct your brushes with this in mind.
 | 
			
		|||
 | 
			
		||||
### And more limits
 | 
			
		||||
 | 
			
		||||
There are more limits on top of this, which stem from haku's design.
 | 
			
		||||
Since it's running _your_ code on _my_ server, it has some arbitrary limits set to prevent it from causing much harm.
 | 
			
		||||
There are more limits on top of this, which stem from rakugaki's design.
 | 
			
		||||
 | 
			
		||||
haku code cannot be too long, and it cannot execute too long.
 | 
			
		||||
It cannot consume too much memory---you cannot have too many definitions, or too many temporary values at once.
 | 
			
		||||
There are also memory usage limits on "heavyweight" data, such as functions or lists.
 | 
			
		||||
Recall that rakugaki is multiplayer!
 | 
			
		||||
You can draw with your friends.
 | 
			
		||||
But, your friends may not have a computer as good as yours.
 | 
			
		||||
So to keep the experience fair, haku sets some limits on brush code.
 | 
			
		||||
 | 
			
		||||
A brush cannot be too long, and it cannot execute too long.
 | 
			
		||||
It also cannot consume too much memory---you cannot have too many definitions, or too many temporary values at once.
 | 
			
		||||
 | 
			
		||||
If you ever brush (ha ha) up against these limits, you'll see colorful bars appear beside the brush preview in the bottom right corner.
 | 
			
		||||
These bars show you how much you're nearing the limits!
 | 
			
		||||
 | 
			
		||||
Try adding this line to your brush:
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
r: range 1 30000
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`range 1 30000` generates a list of integers between 1 and 30000, without having you write them out one by one.
 | 
			
		||||
But we can also see that it consumes a bunch of _fuel_ (units of execution time), and _a lot_ of memory---almost all, in fact!
 | 
			
		||||
 | 
			
		||||
Code size is harder to run up against, because it requires writing a pretty huge amount of characters into the editor.
 | 
			
		||||
Feel free to try it out yourself---try writing out some really long lists by hand, and see what happens!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Reticles
 | 
			
		||||
| 
						 | 
				
			
			@ -839,17 +858,17 @@ This allows you to _animate_ your brushes over time!
 | 
			
		|||
For example, this brush draws a rainbow.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
colorCurve = \n ->
 | 
			
		||||
colorCurve: \n ->
 | 
			
		||||
  abs (cos n)
 | 
			
		||||
 | 
			
		||||
pi = 3.14159265
 | 
			
		||||
l = 0.1  -- wavelength
 | 
			
		||||
pi: 3.14159265
 | 
			
		||||
l: 0.1  -- wavelength
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
  let r = colorCurve (d Num * l)
 | 
			
		||||
  let g = colorCurve (d Num * l + pi/3)
 | 
			
		||||
  let b = colorCurve (d Num * l + 2*pi/3)
 | 
			
		||||
  let color = rgba r g b 1
 | 
			
		||||
  r = colorCurve (d Num * l)
 | 
			
		||||
  g = colorCurve (d Num * l + pi/3)
 | 
			
		||||
  b = colorCurve (d Num * l + 2*pi/3)
 | 
			
		||||
  color = rgba r g b 1
 | 
			
		||||
  stroke 8 color (line (d From) (d To))
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -859,28 +878,28 @@ Currently, `withDotter` is the only reticle available in rakugaki, and it cannot
 | 
			
		|||
In the future rakugaki might get reticles that let you select lines, rectangles, ellipses, curves... but today is not that day.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### What's that, `let`?
 | 
			
		||||
### What's that, `=`?
 | 
			
		||||
 | 
			
		||||
I mentioned before that you cannot have defs inside functions.
 | 
			
		||||
What you _can_ have though, is `let`s, which define _variables_.
 | 
			
		||||
What you _can_ have though, is _variables_, defined with `name = data`.
 | 
			
		||||
 | 
			
		||||
Unlike defs, which are constant and cannot vary, variables' values can depend on function parameters---and a function can be called with a different set of parameters each time, thus making them variable!
 | 
			
		||||
 | 
			
		||||
A `let` always takes the following form.
 | 
			
		||||
A variable always takes the following form.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
let name = value
 | 
			
		||||
name = value
 | 
			
		||||
then
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
It's very similar to a def, with one major difference.
 | 
			
		||||
Because a `let` by itself only _names a value_ and does not have a result, it must be followed by another expression on the following line---and that expression determines the result.
 | 
			
		||||
The magic is that this continuing expression can refer to the `name` we had previously assigned in the `let` expression.
 | 
			
		||||
Because a variable by itself only _names a value_ and does not have a result, it must be followed by another expression on the following line---and that expression determines the result.
 | 
			
		||||
The magic is that this continuing expression can refer to the `name` we had previously assigned in the `name = value` expression.
 | 
			
		||||
 | 
			
		||||
::: aside
 | 
			
		||||
 | 
			
		||||
Here's a bit of trivia: the variable defined by a `let` is exactly the same as a function parameter.
 | 
			
		||||
The `let` above is equivalent to applying the argument `value` to a function taking in the parameter `name`, and returning `then` as the result.
 | 
			
		||||
Here's a bit of trivia: variables are exactly the same as function parameters!
 | 
			
		||||
The `name = value` expression above is equivalent to applying the argument `value` to a function taking in the parameter `name`, and returning `then` as the result.
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
(\name -> then) value
 | 
			
		||||
| 
						 | 
				
			
			@ -893,15 +912,15 @@ That's right. haku is a cute little Haskell for artists.
 | 
			
		|||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
`let`s aren't only useful for reusability---they're also helpful for breaking your brushes into smaller, more digestible pieces!
 | 
			
		||||
Variables aren't only useful for reusability---they're also helpful for breaking your brushes into smaller, more digestible pieces!
 | 
			
		||||
Compare the above version of the rainbow brush to this version, where all the `let`s are written inline:
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
colorCurve = \n ->
 | 
			
		||||
colorCurve: \n ->
 | 
			
		||||
  abs (cos n)
 | 
			
		||||
 | 
			
		||||
pi = 3.14159265
 | 
			
		||||
l = 0.1  -- wavelength
 | 
			
		||||
pi: 3.14159265
 | 
			
		||||
l: 0.1  -- wavelength
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
  stroke 8 (rgba (colorCurve (d Num * l)) (colorCurve (d Num * l + pi/3)) (colorCurve (d Num * l + 2*pi/3)) 1) (line (d From) (d To))
 | 
			
		||||
| 
						 | 
				
			
			@ -912,6 +931,41 @@ That's one hard to read beast of a `stroke`!
 | 
			
		|||
Generally, if a line is so long it wraps around rakugaki's narrow little text editor, it's probably a good idea to split it into variables.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### So... sometimes it's `:`, sometimes it's `=`, help, I'm confused!
 | 
			
		||||
 | 
			
		||||
If you're having trouble understanding when to use `:` and when to use `=`, here's a short version:
 | 
			
		||||
 | 
			
		||||
- `:` defines a name across your whole program.
 | 
			
		||||
- `=` defines a name that's only visible on the next line (more or less.)
 | 
			
		||||
 | 
			
		||||
Since `:` defines a name accessible from your whole program, it cannot access function parameters---which are temporary and local.
 | 
			
		||||
The opposite of "accessible in the whole program"!
 | 
			
		||||
 | 
			
		||||
As for `=`, I said it defines a name that's only visible on the next line "_more or less_", because technically the line that follows can actually be broken up into multiple lines, as would be the case with e.g. an `if`:
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
color: #000
 | 
			
		||||
thickness: 4
 | 
			
		||||
length: 5
 | 
			
		||||
duty: 0.5
 | 
			
		||||
 | 
			
		||||
or_: \a, b -> -- haku doesn't have a boolean OR yet...
 | 
			
		||||
  if (a) a
 | 
			
		||||
  else b
 | 
			
		||||
 | 
			
		||||
withDotter \d ->
 | 
			
		||||
  visible = mod (d Num) length < length * duty
 | 
			
		||||
  if (visible)
 | 
			
		||||
    -- this is more than one line, and `visible` can still be used here!
 | 
			
		||||
    stroke thickness color (line (d From) (d To))
 | 
			
		||||
  else
 | 
			
		||||
    ()  -- ...and here, too!
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Another thing: as shown in the examples above, you can chain multiple `=` expressions together, which also breaks this "next line" rule of thumb.
 | 
			
		||||
But it should still help in building an intuition and spotting the patterns!
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Have fun
 | 
			
		||||
 | 
			
		||||
With that said, I hope you can have fun with rakugaki despite it being in its infancy!
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,8 +25,6 @@ Operators may have one or two arguments, where one argument corresponds to a pre
 | 
			
		|||
Note that this documentation lists a unary and binary operator of the same spelling as _two separate functions_, not overloads of a single function.
 | 
			
		||||
 | 
			
		||||
The argument name usually does not matter when calling the function - it is only used for documentation purposes.
 | 
			
		||||
The one exception is arguments called `...`, which signify that zero or more arguments can be passed to the function at that position.
 | 
			
		||||
(Currently there are no functions that accept any number of arguments, though.)
 | 
			
		||||
 | 
			
		||||
The argument _type_ however is important.
 | 
			
		||||
If you try to use a function with the wrong type of value as its argument, it will fail with an error.
 | 
			
		||||
| 
						 | 
				
			
			@ -567,7 +565,7 @@ For example, consider multiplicatively blending two colors.
 | 
			
		|||
```haku
 | 
			
		||||
-- This is how you can multiply two colors together.
 | 
			
		||||
-- Note that the `*` operator works for colors, so you don't need to define this in your brushes.
 | 
			
		||||
mulRgba = \a, b ->
 | 
			
		||||
mulRgba: \a, b ->
 | 
			
		||||
  rgba (rgbaR a * rgbaR b) (rgbaG a * rgbaG b) (rgbaB a * rgbaB b) (rgbaA a * rgbaA b)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -575,11 +573,11 @@ If haku represented colors using an 8-bit `0` to `255` range instead, to multipl
 | 
			
		|||
 | 
			
		||||
```haku
 | 
			
		||||
-- NOTE: This example does NOT work correctly.
 | 
			
		||||
mulRgba = \a, b ->
 | 
			
		||||
  let red = (rgbaR a * rgbaR b) / 255
 | 
			
		||||
  let green = (rgbaG a * rgbaG b) / 255
 | 
			
		||||
  let blue = (rgbaB a * rgbaB b) / 255
 | 
			
		||||
  let alpha = (rgbaA a * rgbaA b) / 255
 | 
			
		||||
mulRgba: \a, b ->
 | 
			
		||||
  red = (rgbaR a * rgbaR b) / 255
 | 
			
		||||
  green = (rgbaG a * rgbaG b) / 255
 | 
			
		||||
  blue = (rgbaB a * rgbaB b) / 255
 | 
			
		||||
  alpha = (rgbaA a * rgbaA b) / 255
 | 
			
		||||
  rgba red green blue alpha
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -683,32 +681,38 @@ Some of these operations may be a bit confusing, so here are some examples.
 | 
			
		|||
 | 
			
		||||
```haku
 | 
			
		||||
-- To add two to all elements in a list:
 | 
			
		||||
list = range 1 4  -- [1, 2, 3, 4]
 | 
			
		||||
twoAdded = map list \x ->
 | 
			
		||||
list: range 1 4  -- [1, 2, 3, 4]
 | 
			
		||||
twoAdded: map list \x ->
 | 
			
		||||
  x + 2
 | 
			
		||||
-- [3, 4, 5, 6]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
-- To filter out only even numbers in a list:
 | 
			
		||||
list = range 1 10
 | 
			
		||||
isEven = \x -> mod x 2 == 0
 | 
			
		||||
onlyEven = filter list isEven
 | 
			
		||||
list: range 1 10 -- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 | 
			
		||||
isEven: \x -> mod x 2 == 0
 | 
			
		||||
onlyEven: filter list isEven
 | 
			
		||||
-- [2, 4, 6, 8, 10]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
-- To sum all the numbers in a list:
 | 
			
		||||
list = [1, 3, 10, 2, 30, 4, 1]
 | 
			
		||||
sum = reduce list 0 \acc, value -> acc + value
 | 
			
		||||
list: [1, 3, 10, 2, 30, 4, 1]
 | 
			
		||||
sum: reduce list 0 \acc, value -> acc + value
 | 
			
		||||
-- 51
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
```haku
 | 
			
		||||
-- To flatten a singly-nested list:
 | 
			
		||||
list = [[1, 2], [3, 4], [5, 6]]
 | 
			
		||||
flatList = flatten list  -- [1, 2, 3, 4, 5, 6]
 | 
			
		||||
list: [[1, 2], [3, 4], [5, 6]]
 | 
			
		||||
flatList: flatten list  -- [1, 2, 3, 4, 5, 6]
 | 
			
		||||
 | 
			
		||||
-- Note that this only applies to a single level of nesting:
 | 
			
		||||
deepList = [[[1, 2, 3, 4]]]
 | 
			
		||||
lessDeepList = flatten deepList  -- [[1, 2, 3, 4]]
 | 
			
		||||
deepList: [[[1, 2, 3, 4]]]
 | 
			
		||||
lessDeepList: flatten deepList  -- [[1, 2, 3, 4]]
 | 
			
		||||
 | 
			
		||||
-- This can be used to join lists together without nesting:
 | 
			
		||||
join: \a, b -> flatten [a, b]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue