Другие типы данных

Пусто (:None)

:None (пусто) — не содержит значения (точнее имеет одно значение :None). Указывается в тексте программы как один подчерк «_». Значение None имеют не инициализированные переменные и при попытке чтения из такой переменной возникает ошибка.

Тип переменной может быть явно указан или выведен автоматически из присваиваемого значения. Присвоить новое значение уже инициализированной переменной можно только для совместимого типа, так как неявное преобразование типов не допускаются.

    $var := _; # Создать не инициализированную переменную
    $var2 := var; # Ошибка!!! Нельзя прочитать не инициализированную переменную var
    $var = 1000; # У переменной будет тип Short (минимальный размер для хранения значения)
    $var = 0,5; # Ошибка!!! Short ← Float не совместимы
    $var = _; # Очистить значение переменной
    $var = 0,5; # Теперь можно, т. к. None совместим с любым типом

Диапазон (:Range)

Диапазон — специальный тип данных, являющейся приблизительным аналогом типа «генератор» в Python. К диапазону можно обращаться как к итератору и он будет поочередно выдавать элементы в указанном интервале с заданным шагом. Диапазон в тексте программы указывается как два или три элемента через две точки, например 1..5 — диапазон от единицы до пяти с шагом по умолчанию 1. В качестве параметров диапазона можно указывать не только литералы, но и имена переменных и даже рациональные числа. Например, 0,1..$stop..0,1 — диапазон от 0,1 до значения, указанного в переменной $stop с шагом 0,1 или 0..1\10 - диапазон рациональных числе от 0 до 10.

Диапазон целых чисел можно использовать в качестве индекса у тензоров (точнее, у любых объектов, которые допускают доступ к своим элементам по индексу, т.е. тензоры, словари и текстовые строки). Фактический, это поведение аналогично slice в языке Python и array[1:5] в Python означает тоже самое, что и array[1..5] в NewLang.

В качестве индекса у тензоров еще можно указать произвольное количество измерений с помощью многоточия, т.е.

    $tensor[…, 0] = 0; # Обнулить все первые элементы в каждом измерении.

В будущем можно будет добавить возможность указывать индексы элементов с помощь диапазонов

$tensor = :Tensor[12](4..4 = 29, 2..3 = 15, , 7..9..2 = 7, 10 ...); # [0, 0, 15, 15, 29, 0, 0, 7, 0, 7, 10, 10,]

Поток (:Thread)

Поток — специальный тип данных, который запускает функцию в отдельном потоке, который выполнтеся паралельно основному потоку приложения. Поток созадется в остановленном виде и для его запуска необходимо вызвать метод start(), а чтобы дождаться завершения потока вызывается метод join().

    rand():Int32 := %rand;
    usleep(usec:DWord64):None := %usleep;
    printf(format:FmtChar, ...):Int32 := %printf;

    func(count:Integer, target:String) := {
        $iter := 1..$count?; # Итератор для диапазона от 1 до $count
        [ $iter ?! ] <-> {   # Цикл, пока итератор валидный
            
            $step := $iter!;  # Получить текущий и перейти на следующий элемент итератора
            
            printf('Number %d from %s!', $step, $target);
                
            usleep( rand() % 1000 );    # Случайная задержка

        }
    }

    thread = :Thread(func, 5, 'thread');

    thread.start();

    func(5, 'main');

    thread.join();

Number 1 from the thread! Number 1 from the main! Number 2 from the thread! Number 2 from the main! Number 3 from the thread! Number 4 from the thread! Number 3 from the main! Number 4 from the main! Number 5 from the main! Number 5 from the thread!

Условные переменные (condvars). Сходны с событиями, но не являются объектами, занимающими память — используется только адрес переменной, понятие «содержимое переменной» не существует, в качестве условной переменной может использоваться адрес произвольного объекта. В отличие от событий, установка условной переменной в просигнализированное состояние не влечёт за собой никаких последствий в случае, если на данный момент нет потоков, ожидающих на переменной. Установка события в аналогичном случае влечёт за собой запоминание состояния «просигнализировано» внутри самого события, после чего следующие потоки, желающие ожидать события, продолжают исполнение немедленно без остановки. Для полноценного использования такого объекта необходима также операция «освободить mutex и ожидать условную переменную атомарно». Активно используются в UNIX-подобных ОС. Дискуссии о преимуществах и недостатках событий и условных переменных являются заметной частью дискуссий о преимуществах и недостатках Windows и UNIX.

ERESOURCE. Мьютекс, поддерживающий рекурсивный захват, с семантикой разделяемого или эксклюзивного захвата. Семантика: объект может быть либо свободен, либо захвачен произвольным числом потоков разделяемым образом, либо захвачен всего одним потоком эксклюзивным образом. Любые попытки осуществить захваты, нарушающее это правило, приводят к блокировке потока до тех пор, пока объект не освободится так, чтобы сделать захват разрешённым. Также есть операции вида TryToAcquire — никогда не блокирует поток, либо захватывает, либо (если нужна блокировка) возвращает FALSE, ничего не делая. Используется в ядре Windows, особенно в файловых системах — так, например, любому кем-то открытому дисковому файлу соответствует структура FCB, в которой есть 2 таких объекта для синхронизации доступа к размеру файла. Один из них — paging IO resource — захватывается эксклюзивно только в пути обрезания файла, и гарантирует, что в момент обрезания на файле нет активного ввода-вывода от кэша и от отображения в память.

Интерфейс операционной системы (:System)

Для доступа к различным функциям операционной системы NewLang предоставляет специальный тип данных :System. Он позволяет запускать системные команды и переопределять для них параметры системного окружения.

Выполнение команды происходит при вызове метода run(), которому нужно передать имя системной команды и при необходимости дополнительные аргументы командной строки.

