Перейти к содержанию

Обзор языка Mojo

На этой странице представлен обзор языка Mojo.

Если вы знакомы с Python, то большая часть кода Mojo покажется вам знакомой. Однако Mojo включает в себя такие функции, как статическая проверка типов, безопасность памяти, технологии компиляторов нового поколения и многое другое. Таким образом, Mojo также имеет много общего с такими языками, как C++ и Rust.

Если вы предпочитаете учиться на практике, ознакомьтесь с руководством по началу работы с Mojo.

На этой странице мы познакомим вас с основным синтаксисом Mojo, чтобы вы могли быстро приступить к написанию кода и разобраться в других кодах Mojo, с которыми вы столкнетесь. В последующих разделах руководства по Mojo эти темы рассматриваются более подробно, а ссылки приведены ниже по мере необходимости.

Давайте начнем! 🔥

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

Hello world

Вот традиционная программа "Hello, world!" в Mojo:

def main():
    print("Hello, world!")

Каждая программа Mojo должна включать функцию с именем main() в качестве точки входа. Вскоре мы поговорим о функциях подробнее, но пока достаточно знать, что вы можете написать def main():, за которым следует текст функции с отступом.

Оператор print() выполняет то, что вы ожидаете, выводя свои аргументы в стандартный вывод.

Переменные

В Mojo вы можете объявить переменную, просто присвоив значение новой именованной переменной:

def main():
    x = 10
    y = x * x
    print(y)

Вы также можете явно объявлять переменные с помощью ключевого слова var:

var x = 10

При объявлении переменной с помощью var вы также можете объявить тип переменной, с присваиванием или без него:

def main():
    var x: Int = 10
    var sum: Int
    sum = x + x

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

x = 10
x = "Foo" # Ошибка: Не удается преобразовать значение "StringLiteral" в "Int"

Для получения более подробной информации смотрите страницу о переменных.

Блоки и инструкции

Блоки кода, такие как функции, условия и циклы, обозначаются двоеточием, за которым следуют строки с отступом. Например:

def loop():
    for x in range(5):
        if x % 2 == 0:
            print(x)

Вы можете использовать любое количество пробелов или знаков табуляции в качестве отступа (мы предпочитаем 4 пробела).

Все инструкции кода в Mojo заканчиваются новой строкой. Однако инструкции могут занимать несколько строк, если вы сделаете отступ в следующих строках. Например, эта длинная строка занимает две строки:

def print_line():
    long_text = "This is a long line of text that is a lot easier to read if"
                " it is broken up across two lines instead of one long line."
    print(long_text)

И вы можете связывать вызовы функций по цепочке между строками:

def print_hello():
    text = ",".join("Hello", " world!")
    print(text)

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

Функции

Вы можете определить функцию Mojo, используя ключевое слово def или fn. Например, в следующем примере ключевое слово def используется для определения функции с именем greet, которая требует единственного строкового аргумента и возвращает строку:

def greet(name: String) -> String:
    return "Hello, " + name + "!"

Значения def и fn отличаются от значений по умолчанию для обработки ошибок и изменяемости аргументов. Более подробную информацию об определении и вызове функций см. на странице Функции.

Комментарии

Вы можете создать однострочный комментарий, используя символ хэша #:

# Это комментарий. Компилятор Mojo игнорирует эту линию.

Комментарии также могут следовать за некоторым кодом:

var message = "Hello, World!" # This is also a valid comment

Комментарии к документации API заключены в тройные кавычки. Например:

fn print(x: String):
    """Prints a string.

    Args:
        x: The string to print.
    """
    ...

Документирование вашего кода с помощью таких комментариев (известных как "docstrings") - это тема, которую нам еще предстоит полностью раскрыть, но вы можете сгенерировать ссылку на API из docstrings с помощью команды mojo doc.

Технически, docstrings - это не комментарии, а специальное использование синтаксиса Mojo для многострочных строковых литералов. Более подробную информацию смотрите в разделе Строковые литералы на странице, посвященной типам.

Структуры

Вы можете создавать высокоуровневые абстракции для типов (или "объектов") в виде структуры.

Структура в Mojo похожа на класс в Python: они оба поддерживают методы, поля, перегрузку операторов, декораторы для метапрограммирования и так далее. Однако структуры Mojo полностью статичны — они привязываются во время компиляции, поэтому не допускают динамической отправки или каких-либо изменений структуры во время выполнения. (В будущем Mojo также будет поддерживать классы в стиле Python.)

Например, вот базовая структура:

struct MyPair(Copyable):
    var first: Int
    var second: Int

    fn __init__(out self, first: Int, second: Int):
        self.first = first
        self.second = second

    fn __copyinit__(out self, existing: Self):
        self.first = existing.first
        self.second = existing.second

    def dump(self):
        print(self.first, self.second)

И вот как вы можете это использовать:

def use_mypair():
    var mine = MyPair(2, 4)
    mine.dump()

Обратите внимание, что некоторые функции объявляются с помощью функции fn, в то время как функция dump() объявляется с помощью функции def. В общем, вы можете использовать любую форму в структуре.

Структура MyPair содержит два специальных метода: __init__(), конструктор, и __copyinit__(), конструктор копирования. Методы жизненного цикла, подобные этому, управляют созданием, копированием, перемещением и уничтожением структуры.

Для большинства простых типов вам не нужно писать методы жизненного цикла. Вы можете использовать декоратор @fieldwise_init, чтобы сгенерировать стандартный инициализатор для вашего поля, и Mojo синтезирует конструкторы копирования и перемещения, если вы запросите их с учетом соответствия трейтам. Таким образом, структура MyPair может быть упрощена до этого:

