Open13
Teal言語 学習ログ

予備

予備2

配列
-- `{型名}` で配列の型定義
local names: {string} = {"John", "Paul", "George", "Ringo"}
local lengths = {}
for _, n in ipairs(names) do
table.insert(lengths, #n) -- this makes the lengths table a {number}
end
local sizes: {number} = {34, 36, 38}
sizes[#sizes + 1] = true as number -- this does not perform a conversion! it will just stop tl from complaining!
local sum = 0
for i = 1, #sizes do
sum = sum + sizes[i] -- will crash at runtime!
end
print(sum)

タプル
-- `{string, integer}` 2要素のタプル型を表す.
local p1 = { "Anna", 15 }
local p2 = { "Bob", 37 }
local p3 = { "Chris", 65 }
local age_of_p1: number = p1[2] -- no type errors here
-- error! index 3 out of range for tuple {1: string, 2: integer}
--local nonsense = p1[3]
local my_number = math.random(1, 2)
print(my_number)
local x = p1[my_number] -- => x は `string | number` の union型
if x is string then
print("Name is " .. x .. "!")
else
print("Age is " .. x)
end
-- error! expected maximum length of 2, got 3
--local p4: {string, integer} = { "Delilah", 32, false }
-- 配列とタプルの型注釈
-- 全ての要素が同じ型のとき、配列として推論される
-- 複数の型が混在するユニオン型の配列が必要なとき、明示的な型注釈が必要
local array_of_union: {string | number} = {1, 2, "hello", "hi"}
-- 同じ型のタプルが必要なとき、明示的な型注釈が必要
local tuple_of_nums: {number, number} = {1, 2}

マップ型
-- `{型名:型名}` でマップ型
local populations: {string:number}
--local complicated: {Object:{string:{Point}}} = {}
local modes = { -- {boolean:number} に推論される
[false] = 127,
[true] = 230,
}
-- 文字列をキーとするマップを使用するときは型注釈を明示したほうが良い.
-- レコード型を混同される恐れがあるため.
local is_vowel: {string:boolean} = {
a = true,
e = true,
i = true,
o = true,
u = true,
}

レコード型
-- レコードはオブジェクトや構造体を表現するのに便利.
-- レコード型の変数を宣言するには、まずレコード型の型定義が必要.
-- global type と宣言するとグローバルで有効
local type Point = record
x: number
y: number
end
-- 型定義には糖衣構文がある
local record Vector
x: number
y: number
end
-- 同じテーブル構造でも型が違うとエラー
local v1: Vector = { x = 100, y = 100 }
local p2: Point = v1 -- Error!
-- `as` キーワードでキャスト可能
local p2 = v1 as Point -- Teal goes "ok, I'll trust you..."
-- レコード関数の定義が可能
function Point.new(x: number, y: number): Point
local self: Point = setmetatable({}, { __index = Point })
self.x = x or 0
self.y = y or 0
return self
end
-- Lua の `:` でのテーブル関数定義も受け継いでいる
function Point:move(dx: number, dy: number)
self.x = self.x + dx
self.y = self.y + dy
end
-- レコード型に関数メンバを定義すれば、動的なコールバック関数などを保持可能
local record Obj
location: Point
draw: function(Obj) -- ←これ
end
-- 配列I/F を宣言すると、配列データも保持できる
-- フィールド名でアクセスする方法と、配列インデックスの療法で要素にアクセスできる
local record Node is {Node}
weight: number
name: string
end
-- レコードは入れ子定義可能
-- モジュールをレコードとしてエクスポートする際に便利
local record http
record Response
status_code: number
end
get: function(string): Response
end
return http
-- こう使う
local http = require("http")
local x: http.Response = http.get("http://example.com")
print(x.status_code)

インターフェイス
-- インターフェイスとは、本質的には抽象レコード.
-- レコードは Luaテーブルとしても型としても利用可能.
local interface MyAbstractInterface
a: string
x: integer
y: integer
my_func: function(self, integer) -- `self` が型定義に含まれている点に注目
another_func: function(self, integer, self) -- `self` が...に注目
end
-- インターフェイスは値を保持できない
MyAbstractInterface.a = "this doesn't work" -- error!
-- インスタンスだったら値を保持できる
local obj: MyAbstractInterface = { x = 10, y = 20 } -- this works
-- インターフェイスは値を保持できない
function MyAbstractInterface:my_func(n: integer) -- error!
end
-- インスタンスだったら値を保持できる
obj.my_func = function(self: MyAbstractInterface, n: integer) -- this works
end
-- レコード型は `is` 演算子でインターフェイスを継承できる
local record MyRecord is MyAbstractInterface
b: string
end
local r: MyRecord = {}
r.b = "this works"
r.a = "this works too because 'a' comes from MyAbstractInterface" -- 継承したメンバ
-- the following function complies to the type declared for `another_func`
-- in MyAbstractInterface, because MyRecord is the `self` type in this context
-- 以下の関数は、`MyAbstractInterface` で宣言されている `another_func` の型に準拠する.
-- この文脈において `MyRecord` がこのコンテキストにおける `self` 型であるため.
function MyRecord:another_func(n: integer, another: MyRecord)
print(n + self.x, another.b)
end

ジェネリクス
-- `<型変数>` でジェネリクス
local function keys<K,V>(xs: {K:V}):{K}
local ks = {}
for k, v in pairs(xs) do
table.insert(ks, k)
end
return ks
end
local s = keys({ a = 1, b = 2 }) -- s is {string}
-- ジェネリクスレコード型
local type Tree = record<X>
{Tree<X>}
item: X
end
local t: Tree<number> = {
item = 1,
{ item = 2 },
{ item = 3, { item = 4 } },
}
-- 型変数はis演算子を使用してインターフェースによって制限可能
local function largest_shape<S is Shape>(shapes: {S}): S
local max = 0
local largest: S
for _, s in ipairs(shapes) do
if s.area >= max then
max = s.area
largest = s
end
end
return largest
end

Enum型
列挙型
-- enum型の変数や引数は、宣言されたリストに含まれる値のみを受け付る.
-- enum型は文字列へ変換は可能だが、その逆は不可.
-- 任意の文字列をキャストによってenum型に変換することは可能.
local type Direction = enum
"north"
"south"
"east"
"west"
end
-- 糖衣構文
local enum Direction
"north"
"south"
"east"
"west"
end

関数定義
-- 関数の型定義可能
local type Comparator = function<T>(T, T): boolean
-- `?` はオプション引数を表す
-- sort のためのカスタム比較関数 cmp? を受け取れる.
local function mysort<A>(arr: {A}, cmp?: Comparator<A>)
-- 関数定義を書く...
end
-- 多値を返すときの型定義
-- f は「オプション引数文字列を受取り、{number:number}のタプルとnumberを返す関数」を受け取る関数
f: function(function(? string): (number, number), number)
-- イテレータ関数を返す関数の例(from 'Programming in Lua')
local function allwords(): (function(): string)
local line = io.read()
local pos = 1
return function(): string
while line do
local s, e = line:find("%w+", pos)
if s then
pos = e + 1
return line:sub(s, e)
else
line = io.read()
pos = 1
end
end
return nil
end
end
for word in allwords() do
print(word)
end
-- 可変長引数を受け取る関数の型定義
-- 返り値も可変長
local function test(...: number): number...
print(...)
return ...
end
local a, b, c = test(1, 2, 3)
-- Lua の動的言語の関数を使う場合、返り値の型は `any...` となる.
-- このとき呼び出し側で `as` で具体的な型を指定する
local s = { 1234, "ola" }
local a, b = table.unpack(s) as (number, string)
print(a + 1) -- `a` has type number
print(b:upper()) -- `b` has type string

Union型
-- Union型の変数が定義可能
-- 型制限のあるジェネリクス変数のようなもの
local a: string | number | MyRecord
local b: {boolean} | MyEnum
local c: number | {string:number}
-- Union型の変数は使用時に `is` での型判別が必要
local a: string | number | MyRecord
if a is string then
print("Hello, " .. a)
elseif a is number then
print(a + 10)
else
print(a.my_record_field)
end
-- `is` は式なので、こう使える
local a: string | number
local x: number = a is number and a + 1 or 0

any型
any
は、動的型付けのLua変数と同様に、あらゆる値を受け入れる。ただし、Tealではこの値に関する一切の情報を把握していないため、等値比較やnilとの比較以外にできることはほとんどなく、as
演算子を使用して他の型に明示的にキャストする程度の操作しか行えない。
一部のLuaライブラリでは、Tealでは容易に表現できない複雑な動的型をする。このような場合、any
型を使用し、明示的な型変換を行うのが最後の手段となる。