Skip to content

Commit 6611505

Browse files
committed
media/posts/ => media/content/
`media/posts/` was accidentally ignored by `.gitignore`.
1 parent cfc2f9c commit 6611505

32 files changed

+80
-31
lines changed

content/rust-is-hard-or-the-misery-of-mainstream-programming.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[HN]: https://news.ycombinator.com/item?id=31601040
66
[r/rust]: https://www.reddit.com/r/rust/comments/v3cktw/rust_is_hard_or_the_misery_of_mainstream/
77

8-
![](../media/posts/rust-is-hard-or-the-misery-of-mainstream-programming/rustaceans-meme.jpeg)
8+
![](../media/content/rust-is-hard-or-the-misery-of-mainstream-programming/rustaceans-meme.jpeg)
99

1010
When you use Rust, it is sometimes outright preposterous how much knowledge of language, and how much of programming ingenuity and curiosity you need in order to accomplish the most trivial things. When you feel particularly desperate, you go to [rust/issues] and search for a solution for your problem. Suddenly, you find an issue with an explanation that it is theoretically impossible to design your API in this way, owing to some subtle language bug. The issue is <span style="background-color: rgb(35, 134, 54); color: white; display: inline-block; padding: 5px 12px; border-radius: 28px; font-size: 16px; font-family: sans-serif;"><svg style="vertical-align: middle; margin-bottom: 3px;" height="16" class="octicon octicon-issue-opened" viewBox="0 0 16 16" version="1.1" width="16" aria-hidden="true"><path fill="#FFFFFF" d="M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path><path fill="#FFFFFF" fill-rule="evenodd" d="M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z"></path></svg> Open</span> and dated Apr 5, 2017.
1111

@@ -355,11 +355,11 @@ error[E0308]: mismatched types
355355

356356
(In a real-world scenario, the above error would probably be [20x bigger].)
357357

358-
[20x bigger]: ../media/posts/rust-is-hard-or-the-misery-of-mainstream-programming/teloxide-error.txt
358+
[20x bigger]: ../media/content/rust-is-hard-or-the-misery-of-mainstream-programming/teloxide-error.txt
359359

360360
## Third try: Using Arc
361361

362-
<img src="../media/posts/rust-is-hard-or-the-misery-of-mainstream-programming/arc-meme.jpeg" width="580px" />
362+
<img src="../media/content/rust-is-hard-or-the-misery-of-mainstream-programming/arc-meme.jpeg" width="580px" />
363363

364364
When I was novice in Rust, I used to think that references are simpler than smart pointers. Now I am using `Rc`/`Arc` almost everywhere where using lifetimes causes too much pain and performance is not a big deal. Believe or not, all of the aforementioned problems were caused by that single lifetime in `type Handler`, `'a`.
365365

@@ -521,7 +521,7 @@ So if I "figured out it all", why should not I develop a sublime version of Rust
521521

522522
If you still want to create a PL of the future, I wish you good luck and strong mental health. You are endlessly courageous and hopelessly romantic.
523523

524-
![](../media/posts/rust-is-hard-or-the-misery-of-mainstream-programming/sad-keanu.jpeg)
524+
![](../media/content/rust-is-hard-or-the-misery-of-mainstream-programming/sad-keanu.jpeg)
525525

526526
## Related ideas
527527

content/sat-supercompilation.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,13 @@ A supercompiler's input is a pair of an expression and program, the latter being
7171
Let the task for a hypothetic supercompiler be the expression `add(S(Z), S(S(Z)))` together with the definition of `add` above. In this case, the work of a supercompiler is as simple as sequential reduction of the initial expression to the target expression `S(S(S(Z)))`, according to the rules of `add`:
7272

7373
<div align="center">
74-
<img src="../media/posts/sat-supercompilation/add-1-2.png" width="400px">
74+
<img src="../media/content/sat-supercompilation/add-1-2.png" width="400px">
7575
</div>
7676

7777
However, to think of a supercompiler as of a mere expression evaluator is a grave mistake. Let us consider what happens when it encounters a _variable_ that does not stand for some concrete expression. For example, let the task be `add(S(S(Z)), b)` with the same definition of `add`, where `b` is understood as "any" expression:
7878