@fieldwise_init
struct MyPair(Copyable, Movable):
    var first: Int
    var second: Int

    def dump(self):
        print(self.first, self.second)

Для получения более подробной информации смотрите страницу о структурах.

Traits

Трейт подобен шаблону характеристик для структуры. Если вы хотите создать структуру с характеристиками, определенными в трейте, вы должны реализовать каждую характеристику (например, каждый метод). Каждая характеристика в трейте является "требованием" к структуре, и когда ваша структура реализует все требования, считается, что она "соответствует" этому трейту.

Использование трейтов позволяет создавать универсальные функции, которые могут принимать любой тип, соответствующий трейт, а не только определенные типы.

Например, вот как можно создать трейт:

trait SomeTrait:
    fn required_method(self, x: Int): ...

Три точки, следующие за подписью метода, - это синтаксис Mojo, указывающий на то, что метод не реализован.

Вот структура, которая соответствует SomeTrait:

@fieldwise_init
struct SomeStruct(SomeTrait):
    fn required_method(self, x: Int):
        print("hello traits", x)

Затем, вот функция, которая использует трейт в качестве типа аргумента (вместо типа struct):

fn fun_with_traits[T: SomeTrait](x: T):
    x.required_method(42)

fn use_trait_function():
    var thing = SomeStruct()
    fun_with_traits(thing)

Вы увидите атрибуты, используемые во многих API-интерфейсах, предоставляемых стандартной библиотекой Mojo. Например, типы коллекций Mojo, такие как List и Dict, могут хранить любой тип, соответствующий копируемым и перемещаемым атрибутам. Вы можете указать тип при создании коллекции:

my_list = List[Float64]()

Вероятно, вас интересуют квадратные скобки в функции fun_with_traits(). Это не аргументы функции (которые заключены в круглые скобки); это параметры функции, которые мы объясним далее.

Без трейтов аргумент x в функции fun_with_traits() должен был бы объявлять определенный тип, который реализует required_method(), такой как SomeStruct (но тогда функция принимала бы только этот тип). С помощью трейтов функция может принимать любой тип для x, если он соответствует ("реализует") некоторым параметрам. Таким образом, функция fun_with_traits() известна как "универсальная функция", поскольку она принимает обобщенный тип вместо определенного типа.

Для получения более подробной информации смотрите страницу о traits.

Параметризация

В Mojo параметр - это переменная времени компиляции, которая становится константой времени выполнения, и она объявляется в квадратных скобках в функции или структуре. Параметры позволяют выполнять метапрограммирование во время компиляции, что означает, что вы можете генерировать или изменять код во время компиляции.

Многие другие языки используют "параметр" и "аргумент" взаимозаменяемо, поэтому имейте в виду, что когда мы говорим о таких понятиях, как "параметр" и "параметрическая функция", мы говорим об этих параметрах во время компиляции. Принимая во внимание, что "аргумент" функции - это значение во время выполнения, указанное в круглых скобках.

Параметризация - это сложная тема, которая более подробно рассматривается в разделе "Метапрограммирование", но мы хотим немного прояснить ее здесь. Для начала давайте рассмотрим параметрическую функцию:

def repeat[count: Int](msg: String):
    @parameter # вычислите следующий цикл for во время компиляции
    for i in range(count):
        print(msg)

Эта функция имеет один параметр типа Int и один аргумент типа String. Для вызова функции необходимо указать как параметр, так и аргумент:

def call_repeat():
    repeat[3]("Hello")
    # Напечатает "Hello" 3 раза

Указав count в качестве параметра, компилятор Mojo может оптимизировать функцию, поскольку это значение гарантированно не изменится во время выполнения. А декоратор @parameter в коде сообщает компилятору, что он должен вычислять цикл for во время компиляции, а не во время выполнения.

Компилятор эффективно генерирует уникальную версию функции repeat(), которая повторяет сообщение только 3 раза. Это повышает производительность кода, поскольку во время выполнения требуется меньше вычислений.

Аналогично, вы можете определить структуру с параметрами, что фактически позволяет вам определять варианты этого типа во время компиляции в зависимости от значений параметров.

Более подробную информацию о параметрах смотрите в разделе, посвященном метапрограммированию.

Интеграция с Python

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

Например, вот как вы можете импортировать и использовать NumPy:

from python import Python

def main():
    var np = Python.import_module("numpy")
    var ar = np.arange(15).reshape(3, 5)
    print(ar)
    print(ar.shape)

В среде, где вы используете Mojo, должен быть установлен модуль Python (например, numpy).

Более подробную информацию смотрите на странице, посвященной интеграции с Python.

Следующие шаги

Надеемся, что на этой странице вы получили достаточно информации, чтобы начать экспериментировать с Mojo, но это лишь поверхностное знакомство с тем, что доступно в Mojo.

Если у вас есть желание прочитать больше, ознакомьтесь с каждой страницей этого руководства по Mojo — следующая страница посвящена функциям.

В противном случае, вот несколько других ресурсов, с которыми стоит ознакомиться:

  • Смотрите практическое руководство "Начало работы с Mojo", которое поможет вам освоиться с Mojo.

  • Если вы хотите поэкспериментировать с каким-то кодом, клонируйте наше репозиторий на GitHub, чтобы попробовать наши примеры кода:

 git clone https://github.com/modular/modular.git
 cd max/examples/mojo
  • Чтобы ознакомиться со всеми доступными API-интерфейсами Mojo, ознакомьтесь со справочником по стандартной библиотеке Mojo.