← Timeline
Avatar
Tigra
(updated )
Дискуссия про функциональный язык OCaml

Предмет обсуждения - язык OCaml.

Собеседник:

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

Evaluation of these expressions involves an environment that maps identifiers to values, represented as a list of pairs.

# let rec eval env = function
    | Num i -> i
    | Var x -> List.assoc x env
    | Let (x, e1, in_e2) ->
       let val_x = eval env e1 in
       eval ((x, val_x) :: env) in_e2
    | Binop (op, e1, e2) ->
       let v1 = eval env e1 in
       let v2 = eval env e2 in
       eval_op op v1 v2
  and eval_op op v1 v2 =
    match op with
    | "+" -> v1 + v2
    | "-" -> v1 - v2
    | "*" -> v1 * v2
    | "/" -> v1 / v2
    | _ -> failwith ("Unknown operator: " ^ op);;
val eval : (string * int) list -> expression -> int = <fun>
val eval_op : string -> int -> int -> int = <fun>

Дык, энто же паттерн матчинг. За ним куча complexity спрятана 🙂
Что мне не нравится из того, что используется в этом куске кода, это, например, то, что для определения функции с паттерн матчингом используется слово function, а для "обычной" функции - нет.

Ещё не нравится, что and используется в выражении, чтоб доопределить ещё функцию eval_op (я так понял этот код) - но это скорей вопрос к автору кода, а не к автору языка.

Но в целом это довольно "скороспелые" претензии, не прочувствовав язык как нечто целостное, я бы их всерьёз не предъявлял 🙂

ок давай разберем

  1. определение функции бинарным OR "|" при этом в коде чуть ниже после энд оператор уже вроде как обычный OR используется.

  2. что вообще должно означать Num i -> i ? Почему не просто Num i?

  3. у нас в обозначении функции env в ее коде env. Очень надеюсь что это рекурсия то нихрена не уверн.

  4. val_x = а дальше хер пойми что. Ну и фраза снизу Let x=1 in x+x. Мне как математику от такого обозначения хочеться выйти в окно.. бо вообще хер пойми что это выражение значит.

  5. конкатанация строк через знак возведения в степень.. ну тоже сильно. Особенно будет сильно когда в коде две строки а и б и код a^b ))

Чем больше различие в языке от стандартных норм.. тем больше проблем это создает при переходе.

А херова туча закарлючек и отсутсвие скобок в нужных местах делает это повестью минувших лет особенно в мат операциях. и большем обьеме саб вызовов

  1. Это не бинарный OR, это паттерн матчинг - вся вот эта конструкция
| ... -> ...
| ... -> ...
...
| -> ...

Это как бы расширенный switch, который распознаёт не просто списки или диапазоны целых, но более сложные структуры, например,

  1. Тут скорее всего case-классы. Это у нас код чего? Эвалюейшена некого выражения. Предполагаю, выражения на окамле же.
    Num, Var, Let, Binop - это Case-классы, разновидности составляющей распаршенного выражения.
    Соответственно если у нас паттерн такой, Num i, это значит, что сматчится кейс-класс Num, при этом в качестве его параметра стоит i - символ, значит, сматчится Num с любым параметром, а i забиндится на конкретное значение, соответственно результатом всего паттерн-матчинг-выражения будет то что после ->, т.е. i.

  2. env это параметр функции eval, ну и да, там есть рекурсия

  3. Let ( x, e1, in_e2) -> ... заматчили кейс-класс Let, забиндили его параметры (или, если хочешь, поля) в соответствующие переменные.
    Выражение такого типа - по аналогии с другими функциональными языками - let x = ... in ... - это значит вычислить x и подставить его в выражение после in. В императивном языке это бы было сделано просто двумя последовательными присвоениями. Но с такой конструкцией транслятору языка ясно, что для чего нужно вычислить, что позволяет легче делать ленивые вычисления.
    eval ((x, val_x) :: env) in_e2 - сконкатенировать пару (x, val_x) к списку env и вызвать eval с двумя параметрами - вот этот список и in_e2.