7979
<div align="center">
80-
<img src="../media/posts/sat-supercompilation/add-2-b.png" width="400px">
80+
<img src="../media/content/sat-supercompilation/add-2-b.png" width="400px">
8181
</div>
8282

8383
It _just_ works!
@@ -87,21 +87,21 @@ A supercompiler saw the variable `b` when trying to reduce `S(S(add(Z, b)))`, an
8787
Now consider what happens if there is a need to _pattern-match_ on an unknown variable. In this case, we cannot just proceed with "direct" computation since there are several possibilities of the form the variable may take. Suppose that the task is `add(a, 2)` [^naturals] with the same function `add`. What a supercompiler does is that it _analyze_ the expression `add(a, 2)` according to all the possibilities of `a`, which are either `Z` or `S(v1)`, where `v1` is some fresh variable identifier. The situation looks like this:
8888

8989
<div align="center">
90-
<img src="../media/posts/sat-supercompilation/add-a-2.png" width="600px">
90+
<img src="../media/content/sat-supercompilation/add-a-2.png" width="600px">
9191
</div>
9292

9393
A supercompiler has built an (incomplete) _process tree_ that describes the execution of `add(a, 2)` in a general sense. In the first branch, `a` is substituted for `Z` according to the first rule of `add`; in the second branch, `a` is substituted for `S(v1)` according to the second rule of `add`. The resulting two nodes are labelled with expressions that resulted in reducing a particular substitution of the parent expression.
9494

9595
However, the supercompilation is not complete yet: there is still a node labelled as `S(add(v1, 2))`. A supercompiler decides to _decompose_ it, meaning to move `add(v1, 2)` out of `S(...)` in the following way:
9696

9797
<div align="center">
98-
<img src="../media/posts/sat-supercompilation/add-a-2-decompose.png" width="600px">
98+
<img src="../media/content/sat-supercompilation/add-a-2-decompose.png" width="600px">
9999
</div>
100100

101101
After that, if we proceed with supercompiling `add(v1, 2)`, we will eventually arrive at the initial expression `add(a, 2)`. This is because the expressions `add(v1, 2)` and `add(a, 2)` are _alpha equivalent_, meaning that they only differ in the names of variables. A supercompiler should be smart enough to detect this situation of alpha equivalence and, instead of continuing infinite supercompilation, just draw a back arrow from `add(v1, 2)` to the initial node as depicted below:
102102

103103
<div align="center">
104-
<img src="../media/posts/sat-supercompilation/add-a-2-loop.png" width="600px">
104+
<img src="../media/content/sat-supercompilation/add-a-2-loop.png" width="600px">
105105
</div>
106106

107107
Hooray, the supercompilation of `add(a, 2)` is now complete!
@@ -198,7 +198,7 @@ That being said, a [sufficiently smart supercompiler] can transform a two-pass l
198198
[sufficiently smart supercompiler]: https://twitter.com/hirrolot/status/1741049231656280289
199199

200200
<div align="center">
201-
<img src="../media/posts/sat-supercompilation/list-fusion.jpeg">
201+
<img src="../media/content/sat-supercompilation/list-fusion.jpeg">
202202
</div>
203203

204204
A similar example can be found in _"Rethinking Supercompilation"_ by Neil Mitchell [^rethinking-supercomp] and in [^supercompiler-concept] (section 6, _"Examples of supercompilation"_).
@@ -232,7 +232,7 @@ $$
232232
Then `OR(x, OR(y, OR(NOT z, F)))` would correspond to "x OR y OR NOT z":
233233

234234
<div align="center">
235-
<img src="../media/posts/sat-supercompilation/if-x-y-not-z.png" width="550px">
235+
<img src="../media/content/sat-supercompilation/if-x-y-not-z.png" width="550px">
236236
</div>
237237

238238
Now consider the encoding of a conjunction of clauses [^and-blowup]:
@@ -248,35 +248,35 @@ $$
248248
Then `AND(OR(x, F), AND(OR(NOT y, F), T))` would correspond to "x AND NOT y":
249249

250250
<div align="center">
251-
<img src="../media/posts/sat-supercompilation/x-and-not-y.png" width="550px">
251+
<img src="../media/content/sat-supercompilation/x-and-not-y.png" width="550px">
252252
</div>
253253

