Раздел дорабатывается

Модули

Каждый исходный файл Фикус обрабатывается компилятором Фикус как модуль. Большие проекты могут быть разделены на несколько файлов, представляющих осмысленные и, возможно, повторно используемые компоненты, после чего вы можете импортировать один модуль в другой и воспользоваться функциональностью импортируемого модуля. По соглашению модули, предназначенные для импорта в другие модули, имеют имена смешанного регистра и начинаются с заглавной латинской буквы, например, MyModule.fx. После того, как вы создали MyModule.fx и разместили там некоторые определения, вы можете импортировать его в другой модуль и получать доступ к его функционалу используя точечную нотацию:

// MyModule.fx
...
fun fact(n: int) = if n <= 1 {1} else {n*fact(n-1)}

// где-то в начале файла a_script.fx
import MyModule
...
val n = 5
println(f"{n}! = {MyModule.fact(n)}")

Существует стандартная библиотека Фикус, включающая ряд полезных модулей, реализующих общую функциональность. Мы будем использовать некоторые из этих модулей в руководстве по мере необходимости. Компилятор Фикус включает функциональность из стандартного модуля Builtins в каждый скомпилированный модуль, таким образом, если вы используете функциональность из Builtins, вам не нужно помещать директиву import Builtins в ваш код. Аналогично этому, нет необходимости явно импортировать следующие стандартные модули, поскольку они импортируются автоматически: Array, Char, List, Math, String, Vector.

Директива import является единственной командой для работы с модулями и имеет две вариации:

  1. import-as
// например, import SomeVeryLongModuleName as m
import ModuleName as Alias
  1. from-import
// импортировать выбранную функциональность
from MyModule import fact
val f5 = fact(5)

// или всё сразу
from MyModule import *

Обратите внимание, что в случае перегруженных функций from-import импортирует все функции с указанным именем. Если мы хотим импортировать одну конкретную функцию из модуля, мы можем использовать следующий код вместо этого:

import File
val fopen = File.open
// в случае перегруженных функций
// нам нужно явно указать тип
val dsin = (Math.sin : double->double)

Заметим также, что поскольку значения неизменяемые, компилятор не различает между dsin и Math.sin: double->double в приведённом примере, и использование псевдонима не вызывает потери производительности.

Сложные модули

Замечание по реализации: на данный момент лишь часть этой функциональности реализована. Возможно организовать размещение модулей в подпапках, однако поддержка файла __init__.fx отсутствует.

При разработке сложных фреймворков или приложений плоская структура модулей (один модуль на файл) быстро становится недостаточной. Одним из возможных обходных путей могло бы стать специальное именование схем, то есть использование имён вроде DeepLearning_Nonlin_ReLU.fx, помогающих группировать модули. Лучшим решением было бы использовать механизм сложных модулей в Фикус, похожий на тот, что используется в Python. Предположим, мы хотим создать сложный модуль, обеспечивающий интерфейс к библиотеке компьютерного зрения OpenCV, состоящей из множества частей (Core, Imgproc и др.). Нам также хотелось бы поместить суб-коллекцию Contrib экспериментальной функциональности, содержащую много компонентов (DNN, Text и др.). Во Фикус вся эта упаковка может выглядеть следующим образом:

ficus/lib # родительская папка библиотеки, хотя её необязательно размещать именно здесь;
            # вы можете разместить пакеты везде, где захотите,
            # пока настроите переменную окружения ficus_PATH,
            # либо используйте опцию -I с фикусом
    OpenCV/
        __init__.fx # необязательный прокси-модуль "импортировать всё"
        Core.fx # основная функциональность
        Imgproc.fx # обработка изображений
        ...
        Contrib/
            __init__.fx # необязательный прокси-модуль "импортировать всё"
            Bioinspired.fx # глубокое обучение
            Text.fx # обнаружение текста
            ...

В вашем приложении вы сможете

  • импортировать всё целиком
import OpenCV as cv // псевдоним cv, конечно же, опционален
  • импортировать определённую часть
import OpenCV.Contrib.Text
import OpenCV.Contrib.Bioinspired as cv_bio
  • импортировать определённые элементы функциональности
from OpenCV.Imgproc import Canny

При реализации таких сложных модулей Следует помнить:

  • реализация механизма сложных модулей довольно проста, никаких магических особенностей относительно пространств имён или зависимостей нет. То есть, вы реализуете отдельные модули обычным способом и вручную импортируете всю необходимую функциональность. Обратите внимание, что при импорте некоторых частей сложного модуля в другие части необходимо указывать полные пути с использованием точки .:
// OpenCV.Imgproc
import OpenCV.Core // или используйте "import OpenCV.Core as Core",
                   // чтобы ввести удобные псевдонимы
  • циклических зависимостей модулей следует избегать; впрочем, Фикус в этом случае сообщит об ошибке.

  • файлы __init__.fx являются необязательными. Например, если файл OpenCV/Contrib/__init__.fx отсутствует, вы всё равно можете импортировать OpenCV.Contrib.Text, но не сможете импортировать сам каталог OpenCV.Contrib; иными словами, наличие файла __init__.fx позволяет пользователям импортировать весь каталог.

  • содержимое файла __init__.fx свободно формируется автором пакета и совершенно произвольно. Есть два популярных подхода, которые можно комбинировать:

  1. привести всё в одно пространство имён:
// OpenCV/__init__.fx
from OpenCV.Core import *
from OpenCV.Imgproc import *
...

тогда пользователь сможет написать:

import OpenCV as cv
cv.dft(...)
cv.Canny(...)
  1. служить в качестве быстрого средства пакетного импорта:
// OpenCV/__init__.fx
import OpenCV.Core as Core
import OpenCV.Imgproc as Imgproc
...

тогда пользователь сможет записать:

import OpenCV as cv
cv.Core.dft(...)
cv.Imgproc.Canny(...)
  1. предложить разные альтернативы посредством нескольких прокси-модулей, например:
// OpenCV/All.fx
import OpenCV.Core as Core
import OpenCV.Imgproc as Imgproc
...

тогда пользователь сможет писать:

import OpenCV.All as cv
cv.Core.dft(...)
cv.Imgproc.Canny(...)