Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Основы Unison

В Unison все начинается с «терма». Если по-простому, то терм - это название какого-либо значения или функции. Возьмем такой код:

helloWorld : Text
helloWorld = "Hello, World!"

Такой код объявит переменную helloWorld, которая имеет тип Text и хранит в себе строку «Hello, World!». Сразу скажу, что все переменные неизменяемы - это один из столпов функционального программирования впринципе, и это удобно, когда системы становятся сложными. Если не верите мне - дочитайте книгу и попробуйте сами сделать аналогичные системы на Unison и на, например, Python :) А пока продолжаем!

Из-за того, что Unison сам умеет понимать, что присваивается переменной, мы можем написать без первой строки:

helloWorld = "Hello, World!"

И все будет работать точно так же. Но для наглядности я буду писать типы переменной.

Создайте файл scratch.u в той же папке, в которой у вас запущен UCM. Затем напишите код и сохраните файл. В терминале вы сразу увидите что-то подобное:

    Loading changes detected in ~/unisonlanguage/scratch.u.


  + helloWorld : Text

  Run `update` to apply these changes to your codebase.

Наш UCM сам автоматически отслеживает файл и показывает, что добавилось, удалилось или изменилось. Попробуем написать update:

quickstart/main> update

  Done.

quickstart/main>

Все, теперь ваша переменная может использоваться, даже не находясь в файле! Для наглядности можете попробовать очистить файл и вписать туда следующее:

> helloWorld

После сохранения файла в UCM сразу отобразится сообщение о том, что

  1. Не найдены изменения;
  2. «Вычислится» наше выражение (в данном случае - просто выведется).
quickstart/main>

  Loading changes detected in ~/unisonlanguage/scratch.u.


  No changes found.


    1 | > helloWorld
          ⧩
          "Hello, world!"

Данная стрелочка объявляет «контрольное выражение» - выражение, которое выводится при любом обновлении файла. Это довольно полезно при разработке, и я вам советую использовать их как можно чаще.

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

isGood : Boolean -- булево
isGood = true 

natural : Nat -- натуральное число
natural = 42

justNum : Int -- целое число
justNum = +42

pi : Float -- число с плавающей точкой
pi = 3.14

aChar : Char -- символ
aChar = ?a

blob : Bytes -- сырые байты
blob = 0xsdeadbeef

Прошу обратить внимание, что целые числа обязаны всегда иметь знак перед числом, ведь если будет не +42, а 42, число будет интерпретировано как натуральное. Также для Char перед любым символом нужно поставить знак вопроса, а для Bytes - применить шаблон 0xs<шестнадцатиричный код>.

Также в терминале у нас при сохранении отобразится следующее:

  Loading changes detected in ~/unisonlanguage/scratch.u.


  + aChar   : Char
  + blob    : Bytes
  + isGood  : Boolean
  + justNum : Int
  + natural : Nat
  + pi      : Float

  Run `update` to apply these changes to your codebase.

Пока нет смысла сохранять, лучше немного поработаем с нашими переменными. Например, посмотрим, что будет, если сложить натуральное число с целым:

  I couldn't figure out what + refers to here:

     20 | > natural + justNum

  The name + is ambiguous. I tried to resolve it by type but no term with that
  name would pass typechecking. I think its type should be:

      Nat -> Int -> o

  If that's not what you expected, you may have a type error somewhere else in
  your code.
  Help me out by using a more specific name here or adding a type annotation.

  I found some terms in scope with matching names but different types. If one of
  these is what you meant, try using its full name:

  (Float.+) : Float -> Float -> Float
  (Int.+) : Int -> Int -> Int
  (Nat.+) : Nat -> Nat -> Nat
  (Pattern.+) : Pattern a -> Pattern a -> Pattern a
  (Class.+) : Class -> Class -> Class
  (Bag.+) : Bag a -> Bag a -> Bag a
  (Decimal.+) : Decimal -> Decimal -> Decimal
  (Integer.+) : Integer -> Integer -> Integer
  (Natural.+) : Natural -> Natural -> Natural
  (Rational.+) : Rational -> Rational -> Rational
  (Duration.+) : Duration -> Duration -> Duration
  (Instant.+) : Instant -> Duration -> Instant
  (LocalDate.+) : LocalDate -> Int -> LocalDate
  (Deprecated.+) : Natural.Deprecated -> Natural.Deprecated -> Natural.Deprecated