254254
Is the formula satisfiable? Yes, because we can assign `x` to `T` and `y` to `F`.
255255

256256
Now consider the formula `AND(OR(x, F), AND(OR(NOT x, F), T))`, which is equivalent to "x AND NOT x":
257257

258258
<div align="center">
259-
<img src="../media/posts/sat-supercompilation/x-and-not-x.png" width="550px">
259+
<img src="../media/content/sat-supercompilation/x-and-not-x.png" width="550px">
260260
</div>
261261

262262
Is the formula satisfiable? To figure out, let us remove _dead paths_ from the formula:
263263

264264
<div align="center">
265-
<img src="../media/posts/sat-supercompilation/if-x-f-f.png" width="550px">
265+
<img src="../media/content/sat-supercompilation/if-x-f-f.png" width="550px">
266266
</div>
267267

268268
We have replaced the lower `if x` node with its first child `F` because `x` was already assigned `T` in this path. Since there are no `T` leafs in the resulting tree, it is correct to say that the formula is unsatisfiable: with any value of `x` we will arrive at `F`.
269269

270270
One more example is `AND(OR(x, F), AND(OR(x, OR(y, F)), AND(OR(NOT x, F), T)))`, which is equivalent to "x AND (y OR z) AND NOT x":
271271

272272
<div align="center">
273-
<img src="../media/posts/sat-supercompilation/if-last-example.png" width="600px">
273+
<img src="../media/content/sat-supercompilation/if-last-example.png" width="600px">
274274
</div>
275275

276276
After removing dead paths:
277277

278278
<div align="center">
279-
<img src="../media/posts/sat-supercompilation/if-last-example-final.png" width="600px">
279+
<img src="../media/content/sat-supercompilation/if-last-example-final.png" width="600px">
280280
</div>
281281

282282
The general observation is that, after encoding a CNF formula as an if-tree and removing dead paths from it, if there is at least one `T` leaf, the initial formula is satisfiable; otherwise, the formula is unsatisfiable because there is no path from the root that will take us to `T`. Think about it for a moment.
@@ -290,13 +290,13 @@ _Positive supercompilation_ is a particular model of supercompilation that propa
290290
In this post, we only deal with positive supercompilation. Consider the schematic representation of the CNF formula “x AND NOT x” again:
291291

292292
<div align="center">
293-
<img src="../media/posts/sat-supercompilation/x-and-not-x.png" width="550px">
293+
<img src="../media/content/sat-supercompilation/x-and-not-x.png" width="550px">
294294
</div>
295295

296296
Imagine that `if`, `T`, and `F` are SLL constructors, with `if` holding three arguments: two branches and a variable, which is `T` in the first branch and `F` in the second. If we analyze the uppermost `if x`, we will get the following "process tree" [^if-process-tree]:
297297

298298
<div align="center">
299-
<img src="../media/posts/sat-supercompilation/if-x-f-f.png" width="550px">
299+
<img src="../media/content/sat-supercompilation/if-x-f-f.png" width="550px">
300300
</div>
301301

302302
Supercompilation acted as a dead code eliminator! This is because `x=T` was propagated to the first branch of the uppermost `if x`, resulting in the elimination of the branch `T` of the innermost `if x`. The second uppermost branch remains unchanged.

content/whats-the-point-of-the-c-preprocessor-actually.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ Not really.
163163
When I started designing Metalang99, I was aware of how metaprogramming can go insane. _Metalang99 is an attempt to make it less insane_. With some unhealthy curiosity, you might accidentally call Satan, and he will kindly produce gigabytes of error messages for you, dear. Not kidding, I experienced it on my own:
164164

165165
<div align="center">
166-
![](../media/posts/whats-the-point-of-the-c-preprocessor-actually/error.png)
166+
![](../media/content/whats-the-point-of-the-c-preprocessor-actually/error.png)
167167
</div>
168168

169169
In the above error, I asked a compiler to show a full backtrace of macro expansions. Most of the time, it is just a senseless bedsheet of macro definitions, so I always turn it down by `-ftrack-macro-expansion=0` (GCC) or `-fmacro-backtrace-limit=1` (Clang).
@@ -248,7 +248,7 @@ Looks less nice?
248248

