Рассмотрим синтаксис, обработку ошибок, взаимодействие с языком С и возможности реактивного программирования.
Предыдущая статья здесь.
Особенности синтаксиса
val identity = Matrix4 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
Позиционный способ инициализации полей хорошо подходит для матриц. Целочисленный литерал применяется также для вещественных типов чисел.
control.set_width width set_height control.measure_height arrange import num::vector_f32::Vector3 Vector4 vector_i32::Vector2i
Методы set_width, set_height и arrange вызываются на одной и той же привязке control.
let enabled_extension_names = ["VK_EXT_debug_report" "VK_EXT_debug_utils" "VK_KHR_surface" "VK_KHR_xcb_surface" "VK_KHR_xlib_surface"]
Запятые между элементами не нужны, если каждый находится на отдельной строке или строках. Указатель на массив будет передан в С функцию, поэтому все строковые литералы получат тип CString.
text |> lines |> filter { line -> line.starts_with "layout" } |> map { remove_comment _ }
Операторы |> удобно располагать в вертикальный ряд. Замыкания создаются с помощью фигурных скобок. Каждое использование подчёркивания возвращает следующий параметр.
let up_right = Vector3 left.as_f32 0 border_z let adjusted_size = size as f32 * 1.5 |> as_u32
Преобразование типа можно записать как вызов метода.
let thickness = (left = border.left right = border.right up = border.up down = border.down) let sizes = List<(u32, u32) width height>.new
Поля кортежа не имеют имён по умолчанию, обращаться к ним можно только по явно заданным.
Внутристрочное объявление типов
type Entry = path : String created : DateTime | File size : u32 | Directory entries : Slice<Entry> | Local | Remote url : String
Entry и Directory это типы-суммы, Local и Remote — структуры. Поля path и created доступны как для Entry, так и для каждого из подтипов.
type Entry @abstract = struct path : String created : DateTime is_directory : bool | File size : u32 | Directory entries : Slice<mut ref Entry> is_remote : bool | Remote url : String
Структуры File и Directory наследуют поля структуры Entry. Ссылка типа mut ref Entry указывает на File, Directory или Remote.
Классы типов
type Comparable = class def (>) (rhs : Self) : bool def (<) (rhs : Self) : bool def (>=) (rhs : Self) : bool def (<=) (rhs : Self) : bool type Compare @virtual = def compare (other : Self) : Ordering def (>) (rhs : Self) = let result = compare rhs result == Ordering::Greater ... is Comparable
Тип может быть объявлен принадлежащим к классу, если соответствует всем заданным для класса ограничениям.
Обработка ошибок
def try_read_file (path : String) : Result<String, String> = ... object i32 def try_parse (s : String) : Option<i32> = ...
Result<String, String> содержит либо прочитанный из файла текст в Ok<String>, либо Error<String> с сообщением об ошибке.
def try_add (lhs_path : String) (rhs : i32) = let lhs = let! text = try_read_file lhs_path let! number = i32.try_parse text number lhs + rhs |> Some
Привязки text и number имеют типы String и i32, учитывающие только случаи успешного выполнения функций try_read_file и try_parse.
Ниже показан аналогичный код без let!.
def try_add (lhs_path : String) (rhs : i32) = let lhs = let text = case try_read_file lhs_path of Result::Ok text -> text Result::Error _ -> return None let number = case i32.try_parse text of Some number -> number None -> return None number lhs + rhs |> Some
Полученное от try_read_file сообщение об ошибке можно передать выше по стеку вызовов, вернув Result<i32, String> из try_add вместо Option<i32>.
def try_add (lhs_path : String) (rhs : i32) = let lhs = let! text = try_read_file lhs_path let! number = i32.try_parse text |> to_result "cannot parse $text" number lhs + rhs |> Result::Ok
Option конвертируется в Result с помощью метода to_result.
Взаимодействие с языком С
Типы
type VkInstance = MutPtr type CString = Ptr<c_char>
Именованный кортеж с единственным элементом в С коде представлен как тип элемента, поэтому в объявлениях внешних функций VkInstance и CString можно использовать вместо void* и const char*.
typealias DebugUtilsMessengerCallbackExt = fn (message_severity : DebugUtilsMessageSeverityFlagsExt) * (message_types : DebugUtilsMessageTypeFlagsExt) * (callback_data : ptr DebugUtilsMessengerCallbackDataExt) * (user_data : MutPtr) -> u32 typealias CharFun = fn GlfwWindow * u32 -> Unit
Псевдонимы для типов указателей на функцию.
type SampleCountFlags = flags | 1 | 2 | 4 | 8 | 16 | 32 | 64
Символы флагов могут быть числами или начинаться с цифры.
type InstanceCreateInfo = struct type : StructureType next : Ptr flags : InstanceCreateFlags application_info : ptr ApplicationInfo enabled_layer_count : u32 enabled_layer_names : ptr CString enabled_extension_count : u32 enabled_extension_names : ptr CString
Структуры переводятся на С дословно.
Значения
object f64 val min "DBL_MIN" : f64 val max "DBL_MAX" : f64 val epsilon "DBL_EPSILON" : f64
Внешние константы DBL_MIN, DBL_MAX и DBL_EPSILON представлены полями объекта f64.
Функции
module vk def create_semaphore "vkCreateSemaphore" (device : VkDevice) (create_info : ptr SemaphoreCreateInfo) (allocator : Allocator) (semaphore : mut ptr VkSemaphore) : Result endmodule
Библиотечная функция vkCreateSemaphore доступна под именем vk::create_semaphore.
def create_semaphore (device : VkDevice) = let semaphore_create_info = SemaphoreCreateInfo type = StructureType::SemaphoreCreateInfo next = null flags = SemaphoreCreateFlags@zero let mut semaphore = null let result = vk::create_semaphore device semaphore_create_info@ptr null semaphore@mut_ptr assert result == Result::Success semaphore
Модификаторы @ptr и @mut_ptr возвращают указатель на значение, @fn — указатель на функцию, @zero — обнулённый блок памяти.
type f32 def Real.sin "sinf" : f32 def Real.cos "cosf" : f32 def Real.floor "floorf" : f32 def Real.sqrt "sqrtf" : f32
Внешние функции могут быть не только свободными, но и методами типа, в том числе служить определением абстрактных.
type f64 def Real.sin : f64 def Real.cos : f64 def Real.floor : f64 def Real.sqrt : f64
Функции для f64 уже имеют правильные имена.
Код
В опубликованном коде есть ящики для работы с vulkan и glfw.
Реактивное программирование
type Border = inherit Control var obs left : u32 = 0 var obs right : u32 = 0 var obs up : u32 = 0 var obs down : u32 = 0 var obs content : Option<Control> = None
Модификатор obs создаёт контейнер типа mut Atom<T>, поддерживающий публикацию оповещений об изменении значения.
let content_width = content@atom.map { case _ of None -> 0 Some control -> control.width }
content@atom вместо значения поля content возвращает контейнер, на котором определена функция map.
let thickness = (border.left@atom border.right@atom border.up@atom border.down@atom) |> to_atom
Можно преобразовать кортеж атомов в атом кортежей, thickness имеет тип Atom<(u32, u32, u32, u32)>.
val memory@atom = (content_width, content_height, thickness) |> to_atom |> map { width, height, thickness -> if width == 0 || height == 0 then None else Some (width, height, thickness) } |> option_map { width, height, (left, right, up, down) -> ... MeshMemory.from_quads quads.as_slice }
Из-за модификатора @atom привязка memory имеет тип Option<MeshMemory>, а не Atom<Option<MeshMemory>>.
ссылка на оригинал статьи https://habr.com/ru/articles/818119/
Добавить комментарий