aboutsummaryrefslogtreecommitdiff
path: root/documentation/book/the_lux_programming_language/appendix_d.md
blob: 2d8adcd8ebaebc8a125d1637536a76a4729c850a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# Appendix D: The art of piping

I'm a big fan of piping.

No kidding.

Piping is my favorite way of writing code.

It's almost like a game to me.

I try to figure out ways to get my code to be more pipe-sensitive, to see how far I can get while piping my code.

	My personal record is 14 steps.

## Piping macros in the standard library

Anyhow, after looking at some of the innovations of Clojure in the piping department, I decided to come up with my own tricks to try to get Lux to become a piping superpower.

I added the `library/lux/control/pipe` module, which contains several macros meant to be used within the `|>` macro, and which extend it with awesome capabilities.

Take a look at these babies:

```clojure
... Loops for pipes.
... Both the testing and calculating steps are pipes and must be given inside tuples.
(|> 1
    (loop> [(n.< 10)]
           [++]))
```

`loop>` takes a test _tuple_ and a body _tuple_.

The reason is that each of those tuples represents the steps on an implicit piping macro (_oh, yeah!_).

So `[(n.< 10)]` is like `(|> value (n.< 10))`, and `[++]` is like `(|> value ++)`.

Which value? Whatever has been piped into `loop>` from the underlying `|>` (in this case, the value `1`).

---

```clojure
... Branching for pipes.
... Both the tests and the bodies are piped-code, and must be given inside a tuple.
... If a last else-pipe isn't given, the piped-argument will be used instead.
(|> 5
    (cond> [i.even?] [(i.* 2)]
           [i.odd?]  [(i.* 3)]
           ... else branch
           [(new> -1 [])]))
```

We have looping, and now we have branching; with a `cond`-inspired piping macro (complete with _else_ branch, just in case).

But what's that thing over there? That `new>` thing?

Well, it's another piping macro. Of course!

```clojure
... Ignores the piped argument, and begins a new pipe.
(|> 20
    (i.* 3)
    (i.+ 4)
    (new> 0 [++]))
```

`new>` establishes a new piping sequence that ignores any previous one.

Useful in certain kinds of situations.

---

```clojure
... Gives a name to the piped-argument, within the given expression.
(|> 5
    (let> @ (+ @ @)))
```

`let>` binds the current value piped into it so you can refer to it multiple times within its body.

Pretty nifty, huh?

---

```clojure
... Pattern-matching for pipes.
... The bodies of each branch are NOT pipes; just regular values.
(|> 5
    (case> 0 "zero"
           1 "one"
           2 "two"
           3 "three"
           4 "four"
           5 "five"
           6 "six"
           7 "seven"
           8 "eight"
           9 "nine"
           _ "???"))
```

Yeah, that's right!

I just couldn't resist rolling full-blown pattern-matching into this.

You'll thank me later.

---

```clojure
... Monadic pipes.
... Each step in the monadic computation is a pipe and must be given inside a tuple.
(|> 5
    (do> identity.monad
         [(i.* 3)]
         [(i.+ 4)]
         [+]))
```

And just to show you I'm serious, I did the unthinkable.

Piped monadic expressions!

## How to make your own piping macros

They're easier to make than pattern-matching macros.

All you need is a macro that takes anything you want as parameters, but always takes as its last argument _the computation so far_, as it has been constructed by the `|>` macro prior to the call to your piping macro.

As an example, here's the definition for `let>`:

```clojure
(syntax: .public (let> [binding <code>.any
                        body <code>.any
                        prev <code>.any])
  (in (list (` (let [(~ binding) (~ prev)]
                 (~ body))))))
```

---

All this looks like madness, but I just couldn't contain myself.

Piping is one of the few ways of writing code that just amuses me whenever I do it.

These macros can keep you in the flow while you're writing complex code, so you don't have to switch so much between piping-code and non-piping-code.

Oh... and did I mention the `|>>` macro?

It generates for you a single-argument function that will immediately pipe its argument through all the steps you give it.

```clojure
(only (|>> (member? forbidden_definitions)
           not)
      all_definitions)
```