249249
Bad news: it is impossible to handle all kinds of errors in macros gracefully. But we do not need to handle _all_ of them. It would be sufficient to handle _most of them_. Now I shall convince you that even Rust, a language that sells itself as a language with comprehensible errors, even Rust sometimes produces complete nonsense:
250250

251-
![](../media/posts/whats-the-point-of-the-c-preprocessor-actually/big-boy.png)
251+
![](../media/content/whats-the-point-of-the-c-preprocessor-actually/big-boy.png)
252252

253253
(Kindly given by [Waffle Lapkin].)
254254

@@ -257,10 +257,10 @@ Bad news: it is impossible to handle all kinds of errors in macros gracefully. B
257257
<details>
258258
<summary>Show more hordes of errors...</summary>
259259

260-
![](../media/posts/whats-the-point-of-the-c-preprocessor-actually/1.jpg)
261-
![](../media/posts/whats-the-point-of-the-c-preprocessor-actually/2.jpg)
262-
![](../media/posts/whats-the-point-of-the-c-preprocessor-actually/3.jpg)
263-
![](../media/posts/whats-the-point-of-the-c-preprocessor-actually/4.jpg)
260+
![](../media/content/whats-the-point-of-the-c-preprocessor-actually/1.jpg)
261+
![](../media/content/whats-the-point-of-the-c-preprocessor-actually/2.jpg)
262+
![](../media/content/whats-the-point-of-the-c-preprocessor-actually/3.jpg)
263+
![](../media/content/whats-the-point-of-the-c-preprocessor-actually/4.jpg)
264264

265265
(I believe some of them were on stable Rust.)
266266

content/why-static-languages-suffer-from-complexity.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ references:
2121

