Обновить
102
0.2
Роман Смирнов@Source

Head of Elixir at Ecom.tech

Отправить сообщение
Не знаю, что именно ожидалось :)
Ну как, реальных сообщений, хотя бы как в Erlang :)
В остальном согласен, действительно весьма прогрессивная платформа разработки.
Кстати, давно хотел дать эту ссылку, но всегда за мелочами забывал: Десять вещей, которые я терпеть не могу в ООП.
Спасибо, я читал эту статью несколько лет назад, в оригинале. И в принципе тоже с ней согласен по большей части.
гарантирует второе (отсутствие неожиданного поведения)? В программах на Scheme не бывает багов?
А как отсутствие неожиданного поведения связано с отсутствием багов? С неожиданным поведением только часть багов связана, причём чаще меньшая, т.к. программисты привыкают к «особенностям» используемого языка.

Функция — это тоже список? Да, она задается списком, но это же объект другого рода?
Определение функции — это список с вызовом функции define. Да, разумеется, есть с десяток функций, которые реализуются внутри интерпретатора, но для программиста они ведут они себя точно так же, как и все остальные функции.

тут тоже есть примитивные конструкции, которые не совсем соответствуют исходной концепции?
да вроде нет, можно пример?

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

Концепция, в которую вовлечено два компонента (объект и сообщение) скорее всего проще, чем та, в которой присутствует большее количество.
Вот это не факт. Простота применения ЯП на практике с количеством компонентов в концепции слабо коррелирует.
Есть, например, концепция с нулём компонент — «всё есть ничто» и даже соответствующий ЯП. А вот решить на нём практическую задачу совсем не просто :-D

по моим ощущениям ООП не менее мощная, чем ФП, так как в объектах можно смоделировать любую концепцию ФП
OMFG, зачем? Эти парадигмы совершенно ортогональны. Моя позиция в том, что они должны применяться комплементарно на разных уровнях абстракции. ООП можно применять и в Erlang/Elixir и в диалектах Lisp, но там, где оно реально имеет смысл и пользу. А протаскивание идей ООП через все уровни абстракции вплоть до самых примитивных типов ведёт к излишнему и неоправданному усложнению системы.
Собственно изначальная статья посвящена тому, что «Здравствуй ФП» не обозначает «Прощай ООП», оно означает «Здравствуй нормальное ООП, теперь ты будешь работать для меня, а не наоборот»
Просто в одном случае (с дробями) в ответ на сообщения возвращается один и тот же объект (что логично: число 1/5 как объект существует в единственном экземпляре)
Было бы логично, только в том то и фишка, что возвращаются разные.
Обратите внимание, что 1/5 не равна 1/5 в Smalltalk, несмотря на то, что в данном случае это рациональные дроби, а не десятичные.
А в других случаях WAT просто исключен, да?
Нет, конечно. Какие-то WAT есть практически в любом ЯП, но больше всего их от сайд-эффектов и от неявного приведения типов.

ключевые слова: «позднее связывание»
Так позднее связывание сейчас очень во многих ЯП есть. И возможность «обработать сообщение, для которого у объекта метода нет вообще». Точнее метод то есть, он просто работает как fallback, в Ruby он method_missing называется. А в целом это обычный dynamic dispatch.
Я ожидал от Smalltalk гораздо большего в этом плане…
Согласен. В статье вместо демонстрации сильных сторон Clojure, идёт сравнение тёплого (Selenium WebDriver) с мягким (Selenium IDE). Ну а REPL'ом давно уже никого не удивить )))
Код теста в примере пока что выглядит как-будто его с Java 1-в-1 переписали, не говоря уж о том, что запуск браузера, логирование и подсчёт результатов — это не обязанность конкретного теста.
На мой вкус этот тест (без потери функционала) должен выглядеть примерно так:
(selenium-test gosuslugi-main-search-form "https://www.gosuslugi.ru/"
    (->> ($ ".index-slider-search input")
         (type-text "загранпаспорт")
         (->>keys Keys/ARROW_DOWN)
         (->>keys Keys/ARROW_DOWN)
         (->>keys Keys/ENTER))

    (is (there ($ ".title_search"))))
Не, ну это не просто нежелание отвечать на какое-то сообщение, это не желание инстанцироваться так же как концептуально аналогичный класс. Они ещё и сравниваются по значению, а дроби уже по ссылке.
5 == 5. 
"--> true"
(1/5) == (1/5). 
"--> false"
LargePositiveInteger new == LargePositiveInteger new.  
"--> false"


