- PVSM.RU - https://www.pvsm.ru -
Lua [1] — это, пожалуй, мой любимый "маленький язык", с низкой когнитивной нагрузкой и простотой в изучении и использовании. Он встроен во многое ПО, такое как Redis [2], NGINX через OpenResty [3] и Wireshark [4] (прим. перевод.: и многие другие [5]). Он также используется в качестве скриптового языка в таких играх, как World of Warcraft [6] и Roblox через Luau [7] (прим. перевод.: и многих других [8]). Этот пост — краткое признание в любви языку с некоторыми примерами того, почему он мне так нравится.
В Lua относительно немного фич и относительно мало синтаксиса. К примеру, в языке всего 8 типов:
nil
boolean
number
string
userdata (для представления С-шных структур данных или блоков памяти в куче)
function
thread (для корутин)
table (ассоциативный массив и, по совместительству, единственная встроенная структура данных)
Нет нужды беспокоиться о float, int, usize. Нет нужды беспокоиться о различиях в массивах, словарях и структурах. Даже классы тут это просто таблицы (table) с указанными мета-таблицами (metatables, прим. перев.: думайте о прототипах, как в JavaScript). Такая простота во всём делает Lua лёгким в освоении и использовании, обеспечивая при этом достаточную мощь для выполнения большинства необходимых задач.
Давайте реализуем простой бинарный поиск по массиву на Lua:
-- однострочные комментарии начинаются с двух тире
function binary_search(array, value)
local low = 1
local high = #array -- # это оператор взятия длины
while low <= high do
-- доступ к библиотечным функциям через глобальную таблицу
local mid = math.floor((low + high) / 2)
local mid_value = array[mid]
if mid_value < value then
low = mid + 1
elseif mid_value > value then
high = mid - 1
else
return mid
end
end
return nil
end
res = binary_search({2, 4, 6, 8, 9}, 6)
print(res)
Всё это должно быть относительно знакомо, даже если вы никогда раньше не сталкивались с Lua. Единственное, что может показаться непривычным, это ключевое слово local, которое используется для объявления переменных (прим. перев.: и не только). По умолчанию все они глобальны, так что local используется для объявления локальной переменной относительно текущей области видимости.
Lua является великолепной целью для транспиляции, благодаря его простоте и лёгкости взаимодействия с C. Поэтому, Lua - отличный выбор для предметно-ориентированных языков (DSL-ей), таких как Terra [9], MoonScript [10] и Fennel [11].
В качестве краткого примера, вот тот же бинарный поиск написанный в MoonScript и Fennel соответственно:
binary_search = (array, value) ->
low = 1
high = #array
while low <= high
mid = math.floor((low + high) / 2)
mid_value = array[mid]
if mid_value < value
low = mid + 1
else if mid_value > value
high = mid - 1
else
return mid
return nil
print binary_search {2, 4, 6, 8, 9}, 6
(fn binary-search [array value]
(var low 1)
(var high (length array))
(var ret nil)
(while (<= low high)
(local mid (math.floor (/ (+ low high) 2)))
(local mid-value (. array mid))
(if (< mid-value value) (set low (+ mid 1))
(> mid-value value) (set high (- mid 1))
(do
(set ret mid)
(set low high)))) ; no early returns in Fennel
ret)
(local res (binary-search [2 4 6 8 9] 6))
(print res)
Но истинная сила языка заключается в том, что вы можете внедрить его практически куда угодно - Lua реализован как библиотека для программы-хоста, типа Redis. Традиционно, это была программа на C, но теперь существуют многие реализации виртуальной машины Lua в разных языках, таких как Javascript (с Fengari) [12] или Go (c GopherLua) [13]. Однако, одной из самых популярных реализаций является скриптовый язык Luau для игры Roblox [14].
Возможно, одно из моих любимых применений Lua — это HAProxy, возвращающий нас во времена Apache + mod_php скриптинга. Давайте настроим конфигурацию HAProxy, которая будет отвечать на запросы по определённому пути случайным предсказанием:
local function fortune(applet)
local responses = {
{
quote = "The only people who never fail are those who never try.",
author = "Ilka Chase"
},
{
quote = "The mind that is anxious about future events is miserable.",
author = "Seneca"
},
{
quote = "A leader is a dealer in hope.",
author = "Napoleon Bonaparte"
},
{
quote = "Do not wait to strike until the iron is hot; but make it hot by striking.",
author = "William B. Sprague"
},
{
quote = "You have power over your mind - not outside events. Realize this, and you will find strength.",
author = "Marcus Aurelius"
}
}
local response = responses[math.random(#responses)]
local resp = string.format([[
<html>
<body>
<p>%s<br> --%s</p>
</body>
</html>
]], response.quote, response.author)
applet:set_status(200)
applet:add_header("content-length", string.len(resp))
applet:add_header("content-type", "text/html")
applet:start_response()
applet:send(resp)
end
core.register_service("fortune", "http", fortune)
global
lua-load fortune.lua
defaults
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
frontend fe_main
bind :8080
mode http
http-request use-service lua.fortune if { path /fortune }
И затем запустим её:
$ haproxy -f haproxy.cfg
$ curl localhost:8080/fortune
<html>
<body>
<p>Do not wait to strike until the iron is hot; but make it hot by striking.<br> --William B. Sprague</p>
</body>
</html>
Зачем вообще так делать? Легко вообразить ситуацию, в которой нужна небольшая логика приложения поверх веб-сервера, но вы не хотите писать полноценной веб-приложение под эту задачу. Или же вы хотите расширить функционал существующих приложений, например, добавить небольшой запрос (endpoint) для проверки статуса (healthcheck) Redis-сервера:
-- это сторонний форк redis-lua с поддержкой TLS:
local redis = require("redis-tls")
local settings = {
host = "127.0.0.1",
port = 6379,
database = 14,
password = nil,
}
local utils = {
create_client = function(params)
local client = redis.connect(params.host, params.port, 1, false)
if params.password then
client:auth(params.password)
end
return client
end,
}
local function redis_health(applet)
-- pcall как try/catch, принимает функцию и аргументы,
-- и возвращает true/false и результат выполнения функции
local ok, client = pcall(utils.create_client, settings)
if not ok then
local string_resp = '{"ok":false}n'
applet:set_status(500)
applet:add_header("content-length", string.len(string_resp))
applet:add_header("content-type", "application/json")
applet:start_response()
applet:send(string_resp)
return
end
local resp = client:ping()
local string_resp = string.format('{"ok":%s}n', resp)
applet:set_status(200)
applet:add_header("content-length", string.len(string_resp))
applet:add_header("content-type", "application/json")
applet:start_response()
applet:send(string_resp)
end
core.register_service("redis_health", "http", redis_health)
global
lua-load redis.lua
frontend fe_main
bind :8080
mode http
http-request use-service lua.redis_health if { path /redis_health }
Redis-сервер выключен/лежит:
$ curl 127.0.0.1:8080/redis_health
{"ok":false}
Redis-сервер работает и доступен:
$ curl 127.0.0.1:8080/redis_health
{"ok":true}
Можно пойти дальше и использовать register_action и register_fetches (см. доки [15]) для перехвата информации о запросе, его изменения или добавления дополнительных возможностей аутентификации поверх ПО без встроенной системы аутентификации.
Оно не особо велико, но в нём ведётся множество отличных разработок, а многие библиотеки доступны через простой пакетный менеджер LuaRocks [16]. От библиотек для быстрого парсинга и кодировки JSON [17] до дополнений к стандартной библиотеке [18] (прим. перевод.: или даже добавления программируемых компьютеров в Minecraft [19]) - для каждого найдётся что-то своё.
Отдельного упоминания заслуживает Лиф Коркоран (Leaf Corcoran) [20], написавший Lapis [21] - фантастический небольшой веб-фреймворк для дистрибутива OpenResty, на котором работает сайт LuaRocks.
Прим. перевод.: В отличие от некоторых сообществ, в Lua нет ни мелких, ни больших скандалов или интриг (см. Rust 👀: ^1 [22], ^2 [23], ^3 [24])
Есть ли какой-то вывод? Lua очень хорош, вы можете освоить его за выходные [25] и начать использовать его для написания слоёв авторизации в HAProxy, аддонов для World of Warcraft, игр в рамках Roblox, скриптов для вашего оконного менеджера [26], работы с сетями [27] или просто небольших библиотек, которые делают вас чуточку счастливее.
Прим. перевод.: и не забывайте про игровые движки - Love2D [28], Defold [29] и интеграция с Raylib [30].
От себя я бы рекомендовал посмотреть на Fennel [31], упомянутый выше в переводе. Это LISP, который транспилируется в Lua — вы получаете скорость, простоту и доступность Lua c гибкостью LISP-синтаксиса и макросов.
А для тех, кто только начинает свой путь в программировании, у меня есть промокод HABR23. Где применить не скажу, а то реклама :)
Кстати, я веду Телеграм-канал для заинтересованных в обучении и осмыслении всего в IT, присоединяйтесь, если ещё не! [32]
Автор: Novus Nota
Источник [33]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/razrabotka/385066
Ссылки в тексте:
[1] Lua: https://www.lua.org/about.html
[2] Redis: https://redis.io/docs/manual/programmability/eval-intro/
[3] NGINX через OpenResty: https://openresty.org/en/
[4] Wireshark: https://wiki.wireshark.org/Lua
[5] и многие другие: https://en.wikipedia.org/wiki/List_of_applications_using_Lua
[6] World of Warcraft: https://wow.gamepedia.com/Lua
[7] Roblox через Luau: https://luau-lang.org/
[8] и многих других: https://en.wikipedia.org/wiki/Category:Lua_(programming_language)-scripted_video_games
[9] Terra: http://terralang.org/
[10] MoonScript: https://moonscript.org/
[11] Fennel: https://fennel-lang.org/
[12] Javascript (с Fengari): https://fengari.io/
[13] Go (c GopherLua): https://github.com/yuin/gopher-lua
[14] Luau для игры Roblox: https://create.roblox.com/docs/scripting/luau
[15] доки: https://manpages.ubuntu.com/manpages/impish/man1/haproxy-lua.1.html
[16] LuaRocks: https://luarocks.org/
[17] быстрого парсинга и кодировки JSON: https://luarocks.org/modules/openresty/lua-cjson
[18] дополнений к стандартной библиотеке: https://luarocks.org/modules/tieske/penlight
[19] программируемых компьютеров в Minecraft: https://tweaked.cc/
[20] Лиф Коркоран (Leaf Corcoran): https://leafo.net/
[21] Lapis: https://leafo.net/lapis/
[22] ^1: https://thephd.dev/i-am-no-longer-speaking-at-rustconf-2023
[23] ^2: https://www.jntrnr.com/why-i-left-rust/
[24] ^3: https://gist.github.com/fasterthanlime/42da9378768aebef662dd26dddf04849
[25] освоить его за выходные: https://tylerneylon.com/a/learn-lua/
[26] скриптов для вашего оконного менеджера: https://awesomewm.org/
[27] работы с сетями: https://nmap.org/
[28] Love2D: https://love2d.org/
[29] Defold: https://defold.com/
[30] интеграция с Raylib: https://github.com/tsnake41/raylib-lua
[31] Fennel: https://fennel-lang.org
[32] присоединяйтесь, если ещё не!: https://t.me/novusnota
[33] Источник: https://habr.com/ru/articles/738414/?utm_source=habrahabr&utm_medium=rss&utm_campaign=738414
Нажмите здесь для печати.