Результат выполнения команды (текстовый вывод, код завершения и т.д.) доступен как отдельные свойства объекта.

Пример:

    :System().run("echo", "Hello from the other side!");

    os := :System();
    
    home_dir := os.run("cd", "~");
    printf("`cd ~` ran with exit code %d", os.exitcode());

    unknown_dir := os.run("cd", "doesnotexist");
    printf("`cd doesnotexis` ran with exit code %d", os.exitcode());

Первая строка делает запускает команду echo.

Потом создается объект :System и далее мы создаем две переменные, в которых хранятся результаты этой выполнения команды cd, которые изменяют каталог на домашнюю папку и на несуществующую папку.

Запустив этот файл, мы увидим:

    Hello from the other side!
    `cd ~` ran with exit code 0
    sh: line 0: cd: doesnotexist: No such file or directory
    `cd doesnotexist` ran with exit code 256

Первая строка - результат выполнения команды echo.

Далее команда cd выполняется успешно и изменяет каталог на домашний. Следовательно, код возврата ноль, который получаем с помощью вызова метода exitcode().

Последняя команда cd выполняется с ошибкой, так как пытается изменить каталог на несуществующую папку.

Простой варинат выполнение системной команды

Есть упрощенный вариант выполнения системной команды с помощью заключения строки выполнения в обратные кавычки (как в bash).

    `echo Hello from the other side!`;

В этом случае системная команда :System.run() сразу выполняется, а её текстовый вывод сохраняется в текстовую строку с широкими символами (:StrWide).

Чтобы получить код возврата (например, чтобы проверить результат завершения на ошибки), можно обратится к результату выполнения последней команды $^.exitcode() или сохранить результат выполнения предыдущей команды в отдельную переменню, которая будет иметь тип :System.

    `echo Hello from the other side!`;
    res := $^;
    printf("`cd doesnotexis` ran with exit code %d", res.exitcode());

В дальнейшем планируется расширить возможности интерфейса с операционной системой

  • фоновое выполнение программ (useless_cat_call = subprocess.run([“cat”], stdout=subprocess.PIPE, text=True, input=“Hello from the other side”))
  • переназначение ввода/вывода (list_dir = subprocess.Popen([“ls”, “-l”]); list_dir.wait())
  • чтение/изменние перемнных окржения

Еще пример возможностей для расширения

  • os.name - имя операционной системы. Доступные варианты: ‘posix’, ’nt’, ‘mac’, ‘os2’, ‘ce’, ‘java’.
  • os.environ - словарь переменных окружения. Изменяемый (можно добавлять и удалять переменные окружения).
  • os.getlogin() - имя пользователя, вошедшего в терминал (Unix).
  • os.getpid() - текущий id процесса.
  • os.uname() - информация об ОС. возвращает объект с атрибутами: sysname - имя операционной системы, nodename - имя машины в сети (определяется реализацией), release - релиз, version - версия, machine - идентификатор машины.
  • os.access(path, mode, *, dir_fd=None, effective_ids=False, follow_symlinks=True) - проверка доступа к объекту у текущего пользователя. Флаги: os.F_OK - объект существует, os.R_OK - доступен на чтение, os.W_OK - доступен на запись, os.X_OK - доступен на исполнение.
  • os.chdir(path) - смена текущей директории.
  • os.chmod(path, mode, *, dir_fd=None, follow_symlinks=True) - смена прав доступа к объекту (mode - восьмеричное число).
  • os.chown(path, uid, gid, *, dir_fd=None, follow_symlinks=True) - меняет id владельца и группы (Unix).
  • os.getcwd() - текущая рабочая директория.
  • os.link(src, dst, *, src_dir_fd=None, dst_dir_fd=None, follow_symlinks=True) - создаёт жёсткую ссылку.
  • os.listdir(path=".") - список файлов и директорий в папке.
  • os.mkdir(path, mode=0o777, *, dir_fd=None) - создаёт директорию. OSError, если директория существует.
  • os.makedirs(path, mode=0o777, exist_ok=False) - создаёт директорию, создавая при этом промежуточные директории.
  • os.remove(path, *, dir_fd=None) - удаляет путь к файлу.
  • os.rename(src, dst, *, src_dir_fd=None, dst_dir_fd=None) - переименовывает файл или директорию из src в dst.
  • os.renames(old, new) - переименовывает old в new, создавая промежуточные директории.
  • os.replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None) - переименовывает из src в dst с принудительной заменой.
  • os.rmdir(path, *, dir_fd=None) - удаляет пустую директорию.
  • os.removedirs(path) - удаляет директорию, затем пытается удалить родительские директории, и удаляет их рекурсивно, пока они пусты.
  • os.symlink(source, link_name, target_is_directory=False, *, dir_fd=None) - создаёт символическую ссылку на объект.
  • os.sync() - записывает все данные на диск (Unix).
  • os.truncate(path, length) - обрезает файл до длины length.
  • os.utime(path, times=None, *, ns=None, dir_fd=None, follow_symlinks=True) - модификация времени последнего доступа и изменения файла. Либо times - кортеж (время доступа в секундах, время изменения в секундах), либо ns - кортеж (время доступа в наносекундах, время изменения в наносекундах).
  • os.walk(top, topdown=True, onerror=None, followlinks=False) - генерация имён файлов в дереве каталогов, сверху вниз (если topdown равен True), либо снизу вверх (если False). Для каждого каталога функция walk возвращает кортеж (путь к каталогу, список каталогов, список файлов).
  • os.system(command) - исполняет системную команду, возвращает код её завершения (в случае успеха 0).
  • os.urandom(n) - n случайных байт. Возможно использование этой функции в криптографических целях.