Что вы понимаете под униформностью?
Единообразие, как концептуальное, так и синтаксическое. И как следствие отсутствие неожиданного поведения на уровне языка и стандартной библиотеки.
Хороший пример униформности — Scheme, вот ранее обсуждаемая задачка на Racket Scheme:
(define (sum-squared-up-to n)
  (foldl
    (lambda (x acc) (+ (* x x) acc))
    0
    (range 1 (+ n 1))))

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

Где я говорил про униформность Smalltalk?
Хм, а что мы тогда тут обсуждаем? Насколько я уловил Вашу позицию: «В Elixir униформности нет, не то что в Smalltalk, а как же жить без униформности возможно»
Спасибо, разобрался. Чтобы по-нормальному расширить базовый класс, надо создать свой пакет, потом перейти к определению класса, добавить протокол *ИмяСвоегоПакета и туда уже нужные методы.
А в пакете они появятся автомагически сразу после Accept :-)
Возможно, первым пользователям метода было так удобнее, а потом сработал принцип «здесь так принято»
О том и речь. Когда можно сайд-эффекты замутить над примитивными данными — это рано или поздно приводит к WAT.

Вроде бы очень алогичная вещь: как мы вообще можем писать программы в таких условиях?!? И у вас постоянно проскальзывает это недоумение…
У меня? У меня на эту тему никакого недоумения нет, потому как процессы в Elixir действительно принимают сообщения, и тот, кто посылает сообщение, даже не догадывается какой функцией оно будет обработано и когда (можно задать только таймаут, после которого сообщение теряет актуальность).
А вот в Smalltalk это не так, насколько я понял из PBE. Там посылка сообщения от вызова метода отличается только в теоретическом плане. На практике сообщение обрабатывается одноименным методом и технически от вызова метода это ничем не отличается. Да есть детали реализации, о которых они пишут, типа методы класса-экземпляра. Но фундаментально это ничего не меняет.
Хотя может я что-то недопонял… Буду благодарен, если Вы продемонстрируете практическое отличие сообщений в Pharo от вызова метода в Ruby.

я не выдерживаю данный формат дискуссии — надо куда-то ее переносить.
придётся всё-таки митап по ООП организовывать )))
Поясните.

LargePositiveInteger new. "-> 0"
SmallInteger new. "-> Error: SmallIntegers can only be created by performing arithmetic"

Ну и Formal Specification of the Primitive Methods заодно.
P.S. Чем больше читаю про Smalltalk, тем сильнее у меня ощущение, что Вы рассказывали про какой-то другой ЯП, потому что в этом униформности точно нет. Не то что бы в этом есть что-то плохое, но это факт.
а можно не париться и дать возможность пользователю (разработчику) самому сформировать стратегию и тактику написания надежного кода.
«Не париться» при таком подходе у Вас не получится, ну или надёжный код не получится )
Потому что для надёжности Вам придётся держать в уме детали реализации каждого используемого метода, как бы он там что не изменил.

С методом тоже очевидно: он вернет результат вычисления на базе переданных аргументов и своего состояния, возможно изменив свое состояние :)
Так вот нифига. Я выше уже писал про OrderedCollection>>addAll, который с какого-то перепугу тупо вернул свой аргумент, а не результат вычисления. А на тему «возможно изменив свое состояние», зачем я должен об этом думать, когда можно принципиально разделить эти 2 варианта на уровне языка?

Откройте протокол класса Magnitude или его подкласса и убедитесь, что многие люди считают иначе :)
Ну открыл, вижу что эти люди и с SRP кардинально не согласны :)
А вот примеров объектного поведения я там не вижу. Все эти методы можно было бы распределить в несколько подходящих модулей. И это было бы лучше, чем «свалка» в классе Number.
Думаю, это следствие того, что довольно редко встречаются методы длиннее нескольких строк.
Ну, это в принципе даже плюс. Судя по редактору, типичный метод должен уместиться в 11 строк, включая комментарии.

А даже если и нет — они чем-то сильно мешают?
На самом деле спасают только продвинутые средства навигации по всему этому добру. Хотя я не уверен, что даже они позволяют всегда быстро найти то, что нужно. В общем, на мой субъективный взгляд они явно переборщили с набором методов.

