-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathleontrolski.rss
1648 lines (1492 loc) · 89 KB
/
leontrolski.rss
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Leon Trolski</title>
<description>The website of Leon Trolski</description>
<link>https://leontrolski.github.io</link>
<atom:link href="https://leontrolski.github.io/leontrolski.rss" rel="self" type="application/rss+xml" />
<image>
<title>Leon Trolski</title>
<url>https://leontrolski.github.io/pic.png</url>
<link>https://leontrolski.github.io</link>
</image>
<item>
<title>33 line React - thoughts</title>
<pubDate>Wed, 14 Oct 2020 09:19:37 +0100</pubDate>
<link>https://leontrolski.github.io/33-line-react-thoughts.html</link>
<guid>https://leontrolski.github.io/33-line-react-thoughts.html</guid>
<description>
<h1>33 line React - thoughts</h1>
<p>
<em><a href="33-line-react.html">Original post</a>, <a href="https://news.ycombinator.com/item?id=22776753">original discussion</a>.</em>
</p>
<p>
There were lots of insightful comments in the hacker news thread - thanks, I thought I'd write up some of the thoughts that came out of it. Looking now, this may just be a rant piece - I'll let you decide. In a vaguely Top-Gear-esque way, this post is split into <a href="#style">style</a>, <a href="#performance">performance</a>, <a href="#conclusions">conclusions</a>.
</p>
<h1>TLDR</h1>
<p>Have a go building your next frontend with <em>an as simple as possible</em> <code>pageState -> DOM</code> model, maybe use <a href="https://mithril.js.org/">mithril</a>.</p>
<h2 id="style"><code>.jsx</code>, state management and aesthetics</h2>
<p>
The React homepage, has the following snippet:
</p>
<pre><code>class Timer extends React.Component {
constructor(props) {
super(props);
this.state = { seconds: 0 };
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
&lt;div>
Seconds: {this.state.seconds}
&lt;/div>
);
}
}</code></pre>
<p>
Versus, for example, the code for the noughts and crosses in my <a href="33-line-react.html">original post</a>, there's a huge amount of ceremony here. I have to:
<ul id="ceremony">
<li>Do some OO gubbins.</li>
<li>Wrap all calls to update the state with <code>this.setState(...)</code>.</li>
<li>Conform with quite a large API.</li>
<li>Constantly pass state around with <code>props</code> and <code>this.state</code> (I understand some of this has been sorted with hooks, right?).</li>
<li>Compile the JSX to boring js.</li>
</ul>
There are alleged performance/codebase management reasons for some of these, but I remain a bit sceptical of their applicability to "normal" sized web applications.
</p>
<h3 id="ezzthetic">Ezz-thetic</h3>
<p>
To my eyes, the original mithril <a href="https://raw.githack.com/MithrilJS/mithril.js/master/examples/todomvc/todomvc.js">TodoMVC source</a> is exceptionally expressive and handsome, especially versus the equivalent React <a href="https://github.com/tastejs/todomvc/tree/gh-pages/examples/react">example</a>. Maybe I'm turning into Arthur Whitney, but I'm kind of enjoying long, dense lines like:
</p>
<pre><code>m("section#main", {style: {display: state.todos.length > 0 ? "" : "none"}}, [
m("input#toggle-all[type='checkbox']", {checked: state.remaining === 0, onclick: ui.toggleAll})
...</code></pre>
<p>Consider the recommended React/JSX equivalent:</p>
<pre><code>if (todos.length) {
main = (
&lt;section className="main">
&lt;input
id="toggle-all"
className="toggle-all"
type="checkbox"
onChange={this.toggleAll}
checked={activeTodoCount === 0}
/>
&lt;label
htmlFor="toggle-all"
/>
&lt;ul className="todo-list">
{todoItems}
&lt;/ul>
&lt;/section>
);
}</code></pre>
<h3>Routing</h3>
<p>
As a consumer of webpages, I'm not sure I've ever seen the URL change in an SPA and thought "phewph, I'm glad I wasn't redirected to a new page" - just gimme a normal <code>&lt;a></code> and split your app up yo.
</p>
<h2 id="performance">Performance</h2>
<p>
I had a very unscientific play around with this neat <a href="https://localvoid.github.io/uibench/">benchmarking tool</a>, you can use the "Custom URL"s <code>http://leontrolski.github.io</code> <code>/benchmark</code>, <code>/benchmark/mithril.html</code>, <code>/benchmark/lit-html.html</code> to compare yourself. I'm going to keep the performance figures intentionally vague - I was comparing Vanilla JS, React 16, mithril and 33-line.
<ul>
<li>React and mithril performed very similarly.</li>
<li>React tended to be faster than 33-line by a factor of 2 to 10.</li>
<li>The Vanilla JS solution would often outperform 33-line.</li>
<li>For smaller DOM trees, often the <em>winner</em> was Vanilla JS (small discussion below).</li>
<li>The <a href="https://localvoid.github.io/uibench-react/16/main.js">React code</a> was (gzipped + minified) 40kb, the <a href="https://leontrolski.github.io/benchmark/main.js">33-line code</a> was (gzipped, not minified) 1.8kb, mithril was (gzipped + minified) 9kb.</li>
<li>The "JS Init Time" of react would be 2 to 10+ times slower than both 33-line and mithril, think in the order of +100ms.</li>
<li>The performance of 33-line got proportially worse as the number of divs increased, this makes sense, given the diff algorithm is <em>basic</em>.</li>
<li><a href="https://lit-html.polymer-project.org/">lit-html</a> performs a weenie bit quicker than mithril in some benchmarks, but has a longer "JS Init Time" time. Admittedly, <a href="https://github.com/leontrolski/leontrolski.github.io/blob/master/benchmark/lit-html.html">my implementation</a> is a naive translation of the mithril code, so may be missing some tricks.</li>
</ul>
</p>
<h3>Performance - Notes</h3>
<p>
The <a href="https://github.com/leontrolski/leontrolski.github.io/blob/4f9cea8a5afc55252d38eb1aa1a20eda264a880f/benchmark/main.js">Vanilla JS one</a> just shoves strings of html together and does a big <code>container.innerHTML =</code>, nice and simple. On the other hand, string munging sucks for obvious reasons. Also, you end up with a lot of updates flashing around in the devtools inspector.
</p>
<p>
I had to write a <a href="https://github.com/leontrolski/leontrolski.github.io/blob/54bb7ff011065f0d46ae8f2e3c841dc3aa30c157/benchmark/main.js#L67-L69">few extra lines</a> of 33-line to handle <code>data-</code> attributes, that cranked it up to 37 lines. I think if you were to try productionise this toy code you'd end up with about 3/4 of a <a href="https://mithril.js.org/">mithril</a>.
</p>
<p>
I did one run with the <a href="https://github.com/Freak613/stage0">stage0</a> library thingy, the code was a bit more <a href="https://github.com/Freak613/stage0/blob/master/examples/uibench/app.js">gnarly</a>, but it was <em>rapid</em>. If I was writing eg. a game or a big spreadsheet app that needed high performance, I'd definitely be considering a library in this space.
</p>
<p>
Things that I'd imagine React particularly excels at versus a naive approach would be things like clock counters/animations - small bits of the DOM changing at high frequency - the tradeoff for this performance seems to be state-management-based API complexity. If one is to use a simpler <code>pageState -> DOM</code> model with few library hooks into the guts, it may be necessary to implement clocks etc. out of band of the normal library's render loop.
</p>
<h2>Hacker news meta bit</h2>
<p>
For a while, the top-voted thread was people moaning about how a variable was called <code>m</code>, then a later comment in the code said it was a <code>grid</code>. I agree it was maybe a bit annoying, but I dunno, you read the article and that was your takeaway.. I've been part of a fair few code reviews with this vibe in my time :-)
</p>
<h2 id="conclusions">Conclusions</h2>
<p>
Doing <code>document.querySelectorAll('*')</code> on the airbnb map view (a reasonably complex SPA) returns <b>3407</b> elements.
</p>
<p>
With no thought to performance at all, a simple library can render in the order of 100s of divs per <em>millisecond</em>. You could probably swap React out with 33-line on most sites and no-one would notice, you could also swap it out with some Vanilla JS string munging too - although the developer egonomics would be a bit rubbish.
</p>
<p>
In their next project, I'd recommended any frontend devs out there embrace there inner minimalist and cut the <a href="#ceremony">fat</a>. Make a plain ol' <code>state</code> variable at the top of your file, throw in some functions that mutate it, a touch of <a href="https://mithril.js.org/">mithril</a> to render it, and bang, you're done.
</p>
<p>
If your site's slow (unless you're something really complicated like a game/spreadsheet), it's probably that you put a lot of crap on it, rather than anything to do with how you render your divs.
</p>
</body>
</description>
</item>
<item>
<title>33 line React</title>
<pubDate>Fri, 14 Jan 2022 09:04:32 +0000</pubDate>
<link>https://leontrolski.github.io/33-line-react.html</link>
<guid>https://leontrolski.github.io/33-line-react.html</guid>
<description>
&#x26a0;&#xfe0f; This article contains JS code. It might not work properly in your feed reader.
You might want to visit the <a href="https://leontrolski.github.io/33-line-react.html">original article</a>.
<h1>33 line React</h1>
<p>
<em><a href="33-line-react-thoughts.html">Thoughts</a> on reading through the hacker news <a href="https://news.ycombinator.com/item?id=22776753">response</a>. <a href="96-line-react-jsx.html">96 line</a> version with JSX compiler.</em>
</p>
<br>
<p>
<a href="https://reactjs.org/">React</a>
<ul>
<li>you pass in a function that <em>takes</em> state and <em>returns</em> a virtual DOM (just a tree of plain ol' <code>js</code> objects)</li>
<li>it renders that virtual DOM as a <em>real</em> DOM in the browser</li>
<li>if you change the state, it runs the function again, this returns a new virtual DOM</li>
<li>it efficiently updates the real DOM so that it matches the new virtual DOM</li>
</ul>
It also does a load of other crap as well, but we're going to ignore that.
</p>
<p>
In this post, I'm going to make the smallest React-like thing that can do the above. It's very <a href="https://mithril.js.org/">mithril</a> influenced.
</p>
<p>
Here are sample applications: <a href="33-line-react-calendar.html">calendar picker</a>, <a href="33-line-react-snake.html">snake</a> that use the <a href="https://github.com/leontrolski/leontrolski.github.io/blob/master/33-line-react.js">library</a>.
</p>
<p>
<em>Lots of the code looks pretty code-golfy - I promise I don't do stuff like this at work, neither should you :-)</em>
</p>
<h2>Noughts and crosses</h2>
<p>
We're going to make this noughts and crosses game:
<div id="noughts"></div>
</p>
<style>
.o{background:red;}
.x{background:blue;}
.cell{height:4em;width:4em;border:1px solid black;}
</style>
<script>
let currentPlayer = 'o'
let winner = null
const g = [['', '', ''], ['', '', ''], ['', '', '']] // grid
const move = (value, i, j)=>{
if (value !== '') return
g[i][j] = currentPlayer
currentPlayer = currentPlayer === 'x'? 'o' : 'x'
const winners = [
...[0, 1, 2].map(i=>[g[i][0], g[i][1], g[i][2]].join('')),
...[0, 1, 2].map(j=>[g[0][j], g[1][j], g[2][j]].join('')),
[g[0][0], g[1][1], g[2][2]].join(''),
[g[2][0], g[1][1], g[0][2]].join(''),
]
if(winners.includes('xxx')) winner = 'x'
if(winners.includes('ooo')) winner = 'o'
renderNoughts()
}
const Cell = (value, i, j)=>m('button.cell',
{onclick: ()=>move(value, i, j)}, value
)
const Noughts = ()=>m('',
winner
? m('marquee', `winner: ${winner}`)
: m('h3', `current player: ${currentPlayer}`),
m('table', g.map(
(row, i)=>m('tr', row.map(
(value, j)=>m('td', {class: value}, Cell(value, i, j)))))),
)
const renderNoughts = ()=>m.render(
document.getElementById('noughts'),
{children: [Noughts()]},
)
renderNoughts()
</script>
<p>
Now let's look at the code to this, you can also just view the page source if you want.
</p>
<pre><code>let currentPlayer = 'o'
let winner = null
const g = [['', '', ''], ['', '', ''], ['', '', '']] // grid
const move = (value, i, j)=>{
// ... game logic goes here
renderNoughts()
}
const Cell = (value, i, j)=>m('button.cell',
{onclick: ()=>move(value, i, j)}, value
)
const Noughts = ()=>m('',
winner
? m('marquee', `winner: ${winner}`)
: m('h3', `current player: ${currentPlayer}`),
m('table', g.map(
(row, i)=>m('tr', row.map(
(value, j)=>m('td', {class: value}, Cell(value, i, j)))))),
)
const renderNoughts = ()=>m.render(
document.getElementById('noughts'),
{children: [Noughts()]},
)
renderNoughts()</code></pre>
<p>Cute, so what's going on?</p>
<p>First we defined some state:</p>
<pre><code>let currentPlayer = 'o'
let winner = null
const g = [['', '', ''], ['', '', ''], ['', '', '']] // grid</code></pre>
<p>These hold the state of our game, we will mutate them.</p>
<pre><code>const move = (value, i, j){...}</code></pre>
<p>This function makes a move in the game, it takes <code>'x'</code> or <code>'o'</code> along with 2 integer coordinates. It will mutate all the state variables to reflect the new state of the game. After that, it calls <code>renderNoughts()</code>, this is a call to rerender the game - but we'll come back to that.</p>
<p>Next we define the functions that return virtual DOMs, <code>Noughts</code> and <code>Cell</code>.</p>
<p>
The <code>m(...)</code> calls take:
<ul>
<li>a tag name (eg. <code>'tr'</code>), with <code>.</code>-separated class names</li>
<li>(optionally) a <code>{string: any}</code> object containing all the attributes to attach to the DOM node</li>
<li>an arbitrarily nested list of <b>children</b> - these are other virtual DOM nodes or strings of text</li>
</ul>
And return virtual DOM elements, for example, calling <code>Noughts()</code> would return:
</p>
<pre><code>{
tag: 'div',
attrs: {},
classes: [],
children: [
{
tag: 'h3',
attrs: {},
classes: [],
children: [
'current player: x'
]
},
{
tag: 'table',
attrs: {},
classes: [],
children: [
{
tag: 'tr',
attrs: {},
classes: [],
children: [
...</code></pre>
<p>Next we make the function <code>renderNoughts()</code>, when you call it, it will call our <code>Noughts</code> function, and attempt to efficiently render the resulting virtual DOM onto <code>document.getElementById('noughts')</code></p>
<h2>How does <code>m</code> work?</h2>
<p>
Here's the source <a href="https://github.com/leontrolski/leontrolski.github.io/blob/master/33-line-react-with-comments.js">with</a> and <a href="https://github.com/leontrolski/leontrolski.github.io/blob/master/33-line-react.js">without</a> comments.
<p>
</body>
</description>
</item>
<item>
<title>96 line React with JSX</title>
<pubDate>Mon, 23 Aug 2021 13:16:39 +0100</pubDate>
<link>https://leontrolski.github.io/96-line-react-jsx.html</link>
<guid>https://leontrolski.github.io/96-line-react-jsx.html</guid>
<description>
&#x26a0;&#xfe0f; This article contains JS code. It might not work properly in your feed reader.
You might want to visit the <a href="https://leontrolski.github.io/96-line-react-jsx.html">original article</a>.
<h1>96 line React with JSX</h1>
<p>
A while ago, I wrote a short post showing a minimal <a href="33-line-react.html">33 line React</a>. A lot of the comments focused on the <a href="https://mithril.js.org/">Mithril</a>-like syntax for the virtual DOM. So that we can ignore that aspect, here is a <a href="https://github.com/leontrolski/leontrolski.github.io/blob/master/96-line-react-jsx.js">96 line React with a JSX compiler</a>.
</p>
<h2>Noughts and crosses</h2>
<p>
We're going to make this noughts and crosses game:
<div id="noughts"></div>
</p>
<style>
.o{background:red;}
.x{background:blue;}
.cell{height:4em;width:4em;border:1px solid black;}
</style>
<script type="text/jsx">
let currentPlayer = "o"
let winner = null
const g = [
["", "", ""],
["", "", ""],
["", "", ""],
]
const move = (value, i, j) => {
if (value !== "") return
g[i][j] = currentPlayer
currentPlayer = currentPlayer === "x" ? "o" : "x"
const winners = [
...[0, 1, 2].map((i) => [g[i][0], g[i][1], g[i][2]].join("")),
...[0, 1, 2].map((j) => [g[0][j], g[1][j], g[2][j]].join("")),
[g[0][0], g[1][1], g[2][2]].join(""),
[g[2][0], g[1][1], g[0][2]].join(""),
]
if (winners.includes("xxx")) winner = "x"
if (winners.includes("ooo")) winner = "o"
renderNoughts()
}
const Cell = ({ value, i, j }) => (
<button class="cell" onclick={() => move(value, i, j)}>
{value}
</button>
)
const Noughts = () => (
<div>
{winner ? <marquee>winner: {winner}</marquee> : <h3>current player: {currentPlayer}</h3>}
<table>
{g.map((row, i) => (
<tr>
{row.map((value, j) => (
<td class={value}>
<Cell value={value} i={i} j={j} />
</td>
))}
</tr>
))}
</table>
</div>
)
const renderNoughts = () => m.render(document.getElementById("noughts"), { children: [Noughts()] })
renderNoughts()
</script>
<p>
Now let's look at the code, you can also just view the page source if you want.
</p>
<pre><code>let currentPlayer = "o"
let winner = null
const g = [
["", "", ""],
["", "", ""],
["", "", ""],
]
const move = (value, i, j) => {
// ... game logic goes here
renderNoughts()
}
const Cell = ({ value, i, j }) => (
&lt;button class="cell" onclick={() => move(value, i, j)}>
{value}
&lt;/button>
)
const Noughts = () => (
&lt;div>
{winner ? &lt;marquee>winner: {winner}&lt;/marquee> : &lt;h3>current player: {currentPlayer}&lt;/h3>}
&lt;table>
{g.map((row, i) => (
&lt;tr>
{row.map((value, j) => (
&lt;td class={value}>
&lt;Cell value={value} i={i} j={j} />
&lt;/td>
))}
&lt;/tr>
))}
&lt;/table>
&lt;/div>
)
const renderNoughts = () => m.render(document.getElementById("noughts"), { children: [Noughts()] })
renderNoughts()
</code></pre>
<p>
You can read the bread-and-butter of how it works in the <a href="33-line-react.html">original post</a>, the only difference is, rather than use the simpler hyperscript syntax, we compile any <code>&lt;script type="text/jsx"></code> scripts to plain ol' Javascript with <code>m.parseJsx(source)</code>.
</p>
<p>
So for example:
</p>
<pre><code>const Cell = ({ value, i, j }) => (
&lt;button class="cell" onclick={() => move(value, i, j)}>
{value}
&lt;/button>
)</code></pre>
<p>
Gets compiled to:
</p>
<pre><code>const Cell = ({ value, i, j }) => (
m("button", {"class": "cell", "onclick": () => move(value, i, j), }, " ", value, " ")
)</code></pre>
<em>
<h3>Compiler caveats</h3>
<ul>
<li>I've tried to handle nested strings/templates/components etc, but there will be loads of edge cases this compiler doesn't handle (known example: it doesn't handle html comments).</li>
<li>It determines that we're entering JSX when there is a <code>&lt;</code> <b>not</b> followed by an equals sign or a space. I'm not sure what proper JSX compilers do.</li>
<li>There's basically no error handling, if you pass in invalid JSX, you're on your own.</li>
</ul>
</em>
<script>
// notes:
// - determines that it's not less-than with trailing space
// - doesn't handle html comments
// - <></>
</script>
</body>
</description>
</item>
<item>
<title>The CMD-click manifesto</title>
<pubDate>Thu, 23 Feb 2023 07:56:01 +0000</pubDate>
<link>https://leontrolski.github.io/cmd-click-manifesto.html</link>
<guid>https://leontrolski.github.io/cmd-click-manifesto.html</guid>
<description>
<h1>The CMD-click manifesto</h1>
<small><em>Or CTRL-click or META-click or whatever.</em></small>
<ol>
<li>Being able to CMD-click through code >> any other theoretical concerns.</li>
<li>CMD-clickability is the opposite of indirection - <em>how does this function work?</em> CMD-click it.</li>
<li>Non-CMD-clickable codebases are filled with non-meaningful abstractions and tend to be longer.</li>
<li>Static analysis tooling should be easy to write.</li>
<li>Stack traces should correspond with the structure of the code.</li>
<li>Higher order functions break the golden property, use them as a last resort.
<pre class="language-python"><code>def do_something(data, f: Callable[...]):
f(data) # I can&#39;t CMD-click f</code></pre>
</li>
<li>Trad Object Orientated code tends to break the golden property (this makes sense as <a href="https://leontrolski.github.io/poor-mans-object.html#:~:text=objects%20are%20a%20poor%20man%27s%20closures%20are%20a%20poor%20man%27s%20objects">objects are closures</a>), use it as a last resort.
<pre class="language-python"><code>def do_something(a: Animal):
a.meow() # I can&#39;t CMD-click meow</code></pre>
</li>
<li>Use enum-like information to distinguish between otherwise similar data.
<pre class="language-python"><code>class Cat(Animal): ... # bad
a = Animal(kind=AnimalKind.CAT, ...) # good</code></pre>
</li>
<li>Serializable enum-like <a href="https://basarat.gitbook.io/typescript/type-system/discriminated-unions">discriminants</a> in data can be trivially plonked into databases, regurgitated via JSON, etc.</li>
<li>Choose behaviour based on data, not class hierarchies:
<pre class="language-python"><code>def do_something(data):
if data.kind == Kind.A:
g(data) # I can CMD-click g</code></pre>
</li>
<li>Say arbitrarily complicated things about data, rather than be constrained by inflexible heirarchies.
<pre class="language-python"><code>class Cat(Animal, FourLegged): ... # bad
def has_even_number_of_legs(a: AnimalKind) -&gt; bool # good</code></pre>
</li>
<li>Strong typing aids CMD-clickability, this property of typing is more valuable than correctness.</li>
<li>Microservices in and of themselves are not bad, however:
<ol type="i">
<li>Construct your codebase such that CMD-clicking <em>across</em> a service boundary is as easy as <em>within</em> a service. </li>
<li>Typecheck service boundaries as you would any other code.</li>
<li>Use correlation ids and a <a href="https://www.datadoghq.com/blog/request-log-correlation/">Datadog</a>-like tool to make cross-service stack traces comprehensible for debugging production.</li>
</ol>
</li>
<li>Where you might have to fall back on grepping - make it easy, use full literals:
<pre class="language-python"><code>@app.route(PREFIX + &quot;/v1/add&quot;) # bad
@app.route(&quot;/payments/inbound/v1/add&quot;) # good</code></pre>
</li>
<li>Good code expresses the smallest possible state space the program could operate in - <strong>YAGNI</strong>.
<pre class="language-python"><code>def do_something(data, f: Callable[...]):
# f could be anything - big state space
f(data)
def do_something(data, kind: Kind):
# we enumerate the specific things we can do - small state space
if kind == Kind.A:
g(data)
...</code></pre>
</li>
<li>Lean in on language features, don't introduce <a href="https://github.com/gcanti/fp-ts">unnecessary</a> <a href="https://github.com/ingolemo/python-lenses">abstractions</a>.</li>
<li>There exists deep library code where we want to allow consumers to do anything (extensibility). In application development, this is the exception - you have control over the whole codebase, the code by its nature is extensible.</li>
<li>Ignore this manifesto sooner than code anything outright barbarous.</li>
</ol>
</body>
</description>
</item>
<item>
<title>A developer's summary of Consciousness and the Social Brain</em></title>
<pubDate>Wed, 2 Nov 2022 13:20:15 +0000</pubDate>
<link>https://leontrolski.github.io/consciousness.html</link>
<guid>https://leontrolski.github.io/consciousness.html</guid>
<description>
<h1>A developer's summary of Consciousness and the Social Brain</em></h1>
<p>
"<a href="https://global.oup.com/academic/product/consciousness-and-the-social-brain-9780199928644">Consciousness and the Social Brain</a>" is a book by Princeton neuroscience professor <a href="https://en.wikipedia.org/wiki/Michael_Graziano">Michael Graziano</a>, outlining his theory of consciousness - <b>the attention schema theory</b>.
</p>
<p>
This is a developer's summary only in that I can't imagine many other people having much interest in this website. Having said that, the theory itself is one of information, computation and modelling and so it feels particularly interesting to working programmers. If you enjoyed/hated this post, read the book, it'll be way better than what I'm about to write.
</p>
<h2>Intro</h2>
<p>
As a first year student I attended a talk by <a href="https://en.wikipedia.org/wiki/Susan_Blackmore">Susan Blackmore</a> on consciousness. She's a great speaker, and that talk led me to reading further on the topic including her "best of" book "<a href="https://www.susanblackmore.uk/conversations-on-consciousness/">Conversations on Consciousness</a>" in which she talks to a wide spectrum of thinkers on <a href="https://en.wikipedia.org/wiki/Hard_problem_of_consciousness">the hard problem</a>.
</p>
<p>
A few years and a few books later, one book in particular stood out for its clarity and concision - "Consciousness and the Social Brain" by Michael Graziano. The title is slightly misleading (to me at least) in that it implies his theory might be limited in scope to what our brain does as we interact with other people. Instead it is a simple, materialist theory of mind accompanied by series of observations from neuroscience that support the theory. On finishing the book you may find that the hard problem don't seem so hard after all.
</p>
<h2>Define consciousness</h2>
<p>
Everyone and their uncle has their own definition of consciousness. Questions that get chucked around might include "how do I know that I'm someone distinct from the world?", "how are my memories formed?", "how do I experience the sweetness of the apple?" etc. Graziano starts us out by saying we should just focus on a more specific word: "awareness". In our brain, there is knowledge - <q>the apple is sweet</q>, <q>the traffic is loud</q>, <q>the memory was of a cat</q>, <q>my foot is in pain</q> - and then there is <em>awareness</em> of the knowledge - <q>I am aware of the apple's sweetness</q> etc.
</p>
<p>
This checks out intuitively. If you ask someone to <q>be especially conscious for a bit</q> while they're eating an apple, they will likely focus in on their awareness of the sweetness/crunch etc. - we could similarly have asked <q>be especially aware for a bit</q>. By limiting our question to <q>what is awareness?</q> rather than <q>what is consciousness?</q>, we rid ourselves of a lot of baggage while remaining focused on the essence of the hard problem. Take for example a <a href="https://en.wikipedia.org/wiki/Philosophical_zombie">philosophical zombie</a>, would it be semantically zombie enough if it were never "aware" (as opposed to never "conscious")? If not and you think there's some other important aspect to the hard problem, this ain't the theory for you.
</p>
<p>
How our brain might represent the knowledge itself that we are aware of (taste, memories etc.) is something distinct from awareness and we'll leave that for another bit of the neuroscience department.
</p>
<h2>Graziano's squirrel</h2>
<p>
Graziano's colleague had a patient that reported a squirrel in his head. He agreed that it was physically impossible, but nonetheless it was there (he was <em>really</em> certain). The easy problem is working out how the brain might represent the squirrel, its fur, claws, its location (in the patient's head), this is a knowledge representation problem. The hard problem (especially from the patient's perspective) is how the squirrel (with all its vivid real-ness) might be there at all, in his head.
</p>
<blockquote>
If we all shared that man's delusion [...] we would be certain of its existence, [...] agree collectively that we have it [...] and yet we would have no way to jump from neuronal circuitry to squirrel. [...] We would be forced into the dualist position that the brain is somehow both a neuronal machine and, at the same time, on a higher plane, a squirrel.
</blockquote>
<p>
It is obvious in this case, there is no hard problem as there is no actual squirrel, only a description of one.
</p>
<h2>Graziano's awareness</h2>
<p>
Graziano's colleague had a patient that reported awareness in their head. He agreed that it was physically impossible, but nonetheless it was there (he was <em>really</em> certain). The easy problem is working out how the brain might represent awareness, its vividness, its ethereal nature, its location (in the patient's head)...
</p>
<p>
<em>You get the idea...</em>
</p>
<p>
Graziano's proposition is that there is still no hard problem as there is no actual awareness here, any more than there is an actual squirrel in the previous case, there is only a description of awareness.
</p>
<h2>Arrow <q>B</q></h2>
<p>
Consider the following diagram:
</p>
<pre> Awareness
↑ A ↓ B
Neuronal information
processing</pre>
<p>
A lot of theories only consider arrow <q>A</q> - <q>how does awareness arise?</q> (maybe it arises from complexity somehow, like heat?)
</p>
<p>
Given that we can report awareness, there must also exist an arrow <q>B</q>: information about awareness must eventually reach the neurons that operate our vocal chords. This leads us to ask - how does awareness then impact the neuronal machinery? Most theories consider <q>A</q> to be magical, but once you consider there <em>must</em> be an arrow <q>B</q>, and that the brain is an information processing machine, the simplest theory is that awareness <em>is</em> just an informational description in the brain.
</p>
<p>
Descriptions have the nice property of being able to describe even impossible, magical things (eg. Middle Earth) and so are a good fit for awareness. An uncontroversial example of a nonsensical thing our brain describes would be white light, we consider it to be a pure, low-colour thing, whereas we know from Newton that it's a muddy, many-coloured smush.
</p>
<h3>An optimistic note</h3>
<em>
Graziano makes the claim that this approach is deeply unsatisfying, but that "a theory does not need to be satisfying to be true". There is no ethereal magic to awareness any more, only the description of ethereal magic. Maybe it's one for the philosophers, and maybe it's my inner programmer speaking, but I don't particularly find the magic existing only as information to be particularly disappointing, or even make it particularly less "real". After all, there are branches of physics that consider everything as computation and yet I'd consider all those atoms whizzing around to be real enough for me.
</em>
<h2>What is awareness then, why bother?</h2>
<p>
A description of a squirrel would not be a useful thing for humans to have evolved to have in their head, so why awareness and why all its weird properties?
</p>
<blockquote>
Awareness itself [...] is a complex, continuously recomputed model that describes what it means - the conditions, dynamics and consequences - for a brain to attentively process information.
</blockquote>
<h2>Attention</h2>
<p>
<a href="https://en.wikipedia.org/wiki/Attention">Attention</a> is a well studied part of neuroscience. <em>Loads</em> of information comes to us via our senses and as we have limited computational resource, we limit our high-level computation of this information by only considering some of it of a time. This biasing of which-information-to-consider can be bottom up (caused by an unexpected tennis ball whizzing past our peripheral vision) or top down (caused by playing spot-the-difference). The spotlight of attention acts across the many-dimensions of the information coming in.
</p>
<h2>Attention schema</h2>
<p>
In Graziano's theory, we have an internal model of our own (and others') attention, like a simplified computer simulation/scientific theory that reduces a problem to its essence. We have internal models of real things in the physical world (like tennis balls and <a href="https://en.wikipedia.org/wiki/Body_transfer_illusion#Rubber_hand_illusion">our bodies</a>) and similarly, internal models of attention.
</p>
<p>
Consider the statement "I am aware of X". Awareness does not <em>arise</em> from the neurons responsible for describing the "I" or the "X", it is the "am aware of", a description of the relationship between the you and the thing, a description of attention.
</p>
<h2>The social bit</h2>
<p>
It is uncontroversial that we create models of others' attention (via their gaze, their facial expression, their previous behaviour etc) - this is useful for predicting their behaviour and therefore serves an obvious evolutionary purpose. What if our awareness is the same kind of thing, but about ourselves rather than others? Now we have something falsifiable that we can apply our evidence to, if this were to be true, the same bits of brain would be buzzing in both cases and if that bit of brain were damaged, so would awareness.
</p>
<p>
It turns out there are bits of the brain that look likely candidates - the TPJ and STS. As we're assuming a programmer audience rather than a biologist audience, we won't worry what these stand for.
</p>
<p>
The brain (like lots of software) has weird feedback loops and mucky system boundaries, awareness and attention are deeply bound and one may accentuate the other. I am aware of something, that may boost a top-down attention bias, that itself boosts my attention of it, that attention triggers more awareness etc. Obviously, when building a model of someone else's attention, these feedback loops don't exist. Combine this with the far richer information we receive about ourselves and we get some way to explaining why the model of <em>our</em> attention is so much more complete than that of others.
</p>
<h1>Experimental Evidence</h1>
<blockquote>
That the truth consists of hard-to-vary assertions about reality is the most important fact about the physical world. - David Deutsch
</blockquote>
<p>
<em>If like me you're not a neuroscientist, the different ways the literature describes in which the brain can be damaged and yet still broadly function are really astonishing. Also surprising is the extent to which discoveries in the field seem to be driven by damage to the brain in stroke victims. If you want to read any of the articles referenced below and can't afford to pay, remember, scihub is your friend.</em>
</p>
<h2>Altering the description</h2>
<p>
If awareness is just a description, it should be possible to change part of that description and notice it. What if we could alter the spatial position in the description? Studies <a href="https://science.sciencemag.org/content/317/5841/1048">have shown</a> that you can easily induce out-of-body experiences (ie. alter the spatial postion in the description). One way of doing it is to <a href="https://pubmed.ncbi.nlm.nih.gov/12239558/">zap the TPJ a bit</a>.
</p>
<h2>Losing awareness</h2>
<p>
To test whether it is possible to compute awareness, one way would be to construct it with a computer - this seems a bit far off at the moment. Another way would be to knock out a bit of the brain such that only awareness is diminished - this would point to some neuronal machinery being responsible for it. (If we were to diminish <em>many</em> brain functions, this would be less useful as it would still leave us with the posibility of consciousness arising from brain function in and of itself).
</p>
<p>
<a href="http://www.cnbc.cmu.edu/braingroup/papers/goodale_milner_1992.pdf">Goodale and colleagues</a> studied a patient who had damaged a specific bit of the brain relating to hand-eye coordination. The patient was able to post different shaped letters into slots accurately, but was never conscious of the shape or the size of the letter. Similarly related is the well-studied phenomenon <a href="https://en.wikipedia.org/wiki/Blindsight">blindsight</a> - patients are unable to consciously see a particular region of their visual field - for example, the top right quadrant. Patients will report that they are totally unable to see things in the region, but when shown objects there, they will still often be able to guess correctly. Similar again is <a href="https://en.wikipedia.org/wiki/Binocular_rivalry">binocular rivalry</a> - when two different images are presented one to each eye, they each flick in and out of awareness (rather than super-imposing as you might expect). These studies are not inconsistent with the attention schema theory, but they don't <em>specifically</em> support it as they don't specifically address the bits of the brain hypothesised to model attention - they could support a wide range of theories of consciousness.
</p>
<h3>Which bits of the brain are active when modelling attention?</h3>
<p>
Humans' brains are especially active in the STS when they are processing social tasks, for example <a href="https://www.jneurosci.org/content/17/11/4302">when responding to faces</a>, it will <a href="https://pubmed.ncbi.nlm.nih.gov/15701223/">react more strongly</a> to goal orientated movements (such as an arm grabbing a cup) than to movements without purpose. The STS and the TPJ are also active when participants are <a href="https://pubmed.ncbi.nlm.nih.gov/8556839/">told stories</a> about people and then asked to guess what action they might take. The same regions are <em>also</em> active when people <a href="https://pubmed.ncbi.nlm.nih.gov/20219998/">redirect their own attention</a>.
</p>
<h3>Patients with a damaged STS/TPJ</h3>
<p>
If you ask a patient with <a href="https://en.wikipedia.org/wiki/Hemispatial_neglect">hemispatial neglect</a> to close their eyes and describe the layout of a public square they know, they will describe all the buildings on the right hand side, but totally ignore everything on the left hand side - they have lost awareness of the left side of space. These patients will still respond to images presented on the left side (they may flinch if something scary is presented), but they are not aware of the object in the image. This has shown to be the case even when processing high-level thoughts about things on the left - they are only missing awareness.
</p>
<p>
The attention schema theory predicts that there should <em>broadly</em> be two types of neglect, that in which the representation of attention is damaged (this should happen in the STS/TPJ) and that in which the control of attention is damaged (this should happen elsewhere in the brain). Results in this area were positive but ongoing at the time of the book being written (<a href="#footnote">I'd love to know more about progress here</a>).
</p>
<h1>Footnote</h1>
<p id="footnote">
Reading this book gave me a very strong "oh, that previously mysterious thing kinda makes sense now" feeling. Writing it up was a way for me to try and absorb its arguments but I don't feel I have the time to really delve any deeper into contemporary neuroscience. If anyone reading this is (or knows) someone in this area of neuroscience/psychology/philosophy/AI, I'd love to know (email me!): Is this theory widely read at all? Are there similar competiting theories? Did it make any waves in the field? or is it considered fringe nonsense? As a layman, where is the best place to read about the state of the art in the field? Are people using these ideas in their attempts at building strong AI?
</p>
<p>
Graziano has a <a href="https://www.wwnorton.co.uk/books/9780393652611-rethinking-consciousness">new(ish) book</a> out on the same topic but aimed more at the layman. It would be super cool if this were to become the new pop science bestseller a là A Brief History of Time or Sapiens, buy buy buy!
</p>
</body>
</description>
</item>
<item>
<title>What if we'd had better <code>html</code>-in-<code>js</code> syntax all along?</title>
<pubDate>Tue, 12 May 2020 08:09:22 +0100</pubDate>
<link>https://leontrolski.github.io/dom-syntax.html</link>
<guid>https://leontrolski.github.io/dom-syntax.html</guid>
<description>
<h1>What if we'd had better <code>html</code>-in-<code>js</code> syntax all along?</h1>
<p>
I have a theory that a grave mistake was made in 1995 - the decision not to have a neat, succinct and declarative way of representing html elements in javascript. <em>[On reading the <a href="https://news.ycombinator.com/item?id=23142300">hacker news comments</a> this is pretty historically inaccurate, however, I think this article still stands as "look just how close javascript object notation is to a reasonable way of representing html".]</em>
</p>
<p>
In this post, I'm going to describe the current state of affairs, show a couple of very small additions to javascript's syntax that might've made all the difference, then talk about the repercussions.
</p>
<h2>What options do we have now?</h2>
<p>
We have some APIs available to us, including all the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element">Element</a> properties/methods:
</p>
<pre><code>document.createElement("div")
document.createTextNode("Hi there and greetings!")
el.classList
el.innerHTML
el.removeAttribute()
...</code></pre>
<p>
We can use string templates and functions to try be more declarative:
</p>
<pre><code>const myLi = name => `&lt;li>My name is &lt;em>${name}&lt;/em>&lt;/li>`
const myUl = names => `&lt;ul class="my-ul">${names.map(myLi).join('')}&lt;/ul>`</code></pre>
<p>
This is rubbish for obvious reasons - composing the strings is bug-prone, no typing/linting etc etc. We then have to turn them into elements with the <a href="https://stackoverflow.com/questions/494143">less than elegant</a>:
</p>
<pre><code>function createElementFromHTML(htmlString) {
var div = document.createElement('div');
div.innerHTML = htmlString.trim();
return div.firstChild;
}</code></pre>
<p>
Mmm...
</p>
<p>
We can use the new <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template">template</a> elements, from the MDN docs:
</p>
<pre><code>&lt;template id="productrow">
&lt;tr>
&lt;td class="record">&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;/template></code></pre>
<pre><code>const template = document.querySelector('#productrow')
const clone = template.content.cloneNode(true)
const td = clone.querySelectorAll("td")
td[0].textContent = "1235646565"
tbody.appendChild(clone)</code></pre>
<p>
I don't know about you, but that felt pretty yucky.
</p>
<p>
Of course, there are also various libraries, ranging from string templating ones (like <a href="https://handlebarsjs.com/">handlebars</a>) through to compile-away ones like <code>.jsx</code>.
</p>
<h2>Representing html nodes now</h2>
<p>
In contemporary typescript, a node of html can map to and from the type:
</p>
<pre><code>type HtmlNode = {
tag: string,
attributes: {[key: string]: string | Function},
children: (HtmlNode | string)[]
}</code></pre>
<p>
A node like:
</p>
<pre><code class="language-html">&lt;button id="baz" class="foo bar" data="1" onclick=f>
Hello
&lt;em>there&lt;/em>
&lt;/button></code></pre>
<p>
Would map to and from:
</p>
<pre><code>{
tag: "button",
attributes: {
"id": "baz",
"class": "foo bar",
"data": "1",
"onclick": f,
},
children: [
"Hello",
{
tag: "em",
attributes: {},
children: ["there"],
}
]
}</code></pre>
<p>
Notice the <code>js</code> representation is a bit verbose.
</p>
<p>
The most terse html description language I've worked with was <a href="http://jade-lang.com/">jade</a>, here's an example:
</p>
<pre><code class="language-pug">html(lang="en")
head
title= pageTitle
body
h1.some-class Jade - node template engine
#container
- if (youAreUsingJade)
You are amazing</code></pre>
<p>
This seems nice, the main problem is we are a bit confined in our programming constructs, for example, how would we make the <code>&lt;h1></code> have the additional class <code>hidden</code> on some condition? Jade gets around this by having 3 different ways of specifying classes. Rather than come up with a special templating language, let's just slightly extend vanilla <code>js</code> syntax.
</p>
<h2>Extending the object syntax</h2>
<p>
Right, let's have a go with the example from above. I'm not going to put too much weight on the correctness of this as I'm not suggesting we change all our code, only a "what might've been".
</p>
<pre><code class="language-html">&lt;button id="baz" class="foo bar" data="1" onclick=f>
Hello
&lt;em>there&lt;/em>
&lt;/button></code></pre>
<p>
Would instead be:
</p>
<pre><code class="language-none">button{id: 'baz' class: ['foo' 'bar'] data: '1' onclick: f
'Hello '
em{'there'}
}</code></pre>
<p>
Or formatted longhand:
</p>
<pre><code class="language-none">button{
id: 'baz'
class: ['foo' 'bar']
data: '1'
onclick: f
'Hello '
em{'there'}
}</code></pre>
<p>
So, a checklist of things to allow existing <code>js</code> object syntax to represent html nodes in a reasonably succinct way:
</p>
<ul>
<li>As well as <code>key: value</code> pairs, objects allow trailing <code>value</code>s. I guess these would be accessible with <code>foo.~</code> or some special construct.</li>
<li>They get an optional tag (in this case <code>button</code>). If you've done much JSON deserialisation, tags seem like a good idea (they can help you map data to types), take a look at how they're used (with custom namespacing) in <a href="https://github.com/edn-format/edn#tagged-elements">edn</a> - nice.</li>
<li>To get things nice and small, I've dropped the requirement for commas.</li>
</ul>
<p>
That's it. Our javascript would use this as the main data type (everything else would remain the same). Probably, we'd start (re)writing all our XML-ish html to also use this syntax.
</p>
<h3>Toy examples</h3>
<p>
In the browser:
</p>
<pre><code>const someUl = document.getElementById('some-ul')
const myLi = name => li{'My name is ' em{name}}
someUl.~.push(myLi('Tommy'))</code></pre>
<p>
An express route:
</p>
<pre><code>const names = ["Barry", "Lynette"]
const myLi = name => li{'My name is ' em{name}}
const myUl = names => ul{class: ['my-ul'] ...names.map(myLi)}
app.get('/', (req, res) => res.send(myUl().asHTMLStr())</code></pre>
<p>
Instead of this page's html:
</p>
<pre><code>body{class: "language-javascript"
a{href: "index.html" img{style: "height:2em" src: "pic.png"} "⇦"}
h1{"What if we'd had better " code{"html"} "-in-" code{"js"} syntax all along?"}
p{"I have a theory that a grave mistake was made in 1995 ..."}
...</code></pre>
<h2>What if we'd always had something like this in <code>js</code>?</h2>
<ul>
<li>
There would be no distinction between <code>JSON</code> and <code>html</code>. <em>(What would have been the downstream consequences to API design?)</em>
</li>
<li>
It would be constantly staring us in the face that our html is <em>just structured data</em>.
</li>
<li>
We wouldn't have had the years of weird logic-in-html stuff like in <a href="https://knockoutjs.com/">knockout</a> (remember that one?)
<pre><code>&lt;button data-bind="enable: myItems().length &lt; 5">Add&lt;/button></code></pre>
<p>
Or <a href="https://vuejs.org/">Vue</a> (remember that one?)
</p>
<pre><code>&lt;span v-bind:title="message"></code></pre>
</li>
<li>
We might've had a React-a-like in 1996, and it would have just been "duh, obviously we'll just do it like <a href="https://en.wikipedia.org/wiki/Curses_(programming_library)">curses</a>".
</li>
<li>
We would have had a composable libraries for date selectors, modals, etc. without having to use said big-hairy-library.
</li>
<li>
No one would be using handlebars/Jinja/twig string munging libraries to make html (in the node world at least).
</li>
<li>
The html <code>&lt;form></code> &lt;-> <code>json</code> impedence mismatch (try describing nested data as a form) would probably have been properly sorted by now.
</li>
<li>
Everyones' early noughties plans for <em>everything</em> to be <code>XML</code> might've been more successful. <em>(Would that have been good?)</em>
</li>
</ul>
<h2>What about <code>.jsx</code> though?</h2>
<p>
I think it's a fine enough solution, but the fact that it's a different syntax from your standard <code>js</code> objects encourages people to consider the VDOM objects as "not normal data", but they <em>are</em>. <a href="http://www.eecg.toronto.edu/~jzhu/csc326/readings/iverson.pdf">Notation as a tool of thought</a> innit.
</p>
<h2>Other thoughts</h2>
<p>
There's some great links relating to <code>XML</code> &lt;-> <code>Scheme</code> equivalence/syntax <a href="https://news.ycombinator.com/item?id=9549841">here</a>.
</p>
<p>
Our alternative history having never happened, I <a href="33-line-react-thoughts.html#ezzthetic">prefer</a> the boring hyperscript style, everything is "just code".
</p>
</body>
</description>
</item>
<item>
<title>How do pandas DataFrames work? (kinda)</title>
<pubDate>Mon, 27 Apr 2020 22:56:54 +0100</pubDate>
<link>https://leontrolski.github.io/fake-data-frame.html</link>
<guid>https://leontrolski.github.io/fake-data-frame.html</guid>
<description>
<h1>How do pandas DataFrames work? (kinda)</h1>
<p>
When you're used to plain ol' <code>dict</code>s, <code>int</code>s, <code>list</code>s etc, <code>pandas.DataFrame</code>s exhibit some weirdo behaviour, particulary concerning assignment and operators. This page is a short walk-through of how some of these things happen (and a quick intro to Python's magic methods), you can see the outcome <a href="https://github.com/leontrolski/fake-data-frame">here</a>.
</p>
<p>
<em>Disclaimer:</em> the things presented here are not <em>entirely</em> as the <code>pandas</code> <code>DataFrame</code>s work, they are more intended as a guide to how they do.
</p>
<p>
The below examples use python type hints to help keep things a bit clearer, at the <a href="https://github.com/leontrolski/fake-data-frame/blob/master/fake_data_frame.py">top</a>, we have:
</p>
<pre><code>from typing import Any, Dict, List</code></pre>
<p>
<code>DataFrame</code>s are a collection of <code>Series</code> (AKA columns), let's start with a really dumb <code>FakeSeries</code>.
</p>
<pre><code>class FakeSeries:
def __init__(self, name: str, data: Dict[int, Any]):
self.name = name
self.data = data
def __repr__(self) -> str:
return f'&lt;FakeSeries: {self.name} {self.data}>'</code></pre>
<pre><code>>>> my_series = FakeSeries("some_column_name", {0: 5, 1: 7, 2: 9})
&lt;FakeSeries: some_column_name {0: 5, 1: 7, 2: 9}></code></pre>
<ul>
<li>Note how the <code>__repr__</code> method is used by <code>print()</code></li>
<li>There a list of all the magic methods you can override on a class <a href="https://docs.python.org/3/reference/datamodel.html#basic-customization">here</a></li>
<li>Note how we are storing the series as a map of indices (0, 1, 2) to values (5, 7, 9)</li>
</ul>
<p>
Now we will define our <code>FakeDataFrame</code>, it similarly has a useful <code>__init__</code> and <code>__repr__</code> (although this is only fully fleshed out in the <a href="https://github.com/leontrolski/fake-data-frame/blob/master/fake_data_frame.py">original</a>). On initialisation, it sets <code>self.series_map</code> which is a map of series names to series.
</p>
<pre><code>class FakeDataFrame:
def __init__(self, d: Dict[str, List[Any]]):
self.series_map = {
k: FakeSeries(k, {i: v for i, v in enumerate(l)})
for k, l in d.items()
}
self.length = len(list(d.values())[0])
def __repr__(self):
width = 5
...
return '\n'.join((headers, divider) + rows) + '\n'</code></pre>
<p>
Already, we can see the beginnings of a <code>pandas</code>-like <code>DataFrame</code> interface.
</p>
<pre><code>>>> df = FakeDataFrame({
'a': [4, 5, 6],
'b': [7, 8, 9],
})
a | b
-------------
4 | 7
5 | 8
6 | 9</code></pre>
<p>
Now the clever stuff begins, lets add two methods to <code>FakeDataFrame</code> so that we can retreive and set its <code>Series</code>.
</p>
<pre><code> # handle []
def __getitem__(self, key: str) -> FakeSeries:
return self.series_map[key]
# handle [] =
def __setitem__(self, key: str, value: FakeSeries) -> None:
if key not in self.series_map:
self.series_map[key] = FakeSeries(key, {})
for i, v in value.data.items():
self[key].data[i] = v</code></pre>