-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathluatalk.slide
374 lines (216 loc) · 9.95 KB
/
luatalk.slide
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
# Learn You Some Lua For Great Good!
2022-03-01
chrsm
$WORK presentation
## WTF is Lua?
- dynamically typed scripting language, originally developed at PUC Rio in '93
- designed to be embedded into software, but can be used standalone
It's used in..
- neovim!
- games: too many to list; big names: WoW, Roblox, Dota 2, etc
- popular software: Adobe products, Autodesk, Reason, etc
- popular dev tools/services: Redis, Nginx/OpenResty, HAProxy
- OS services/tools: FreeBSD's bootloader, OpenWRT, etc
- malware! You know you've made it when your language is embedded in malware!
## Why is Lua good?
- Small 30k LoC codebase (for std dist)
- VM model is easy to understand and work with; controlling the VM as a host or module writer is easy
- Simple syntax - very few keywords in standard lua
- Coroutines are first-class: no async/callback hell
- Fast enough for a dynamic language, incredibly fast with LuaJIT
## Personal Gripes
While Lua is one of my favorite languages and it's a tool I constantly reach
for, there are some issues I have with it:
- multiple versions: 5.1 and 5.2 are usually well supported, 5.3+ is oft-ignored
- different environments: Lua standard, LuaJIT, Luau
- package management is terrible (luarocks, etc)
- compiling extensions/modules on Windows is terrible
- dynamic types can be painful sometimes
- everyone has an opinion.jpeg
## Solutions
- versions: I target things 5.2 at max
- VMs: always luajit + _talked-about-later_
- package management: I use [peru](https://github.com/buildinspace/peru), a
"generic" package manager
- extensions: I don't have a good solution :-)
- loose/dynamic nature: tests & assertions for debug builds
- everyone else's opinions: I've picked my own best practices
## Now, let's actually _talk_ about Lua
We'll cover the following in subsequent sections:
- syntax overview
- conditionals
- variables/values
- scopes
- datatypes
- metatables
- coroutines
## Syntax
.play -edit examples/syntax/syntax.go /^-- luastart/,/^-- luaend/
## Conditionals, Loops
Lua supports a few different conditionals and looping mechanisms.
- `if`/`elseif`
- `while`
- `repeat`/`until`
- `for`
## Conditionals Examples
.play -edit examples/cond/cond.go /^-- luastart/,/^-- luaend/
## for example
.play -edit examples/forl/forl.go /^-- luastart/,/^-- luaend/
## Variables and Values in Lua
Lua is dynamically typed: variables do not have types, values have types.
Variables are free for reassignment.
.play -edit examples/variables/example_lua_vars.go /^-- luastart/,/^-- luaend/
## Scoping
.play -edit examples/scopes/scopes.go /^-- luastart/,/^-- luaend/
*Note: Idiomatic Lua tends to not modify the global environment.*
## Types in Lua
Lua only has a few built-in types:
- `string`
- `number`
- `bool`
- `function`
- `table` (associative array)
- `userdata` (types provided by the host)
- `thread` (coroutines)
- `nil` (oh no!)
## Strings, Numbers, Bools
.play -edit examples/snb/snb.go /^-- luastart/,/^-- luaend/
## Functions
Functions are first-class values in Lua and can be assigned to variables or referenced by their name.
.play -edit examples/fn/fn.go /^-- luastart/,/^-- luaend/
## Tables
Tables are associative arrays, and the only real "structured type" in Lua.
.play -edit examples/tables/tables.go /^-- luastart/,/^-- luaend/
## Table Iteration
- numeric keys: ordered, `ipairs`; indexes start at 1, no crying
- arbitrary keys: unordered, `pairs`
.play -edit examples/tableit/tableit.go /^-- luastart/,/^-- luaend/
## Table Iteration: caveats I
There are a few issues with iterating over numerically indexed tables.
`ipairs` is implemented by `lbaselib.c` inside of `luaB_pairs`, which calls
`luaB_next`, which calls `lua_next`... which will stop iterating at the
**first sign of nil!**
.play -edit examples/tableit_ipairs/tableit_ipairs.go /^-- luastart/,/^-- luaend/
Maintaining original keys requires use of another table.
## Table Iteration: caveats II
`table.remove` can be used to remove an element, but shifts the rest of the table.
.play -edit examples/tableit_remove/tableit_remove.go /^-- luastart/,/^-- luaend/
In 5.2 or above, you can use metatables to implement custom `pairs`/`ipairs`
behavior.
You can _simulate_ this in 5.1 by patching `pairs`, `ipairs` or `next` at
runtime, but monkey-patching is bad.
## Table Sugar
Calling `var_table.funcname(...)` is fine, but what about referencing the table
itself from one of the functions?
.play -edit examples/table_fn/table_fn.go /^-- luastart/,/^-- luaend/
Using the colon (`:`) syntax is sugar for passing `t_thing.add(t_thing, 2)`.
## "Classes"
Classes or otherwise structured types don't natively exist in Lua. However, the `table`
type is flexible and used to implement them where desired.
.play -edit examples/classes/classes.go /^-- luastart/,/^-- luaend/
## Metatables
The way our "class" example works is through the Magic(TM) of Lua's `metatables`.
Metatables are tables that define behavior of another table in various operations.
We used `__index`, which effectively means "if you don't find key X here, check this other thing".
5.1 supports the following:
- mathy/bits: `add` (+), `sub` (-), `mul` (*), `div` (/), `mod` (%), `pow` (^), `unm` (-)
- type-y: `len` (#), `eq` (~= or ==), `lt`, `gt`
- utility: `concat` (a .. b), `call` (eg `Player()`)
- meta: `index`, `newindex` (x.key = v)
For example, a type implementing a vec2 could have special methods for math
operations to allow `moved = prev_position ~= cur_position`
## Metatable example
.play -edit examples/metatables/metatables.go /^-- luastart/,/^-- luaend/
## Coroutines!
As Go devs, I expect you have some experience with or understanding of coroutines.
Like Go, Lua's coroutines are cooperative, not preemptive. However,
Go differs in a significant way: you don't have any say in what the scheduler
is doing at any given time (outside of `runtime.Gosched`). Various runtime bits
and packages end up calling out to the scheduler (ex blocking syscalls via
`runtime.entersyscall`).
In the Lua world, coroutines are cooperative from both the consumer and
producer. While this may be unintuitive, it gives much more control to both
sides.
- `coroutine.create( ... )`
- `coroutine.yield( ... )`
- `coroutine.resume( ... )`
- `coroutine.status( ... )`
## Coroutines example
.play -edit examples/coro/coro.go /^-- luastart/,/^-- luaend/
## userdata
`userdata` is any data type provided outside of Lua. Generally, I prefer libs
that use generics/templates/what-have-you that reduce the amount of work needed
to get a type implemented, eg `sol2` for cpp.
.image assets/userdata_cpp.png _ 650
.caption cxx17 w/sol2; some code removed to make this small
But we don't write cpp at work...
##
_BLANK, NO SPOILERS_
## Time for FUN!
.image assets/luapgo.png
.caption logos shamelessly stolen from Wikipedia, (C) some people who made them
## Lua + Go!
There are quite a few wrappers over std Lua, as well as LuaJIT; I even once wrote a
teensy cgo wrapper for luajit: [x/luajit](https://github.com/chrsm/x/blob/master/luajit/luajit.go).
However, I don't like using cgo, so I often reach for [gopher-lua](https://github.com/yuin/gopher-lua).
It's a fully featured implementation of Lua 5.1 _in Go_.
.play -edit examples/golua/golua.go /^//START/,/^//END/
## Lua + Go = <3
All the code you're seeing as part of this talk is actually being executed by `gopher-lua`!
.image assets/gopherlua.png _ 750
.caption hiding some bits for size :-)
## Go -> Lua: userdata
`userdata` is anything provided to the VM that isn't standard in Lua.
For example: I once started working on a window manager for Windows called
[ghettowm](https://github.com/chrsm/ghettowm/). The window manager itself is
written in Go, and is scriptable with Lua. Native APIs are implemented (or linked)
in Go, and exposed to the interpreter.
.play -edit examples/userdatabare/userdatabare.go /^//START/,/^//END/
## Go -> Lua: functions/modules
Here's a snippet from a package
that's provided to the VM from the host language.
.code examples/luavmext/luavmext.go /^//START/,/^//END/
## Lua -> Go: anything
This might be obvious, but you can access data from Lua within Go (or any host).
Getting certain bits of data (locals) is only "useful" or possible when the VM
is actually running, but after-the-fact you can access globals.
.play -edit examples/luatogo/luatogo.go /^//START/,/^//END/
## A Better Way
There's a really good library called `gopher-luar` by layeh that uses `reflect`
to automatically map any Go value into easily-usable values inside of Lua.
.play -edit examples/userdata/userdata.go /^//START/,/^//END/
## ...
I removed several slides related to $WORK. Sorry!
Basically, these slides talked about how Lua could be used with our current
code or how I've used it previously.
## A Better Lua Experience(TM)
Now that you a bit about Lua and usecases, let's talk about how to improve the
experience: treat it like a runtime target.
While I still write pure Lua, I more often than not use a [moonscript](https://github.com/leafo/moonscript)
alternative called [yuescript](https://github.com/pigpigyyy/Yuescript). This
provides a nicer syntax as well as lots of nice builtins that transpile down
to regular Lua code.
- `class` keyword+implementation w/inheritance
- `switch`, `unless`, `with`, null-operators (`x?`), pipe operator (`|>`)
- true `!=` instead of `~=`
- nicer table declarations
- macros
- shorthand func declarations
- import/export
## Example of Yue
.code examples/script.yue
## Lua Compilation
.code examples/script.lua
## Macros in Yue
I really dig the macro system, and am able to declare at build time
whether I'm making a debug build or release, making macros like:
.code examples/macros.yue
## Lua Compilation
.code examples/macros.lua
## Wrapup!
- Lua is small but extensible
- Lua has very simple syntax
- There's a lot of software that uses Lua under the hood
- There are alternatives to the syntax that compile to reasonable code
- Using Lua as an embedded language allows for anyone to use flexible tooling in a sandboxed environment
## Questions?