Мы увидели, что UCM не знает, какой именно оператор выбрать, потому что для каждого типа он определен свой. И мы также можем догадаться, что у нас не получится сложить два разных типа, пускай они и очень похожи. И если мы все-таки хотим это сделать, нам придется одну из переменных привести к другому типу. Но пока что мы не будем это делать, лучше посмотрим, что еще интересного можно делать здесь и сейчас:

19 | > helloWorld ++ " Hello, Unison!"
           ⧩
           "Hello, world! Hello, Unison!"

20 | > natural - 50
           ⧩
           0

Оператор ++ определен для строк и складывает их, тут все понятно. А вот второй случай интереснее: как вы помните, natural у нас имеет значение 42, однако же ошибки не случилось. То есть мы можем не переживать о том, что у нас может получиться неопределенное поведение, так как Nat всегда положителен. Честно скажу: кроме этих двух особенностей я не нашел особых отличий с другими языками программирования: для чисел есть математические операции, для логики есть булевые операции, и т.п. Остальные операторы будут появляться в книге и остальные типы будут рассказаны вам чуть позже. А сейчас я хочу поговорить немного о документации.

Документация

В коде поддерживаются комментарии:

  • два дефиса - для комментария в линию;
  • {-<любой текст>-} - для мультистрочных комментариев;
  • три дефиса - для разграничения файла; Последнее покажу на примере. Если мы впишем в файл следующее:
> helloWorld ++ " Hello, Unison!"
---
> natural - 50
Я люблю Unison

И сохраним, то UCM нам напишет следующее:

    19 | > helloWorld ++ " Hello, Unison!"
           ⧩
           "Hello, world! Hello, Unison!"

quickstart/main>

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

Но дело в том, что комментарии не «загружаются» в базу при ее обновлении, поэтому они используются только при разработке, а не для документации. Возникает закономерный вопрос: а как же тогда документировать и объяснять код?

И на помощь нам приходит особый тип Doc! Он определяется так: {{<текст>}}, и из коробки поддерживает форматирование, фото, видео, таблицы, Mermaid, LaTEX и даже исполнение кода внутри себя! Я ознакомлю вас с базовым функционалом, но, может, в конце книги оставлю полный список возможностей.

Чтобы добавить документацию к чему-либо, есть два способа:

  1. Он подходит для уже добавленных в базу объектов:
helloWorld.doc = {{Это - строка "Привет, мир!"}}
  1. Подходит для пишущихся сейчас объектов:
{{Очень важное число!}}
bestNumber = 42

После сохранения файла UCM пишет нам:

+ bestNumber     : Nat
+ bestNumber.doc : Doc
+ helloWorld.doc : Doc

Готово, у нас есть документация для модуля! Теперь напишем в самом UCM следующее:

quickstart/main> docs bestNumber

  Очень важное число!

quickstart/main>

Команда docs выводит документацию по объекту. То, что вам пригодится уже с первой минуты - это @source{<имя объекта>} для отображения исходников в Doc, @signature{<имя объекта>} для отображения сигнатуры объекта (это как раз тип объекта), а так же можно добавлять Doc внутри Doc. Давайте я вам покажу все три примера вместе:

{{
  @signature{bestNumber}
  
  Очень важное число!

  @source{bestNumber}

  {{bestNumber.doc.examples}}
}}
bestNumber = 42
bestNumber.doc.examples = {{
    Примеры:

    ```
       bestNumber + 1 
    ```     
}}

Сохраним файл и посмотрим на документацию в UCM:

quickstart/main> docs bestNumber

      bestNumber : Nat

  Очень важное число!

      bestNumber : Nat
      bestNumber = 42

  Примеры:

      bestNumber Nat.+ 1
      ⧨
      43

quickstart/main>

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