📜
Arweave AOにおけるLuaの基本記法まとめ
Arweave AO(Actor Oriented)では、プロセスの記述にLuaプログラミング言語を使用します。
この記事では、AOでLuaを書く際の基本的な記法と、AO特有のパターンについて解説します。
変数とデータ型
基本的な変数宣言
-- グローバル変数
MyVariable = "Hello World"
Counter = 42
IsActive = true
-- ローカル変数
local localVar = "ローカル変数"
local number = 123
データ型
Luaは動的型付け言語で、以下のデータ型をサポートしています。
-- 数値型
local integer = 42
local float = 3.14
local scientific = 1.23e-4
-- 文字列型
local str1 = "ダブルクォート"
local str2 = 'シングルクォート'
local multiline = [[
複数行の
文字列も可能
]]
-- 真偽値
local isTrue = true
local isFalse = false
-- nil(未定義値)
local nothing = nil
テーブル(配列・辞書)
Luaのテーブルは配列と辞書の両方の機能を持つ柔軟なデータ構造です。
配列
-- 配列(インデックスは1から開始)
local fruits = {"apple", "banana", "orange"}
print(fruits[1]) -- "apple"
print(#fruits) -- 3(配列の長さ)
辞書(連想配列)
-- 辞書
local person = {
name = "太郎",
age = 25,
active = true
}
-- アクセス方法
print(person.name) -- "太郎"
print(person["age"]) -- 25
複合テーブル
-- 配列と辞書の混合
local mixed = {
"first", -- [1]
"second", -- [2]
name = "Mixed", -- ["name"]
count = 100 -- ["count"]
}
-- ネストしたテーブル
local config = {
database = {
host = "localhost",
port = 5432,
credentials = {
username = "admin",
password = "secret"
}
}
}
関数
基本的な関数定義
-- 通常の関数
function greet(name)
return "Hello, " .. name
end
-- ローカル関数
local function add(a, b)
return a + b
end
-- 無名関数
local multiply = function(x, y)
return x * y
end
高度な関数機能
-- 複数戻り値
function getCoordinates()
return 10, 20
end
local x, y = getCoordinates()
-- 可変長引数
function sum(...)
local args = {...}
local total = 0
for i = 1, #args do
total = total + args[i]
end
return total
end
-- 使用例
local result = sum(1, 2, 3, 4, 5) -- 15
制御構文
条件分岐
local score = 85
if score >= 90 then
print("優秀")
elseif score >= 70 then
print("良好")
else
print("要改善")
end
ループ処理
-- 数値ループ
for i = 1, 10 do
print("Count: " .. i)
end
-- 逆順ループ
for i = 10, 1, -1 do
print(i)
end
-- 配列の反復
local items = {"a", "b", "c"}
for i = 1, #items do
print(i, items[i])
end
-- キー・値ペアの反復(全要素)
local data = {name = "John", age = 30, city = "Tokyo"}
for key, value in pairs(data) do
print(key, value)
end
-- 配列専用の反復(連続インデックスのみ)
for index, value in ipairs(items) do
print(index, value)
end
-- while文
local count = 0
while count < 5 do
print("Count: " .. count)
count = count + 1
end
AO特有の記法
AOでは、通常のLuaに加えていくつかの特別な規則があります。
永続化される変数
-- 大文字で始まるグローバル変数は自動的に永続化される
PersistentData = PersistentData or {}
TotalSupply = 1000000
Balances = Balances or {}
-- 条件初期化パターン(プロセス再起動対応)
if not Owner then
Owner = ao.env.Process.Owner
end
AO組み込み関数
-- メッセージ送信
ao.send({
Target = "プロセスID",
Tags = {
Action = "Transfer",
Amount = "100"
},
Data = "追加データ"
})
-- 環境変数へのアクセス
local processId = ao.id
local owner = ao.env.Process.Owner
メッセージハンドラー
AOの特徴であるメッセージ処理システムです。
基本的なハンドラー
Handlers.add(
"balance_check", -- ハンドラー名
Handlers.utils.hasMatchingTag("Action", "Balance"), -- 条件
function(msg) -- 処理関数
local account = msg.Tags.Account or msg.From
local balance = Balances[account] or 0
ao.send({
Target = msg.From,
Tags = {
Action = "Balance-Response",
Balance = tostring(balance)
}
})
end
)
複雑な条件のハンドラー
-- カスタム条件関数
local function isValidTransfer(msg)
return msg.Tags.Action == "Transfer" and
msg.Tags.To and
msg.Tags.Amount and
tonumber(msg.Tags.Amount) > 0
end
Handlers.add(
"transfer",
isValidTransfer,
function(msg)
local from = msg.From
local to = msg.Tags.To
local amount = tonumber(msg.Tags.Amount)
-- 転送処理
if Balances[from] and Balances[from] >= amount then
Balances[from] = Balances[from] - amount
Balances[to] = (Balances[to] or 0) + amount
ao.send({
Target = msg.From,
Tags = { Action = "Transfer-Success" }
})
else
ao.send({
Target = msg.From,
Tags = { Action = "Transfer-Error", Error = "Insufficient balance" }
})
end
end
)
よく使うパターン
安全な型変換
-- 安全な数値変換
local function safeToNumber(value, default)
local num = tonumber(value)
return num and num or (default or 0)
end
-- 安全な文字列変換
local function safeToString(value)
return tostring(value or "")
end
バリデーション関数
local function validateTransfer(from, to, amount)
-- 送信者・受信者チェック
if not from or not to or from == to then
return false, "Invalid sender or recipient"
end
-- 金額チェック
local numAmount = tonumber(amount)
if not numAmount or numAmount <= 0 then
return false, "Invalid amount"
end
-- 残高チェック
if not Balances[from] or Balances[from] < numAmount then
return false, "Insufficient balance"
end
return true, numAmount
end
-- 使用例
local isValid, amountOrError = validateTransfer(msg.From, msg.Tags.To, msg.Tags.Amount)
if not isValid then
ao.send({
Target = msg.From,
Tags = { Action = "Error", Message = amountOrError }
})
return
end
ユーティリティ関数
-- テーブルのコピー
local function deepCopy(original)
local copy = {}
for key, value in pairs(original) do
if type(value) == "table" then
copy[key] = deepCopy(value)
else
copy[key] = value
end
end
return copy
end
-- テーブルが空かチェック
local function isEmpty(tbl)
return next(tbl) == nil
end
-- 配列の要素を検索
local function findInArray(array, value)
for i, v in ipairs(array) do
if v == value then
return i
end
end
return nil
end
エラーハンドリング
pcall(Protected Call)
-- 安全な関数実行
local success, result = pcall(function()
return someRiskyFunction()
end)
if success then
print("成功: " .. tostring(result))
else
print("エラー: " .. tostring(result))
end
assert
-- 条件チェック
local function divide(a, b)
assert(type(a) == "number", "第1引数は数値である必要があります")
assert(type(b) == "number", "第2引数は数値である必要があります")
assert(b ~= 0, "ゼロで割ることはできません")
return a / b
end
カスタムエラーハンドリング
local function safeExecute(func, errorHandler)
local success, result = pcall(func)
if success then
return result
else
if errorHandler then
return errorHandler(result)
else
print("エラーが発生しました: " .. tostring(result))
return nil
end
end
end
-- 使用例
local result = safeExecute(
function() return calculateSomething() end,
function(error) return "計算に失敗しました: " .. error end
)
実践的な例:簡単なトークンコントラクト
以下は、これまでの知識を組み合わせた実践的な例です。
-- トークンの基本情報
TokenName = "MyToken"
TokenSymbol = "MTK"
TotalSupply = 1000000
Balances = Balances or {}
-- 初期化
if not Owner then
Owner = ao.env.Process.Owner
Balances[Owner] = TotalSupply
end
-- ユーティリティ関数
local function hasBalance(account, amount)
return Balances[account] and Balances[account] >= amount
end
-- 残高照会ハンドラー
Handlers.add(
"balance",
Handlers.utils.hasMatchingTag("Action", "Balance"),
function(msg)
local account = msg.Tags.Account or msg.From
local balance = Balances[account] or 0
ao.send({
Target = msg.From,
Tags = {
Action = "Balance-Response",
Account = account,
Balance = tostring(balance)
}
})
end
)
-- 転送ハンドラー
Handlers.add(
"transfer",
Handlers.utils.hasMatchingTag("Action", "Transfer"),
function(msg)
local from = msg.From
local to = msg.Tags.To
local amount = tonumber(msg.Tags.Amount)
-- バリデーション
if not to or not amount or amount <= 0 then
ao.send({
Target = from,
Tags = { Action = "Transfer-Error", Error = "Invalid parameters" }
})
return
end
if not hasBalance(from, amount) then
ao.send({
Target = from,
Tags = { Action = "Transfer-Error", Error = "Insufficient balance" }
})
return
end
-- 転送実行
Balances[from] = Balances[from] - amount
Balances[to] = (Balances[to] or 0) + amount
ao.send({
Target = from,
Tags = {
Action = "Transfer-Success",
From = from,
To = to,
Amount = tostring(amount)
}
})
end
)
まとめ
AOでのLuaプログラミングは、通常のLuaの知識に加えて以下の点を理解することが重要です。
- 永続化の概念: 大文字で始まるグローバル変数の自動永続化
- メッセージドリブンアーキテクチャ: Handlersを使った非同期処理
- 状態管理: プロセスの再起動に対応した条件初期化
- エラーハンドリング: 分散環境での堅牢なエラー処理
これらの概念を理解することで、AOプラットフォーム上で効率的で安全なプロセスを構築できるようになります。
AOの分散コンピューティング環境では、従来の同期的なプログラミングとは異なる非同期・メッセージベースの思考が求められますが、Luaのシンプルな記法がその複雑さを軽減してくれます。
Discussion