2222
<p class="discussions">[HN](https://news.ycombinator.com/item?id=29996240) · [r/rust](https://www.reddit.com/r/rust/comments/s7sgxq/why_static_languages_suffer_from_complexity/) · [r/ProgrammingLanguages](https://www.reddit.com/r/ProgrammingLanguages/comments/s7shox/why_static_languages_suffer_from_complexity/)</p>
2323

24-
![](../media/posts/why-static-languages-suffer-from-complexity/preview.jpg)
24+
![](../media/content/why-static-languages-suffer-from-complexity/preview.jpg)
2525

2626
People in the programming language design community strive to make their languages more expressive, with a strong type system, mainly to increase ergonomics by avoiding code duplication in final software; however, the more expressive their languages become, the more abruptly duplication penetrates the language itself.
2727

@@ -167,7 +167,7 @@ However, sometimes we may want to apply type-level computation to ordinary `stru
167167
[`Generic`]: https://docs.rs/frunk/latest/frunk/generic/trait.Generic.html
168168
[DTOs]: https://en.wikipedia.org/wiki/Data_transfer_object
169169

170-
![](../media/posts/why-static-languages-suffer-from-complexity/rust-meme.png)
170+
![](../media/content/why-static-languages-suffer-from-complexity/rust-meme.png)
171171

172172
## Sum type -- Tree
173173

@@ -393,7 +393,7 @@ main = assert ((add one two) == three) $ pure ()
393393

394394
## Type-level logic reified
395395

396-
![](../media/posts/why-static-languages-suffer-from-complexity/chad-meme.png)
396+
![](../media/content/why-static-languages-suffer-from-complexity/chad-meme.png)
397397

398398
The purpose of this writeup is only to convey the intuition behind the statics-dynamics biformity and not to provide a formal proof -- for the latter, please refer to an awesome library called [`type-operators`] (by the same person who implemented Smallfuck on types). In essence, it is an algorithmic macro eDSL that boils down to type-level manipulation with traits: you can define algebraic data types and perform data manipulations on them similar to how you normally do in Rust, but in the end, the whole code will dwell on the type-level. For more details, see the [translation rules](https://github.com/sdleffler/type-operators-rs/blob/master/src/lib.rs) and an [excellent guide](https://github.com/sdleffler/type-operators-rs/blob/master/README.md) by the same author. Another noteworthy project is [Fortraith], which is a "compile-time compiler that compiles Forth to compile-time trait expressions":
399399

@@ -559,7 +559,7 @@ BOOST_MPL_ASSERT_RELATION(
559559
</li>
560560
</ul>
561561

562-
![](../media/posts/why-static-languages-suffer-from-complexity/tmp-meme.png)
562+
![](../media/content/why-static-languages-suffer-from-complexity/tmp-meme.png)
563563

564564
Sometimes, software engineers find their languages too primitive to express their ideas even in dynamic code. But they do not give up:
565565

@@ -584,7 +584,7 @@ Recalling the famous [Greenspun's tenth rule], such handmade metalanguages are t
584584
[Greenspun's tenth rule]: https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule
585585
[metalinguistic abstraction]: https://en.wikipedia.org/wiki/Metalinguistic_abstraction
586586

587-
![](../media/posts/whats-the-point-of-the-c-preprocessor-actually/2.jpg)
587+
![](../media/content/whats-the-point-of-the-c-preprocessor-actually/2.jpg)
588588

589589
This is woefully to say, but it seems that an "expressive" PL nowadays means "Hey there, I have seriously messed up with the number of features, but that is fine!"
590590

@@ -722,7 +722,7 @@ Yeah, Idris detects the error and produces a type mismatch! This is basically ho
722722
[Will Crichton's attempt]: https://willcrichton.net/notes/type-safe-printf/
723723
[Zig]: https://ziglang.org/
724724

725-
![](../media/posts/why-static-languages-suffer-from-complexity/printf-meme.png)
725+
![](../media/content/why-static-languages-suffer-from-complexity/printf-meme.png)
726726

727727
I already anticipate the question: what is the problem of implementing `printf` with macros? After all, [`println!`] works just fine in Rust. The problem is macros. Think for yourself: why a programming language needs heavy-duty macros? Because we may want to extend it. Why may we want to extend it? Because a programming language does not fit our needs: we cannot express something using regular linguistic abstractions, and this is why we decide to extend the language with ad-hoc meta-abstractions. In the main section, I provided an argumentation why this approach sucks -- because a macro system has no clue about a language being manipulated; in fact, procedural macros in Rust is just a fancy name for the [M4 preprocessor]. You guys integrated M4 into your language. Of course, this is [better than external M4], but it is nevertheless a method of the 20'th century; proc. macros even cannot manipulate an [_abstract_ syntax tree], because [`syn::Item`], a common structure used to write proc. macros, is indeed known as a [_concrete_ syntax tree], or "parse tree". On the other hand, types are a natural part of a host language, and this is why if we can express a programmatic abstraction using types, we _reuse_ linguistic abstractions instead of resorting to ad-hoc machinery. Ideally, a programming language should have either no macros or only a lightweight form of syntax rewriting rules (like Scheme's [`extend-syntax`] or [syntax extensions] of Idris), in order to keep the language consistent and well-suited to solve expected tasks.
728728

@@ -820,7 +820,7 @@ The only inconvenience I experienced during the development of `printf` is [mass
820820
[`@typeName`]: https://ziglang.org/documentation/master/#typeName
821821
[`@TypeOf`]: https://ziglang.org/documentation/master/#TypeOf
822822

823-
![](../media/posts/why-static-languages-suffer-from-complexity/types-meme.png)
823+
![](../media/content/why-static-languages-suffer-from-complexity/types-meme.png)
824824

825825
Everything is good except that Zig is a systems language. On [their official website], Zig is described as a "general-purpose programming language", but I can hardly agree with this statement. Yes, you can write virtually any software in Zig, but should you? My experience in maintaining high-level code in Rust and C99 says **NO**. The first reason is safety: if you make a systems language safe, you will make programmers deal with borrow checker and ownership (or equivalent) issues that have absolutely nothing to do with business logic (believe me, I know the pain); otherwise, if you choose the C-way manual memory management, you will make programmers debugging their code for long hours with the hope that `-fsanitize=address` would show something meaningful. Moreover, if you want to build new abstractions atop of pointers, you will end up with `&str`, `AsRef<str>`, `Borrow<str>`, `Box<str>`, and the similar. Come on, I just want a UTF-8 string; most of the time, I do not really care whether it is one of those alternatives.
826826

235 KB
Loading
106 KB
Loading
98.5 KB
Loading

0 commit comments

Comments
 (0)