Ну, и многие методы, разумеется, «подгружаются» в классы из пакетов.
А можно пример? Реально интересно посмотреть.
Я-то как раз хочу чтобы «теоретическая красота» работала на практике.
Тогда Вам реально в Elixir.
Я намедни ещё на тему модели акторов читал и наткнулся на прекрасное описание Erlang… в нём очень хорошо сформулировано то, что я Вам пытался донести.
Что касается Elixir, то он добавил в Erlang ещё и метапрограммирование по образцу Lisp. И практически весь его синтаксис построен на макросах времени компиляции.
Вот, например, реализация синтаксиса для записи диапазонов: kernel.ex#L2597..L2638
Я как раз выше спросил: зачем это нужно каждый раз знать?
Мутабельный объект может пройти по цепочке вызовов и измениться где-то глубоко в недрах кода. Хотя вызывающий код может этого совсем не ожидать.
Если же у нас все аргументы функций иммутабельны, то эта проблема отпадает в принципе.

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

Напомню: это int где-нибудь в Java «маскируется» под объект; а числа в Smalltalk-е являются объектами
Неа, даже технически экземпляры SmallInteger в Smalltalk не совсем объекты.
Ну а уж концептуально, числа вообще не могут быть объектами, т.к. не обладают собственным поведением.
Я просто не люблю обсуждать то, что в глаза не видел )))
Возможности IDE впечатляют, хоть сам редактор кода слабоват, зато System Browser, Spotter, Finder (by examples), Debugger реализованы весьма интересно. Думаю, оттуда и дальше будут черпать вдохновение для инструментов разработки под другие языки.
Что касается, стандартной библиотеки, то она выглядит как ActiveSupport на стероидах — десятки тысяч методов, которые в 99.999% случаев вам не понадобятся… и, насколько я понял, без возможности подключить только нужные, потому что они все сразу в базовых классах реализованы, а не добавлены через композицию.
Может не означает обязан. Существует масса объектов, не изменяющих свое состояние.
Вы правы, не обязан. Только в этом и есть когнитивная нагрузка — каждый раз вспоминать:
* какие объекты мутабельны, какие иммутабельны
* на какие сообщения объект вернёт yourself, на какие один из аргументов сообщения, а на какие вернёт новый объект того же типа, что и получатель сообщения

Получается, что на практике никакого единообразия и в помине нет. Хотя мы ещё даже не добрались до сообщений, результат обработки которых нам не нужно ждать. В Smalltalk есть такие? Или поток выполнения всегда ждёт ответа на сообщение?

И я никак не пойму, что же вас не устраивает в числах-объектах?
То, что они только маскируются под объекты.
Так исходный вопрос не я задавал )))
Я дополнил своим уточнением…
А то, что объект может изменить свое состояние, получив сообщение, вроде бы и так очевидно?
Это да. И это показывает, что числа — это не совсем объекты даже в Smalltalk. На самом деле закос под объекты очень сильно увеличивает когнитивную нагрузку при программировании.
Для сравнения, в Elixir акторы при получении сообщения могут менять своё состоянии. А данные — наоборот всегда неизменны.

P.S. Ну а в ответ на сообщение addAll вернуть аргумент — это вообще саботаж )))
Вы за примерами кода пропустили текст комментария:
«Но не в обоих случаях d равно первоначальному значению a.»
Вот, кстати, я и на первые грабли Smalltalk наступил:
«Take care that addAll: also returns its argument, and not the receiver!»
С чего это вдруг?

Исправленная версия:
|a b c d|
a := 1.
b := 2.
d := a.
c := a + b.
c. "-> 3"
d. "-> 1"

|a b c d|
a := OrderedCollection newFrom: #(1).
b := OrderedCollection newFrom: #(2).
d := a.
c := a addAll: b; yourself.
c. "-> an OrderedCollection(1 2)"
d. "-> an OrderedCollection(1 2)"
Но не в обоих случаях d равно первоначальному значению a.

|a b c d|
a := 1.
b := 2.
d := a.
c := a + b.
d. "-> 1"

|a b c d|
a := OrderedCollection newFrom: #(1).
b := OrderedCollection newFrom: #(2).
d := a.
c := a addAll: b.
d. "-> an OrderedCollection(1 2)"

Информация

В рейтинге
2 963-й
Откуда
Россия
Работает в
Зарегистрирован
Активность