Обзор языка 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.