По поводу стандартных вещей в синтаксисе - это дело привычки, традиции (и всегда холивары по этому поводу случаются - фигурные скобки, отступы или вообще begin..end?). Ну и стоит вспомнить, что семейство языков-то древнее, и не факт тогда был, что алголоподобные стали бы рулить. Так что это просто другая традиция, другое ответвление. Это как ругать китайский язык за непривычную нам, европейцам, систему письменности 🙂

Таки да, там кейс-класс, выше определен:

# type expression =
    | Num of int
    | Var of string
    | Let of string * expression * expression
    | Binop of string * expression * expression;;
To react or comment  View in Web Client
Comments (21)
Avatar

let rec это взаимно рекурсивное определение двух функций сразу, зависящих друг от друга: eval и eval_op - отсюда и кривоватый синтаксис с and

👍1
Avatar

Автор кода, конечно, прогнал с function, решив обойтись без названия второго аргумента. Можно было бы написать попроще

let rec eval env expr = match expr with
  | Num x -> x
...
👍1
Avatar

Сперва не совсем понял, почему им понадобилось вообще описывать функции как рекурсивные эксплицитно - вот почему.

👍1
Avatar

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

Avatar
Tigra (updated )

Ну отсутствие рекурсивности-то и вычислить можно, тупо проверив наличие где-то в выражении вызова себя. И оптимизировать хвостовую рекурсию языки в целом могут, распознав её.

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

# let x = 1
        let x = x + 10 in
             x * x

Странноватый стиль, однако, но зачем-то авторам ML такое понадобилось.

Тогда упоминание этого имени в теле функции - это ссылка на старую функцию или на вновь определяемую?

В таком варианте - ссылка на старую:

let print_string s = print_string s; print_newline ()

А в таком - на самоё себя:

let rec print_string s = print_string s; print_newline ()

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

👍1
Avatar

Shmuel Leib Melamud В предыдущем комменте нажатие на "Read more..." для разворачивания коммента то ли тормозит, то ли срабатывает не с первого раза.

Update: Вообще не работает, работает клик по телу коммента.

Avatar

Например, для дебага:

let compute x =
  let res = compute x in
     print("compute " ^ x ^ "=" ^ res); res
👍1
Avatar

Shmuel Leib Melamud комменты с телефона не получают кастомной аватарки, а с компа получают

Avatar
Avatar
Avatar
Avatar

А, это та же фишка, на которую я наткнулся ранее?

P.S.: Если у человека одна-единственная аватарка, контрол для выбора аватарок можно не показывать. Хотя, с другой стороны, это напоминает ему, что их у него может быть больше одной.

Avatar

Да, я именно для этого и оставил - чтобы напоминать, что может быть больше одной аватарки.

👍1
Avatar

Но я таки вижу, что в первых комментах аватарки нет. Какая версия клиента на телефоне? (Открыть меню уведомлений, там в самом низу бледными буквами написана версия.)

Avatar

Я не знаю, что такое меню уведомлений, там ничего не написано - у меня тонкий клиент вокруг web.moera.org , причём нотификации иногда открываются в браузере, а не в клиенте. Похоже, клиент тыщу лет не обновлялся.

App info
Version: 0.10.0.0
Released on Jan 4, 2021

Avatar

Нет, это не та версия. Меню уведомлений - это то, что открывается, если нажать на колокольчик.

Avatar
Avatar
Avatar

Сорри, я сейчас пишу новый клиент для Android, в котором этих проблем с обновлением клиента не будет. Потерпите еще недели две 😞

👍😍2
Avatar
Avatar

Да, можно сбросить в Chrome все данные, связанные с web.moera.org. После этого придется логиниться заново, но клиент должен обновиться. Актуальная версия сейчас: 289b12e+df18.

To react or comment  View in Web Client