гарантирует второе (отсутствие неожиданного поведения)? В программах на Scheme не бывает багов?
А как отсутствие неожиданного поведения связано с отсутствием багов? С неожиданным поведением только часть багов связана, причём чаще меньшая, т.к. программисты привыкают к «особенностям» используемого языка.
Функция — это тоже список? Да, она задается списком, но это же объект другого рода?
Определение функции — это список с вызовом функции define. Да, разумеется, есть с десяток функций, которые реализуются внутри интерпретатора, но для программиста они ведут они себя точно так же, как и все остальные функции.
тут тоже есть примитивные конструкции, которые не совсем соответствуют исходной концепции?
да вроде нет, можно пример?
Ваша позиция понятна, но всё-таки заметно что Вы ей следуете, исходя из того, что неравнодушны к Smalltalk. Это создаёт некоторую предвзятость.
Концепция, в которую вовлечено два компонента (объект и сообщение) скорее всего проще, чем та, в которой присутствует большее количество.
Вот это не факт. Простота применения ЯП на практике с количеством компонентов в концепции слабо коррелирует.
Есть, например, концепция с нулём компонент — «всё есть ничто» и даже соответствующий ЯП. А вот решить на нём практическую задачу совсем не просто :-D
по моим ощущениям ООП не менее мощная, чем ФП, так как в объектах можно смоделировать любую концепцию ФП
OMFG, зачем? Эти парадигмы совершенно ортогональны. Моя позиция в том, что они должны применяться комплементарно на разных уровнях абстракции. ООП можно применять и в Erlang/Elixir и в диалектах Lisp, но там, где оно реально имеет смысл и пользу. А протаскивание идей ООП через все уровни абстракции вплоть до самых примитивных типов ведёт к излишнему и неоправданному усложнению системы.
Собственно изначальная статья посвящена тому, что «Здравствуй ФП» не обозначает «Прощай ООП», оно означает «Здравствуй нормальное ООП, теперь ты будешь работать для меня, а не наоборот»
Просто в одном случае (с дробями) в ответ на сообщения возвращается один и тот же объект (что логично: число 1/5 как объект существует в единственном экземпляре)
Было бы логично, только в том то и фишка, что возвращаются разные.
Обратите внимание, что 1/5 не равна 1/5 в Smalltalk, несмотря на то, что в данном случае это рациональные дроби, а не десятичные.
Нет, конечно. Какие-то WAT есть практически в любом ЯП, но больше всего их от сайд-эффектов и от неявного приведения типов.
ключевые слова: «позднее связывание»
Так позднее связывание сейчас очень во многих ЯП есть. И возможность «обработать сообщение, для которого у объекта метода нет вообще». Точнее метод то есть, он просто работает как fallback, в Ruby он method_missing называется. А в целом это обычный dynamic dispatch.
Я ожидал от Smalltalk гораздо большего в этом плане…
Согласен. В статье вместо демонстрации сильных сторон Clojure, идёт сравнение тёплого (Selenium WebDriver) с мягким (Selenium IDE). Ну а REPL'ом давно уже никого не удивить )))
Код теста в примере пока что выглядит как-будто его с Java 1-в-1 переписали, не говоря уж о том, что запуск браузера, логирование и подсчёт результатов — это не обязанность конкретного теста.
На мой вкус этот тест (без потери функционала) должен выглядеть примерно так:
Не, ну это не просто нежелание отвечать на какое-то сообщение, это не желание инстанцироваться так же как концептуально аналогичный класс. Они ещё и сравниваются по значению, а дроби уже по ссылке.
Единообразие, как концептуальное, так и синтаксическое. И как следствие отсутствие неожиданного поведения на уровне языка и стандартной библиотеки.
Хороший пример униформности — 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 вернуть аргумент — это вообще саботаж )))
Вот, кстати, я и на первые грабли 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)"
В остальном согласен, действительно весьма прогрессивная платформа разработки.
Определение функции — это список с вызовом функции define. Да, разумеется, есть с десяток функций, которые реализуются внутри интерпретатора, но для программиста они ведут они себя точно так же, как и все остальные функции.
да вроде нет, можно пример?
Ваша позиция понятна, но всё-таки заметно что Вы ей следуете, исходя из того, что неравнодушны к Smalltalk. Это создаёт некоторую предвзятость.
Вот это не факт. Простота применения ЯП на практике с количеством компонентов в концепции слабо коррелирует.
Есть, например, концепция с нулём компонент — «всё есть ничто» и даже соответствующий ЯП. А вот решить на нём практическую задачу совсем не просто :-D
OMFG, зачем? Эти парадигмы совершенно ортогональны. Моя позиция в том, что они должны применяться комплементарно на разных уровнях абстракции. ООП можно применять и в Erlang/Elixir и в диалектах Lisp, но там, где оно реально имеет смысл и пользу. А протаскивание идей ООП через все уровни абстракции вплоть до самых примитивных типов ведёт к излишнему и неоправданному усложнению системы.
Собственно изначальная статья посвящена тому, что «Здравствуй ФП» не обозначает «Прощай ООП», оно означает «Здравствуй нормальное ООП, теперь ты будешь работать для меня, а не наоборот»
Обратите внимание, что 1/5 не равна 1/5 в Smalltalk, несмотря на то, что в данном случае это рациональные дроби, а не десятичные.
Так позднее связывание сейчас очень во многих ЯП есть. И возможность «обработать сообщение, для которого у объекта метода нет вообще». Точнее метод то есть, он просто работает как fallback, в Ruby он method_missing называется. А в целом это обычный dynamic dispatch.
Я ожидал от Smalltalk гораздо большего в этом плане…
Код теста в примере пока что выглядит как-будто его с Java 1-в-1 переписали, не говоря уж о том, что запуск браузера, логирование и подсчёт результатов — это не обязанность конкретного теста.
На мой вкус этот тест (без потери функционала) должен выглядеть примерно так:
Единообразие, как концептуальное, так и синтаксическое. И как следствие отсутствие неожиданного поведения на уровне языка и стандартной библиотеки.
Хороший пример униформности — Scheme, вот ранее обсуждаемая задачка на Racket Scheme:
Синтаксис абсолютно однороден. Концепция чистая, даже без сахара. Всё есть список. Первый элемент списка — имя вызываемой функции, остальные — аргументы для неё.
Хм, а что мы тогда тут обсуждаем? Насколько я уловил Вашу позицию: «В Elixir униформности нет, не то что в Smalltalk, а как же жить без униформности возможно»
А в пакете они появятся автомагически сразу после Accept :-)
У меня? У меня на эту тему никакого недоумения нет, потому как процессы в Elixir действительно принимают сообщения, и тот, кто посылает сообщение, даже не догадывается какой функцией оно будет обработано и когда (можно задать только таймаут, после которого сообщение теряет актуальность).
А вот в Smalltalk это не так, насколько я понял из PBE. Там посылка сообщения от вызова метода отличается только в теоретическом плане. На практике сообщение обрабатывается одноименным методом и технически от вызова метода это ничем не отличается. Да есть детали реализации, о которых они пишут, типа методы класса-экземпляра. Но фундаментально это ничего не меняет.
Хотя может я что-то недопонял… Буду благодарен, если Вы продемонстрируете практическое отличие сообщений в Pharo от вызова метода в Ruby.
придётся всё-таки митап по ООП организовывать )))
Ну и Formal Specification of the Primitive Methods заодно.
P.S. Чем больше читаю про Smalltalk, тем сильнее у меня ощущение, что Вы рассказывали про какой-то другой ЯП, потому что в этом униформности точно нет. Не то что бы в этом есть что-то плохое, но это факт.
Потому что для надёжности Вам придётся держать в уме детали реализации каждого используемого метода, как бы он там что не изменил.
Так вот нифига. Я выше уже писал про OrderedCollection>>addAll, который с какого-то перепугу тупо вернул свой аргумент, а не результат вычисления. А на тему «возможно изменив свое состояние», зачем я должен об этом думать, когда можно принципиально разделить эти 2 варианта на уровне языка?
Ну открыл, вижу что эти люди и с SRP кардинально не согласны :)
А вот примеров объектного поведения я там не вижу. Все эти методы можно было бы распределить в несколько подходящих модулей. И это было бы лучше, чем «свалка» в классе Number.
На самом деле спасают только продвинутые средства навигации по всему этому добру. Хотя я не уверен, что даже они позволяют всегда быстро найти то, что нужно. В общем, на мой субъективный взгляд они явно переборщили с набором методов.
А можно пример? Реально интересно посмотреть.
Я намедни ещё на тему модели акторов читал и наткнулся на прекрасное описание Erlang… в нём очень хорошо сформулировано то, что я Вам пытался донести.
Что касается Elixir, то он добавил в Erlang ещё и метапрограммирование по образцу Lisp. И практически весь его синтаксис построен на макросах времени компиляции.
Вот, например, реализация синтаксиса для записи диапазонов: kernel.ex#L2597..L2638
Если же у нас все аргументы функций иммутабельны, то эта проблема отпадает в принципе.
С функциями всегда очевидно, что она вернёт результат вычисления на базе переданных аргументов.
Неа, даже технически экземпляры SmallInteger в Smalltalk не совсем объекты.
Ну а уж концептуально, числа вообще не могут быть объектами, т.к. не обладают собственным поведением.
Возможности IDE впечатляют, хоть сам редактор кода слабоват, зато System Browser, Spotter, Finder (by examples), Debugger реализованы весьма интересно. Думаю, оттуда и дальше будут черпать вдохновение для инструментов разработки под другие языки.
Что касается, стандартной библиотеки, то она выглядит как ActiveSupport на стероидах — десятки тысяч методов, которые в 99.999% случаев вам не понадобятся… и, насколько я понял, без возможности подключить только нужные, потому что они все сразу в базовых классах реализованы, а не добавлены через композицию.
* какие объекты мутабельны, какие иммутабельны
* на какие сообщения объект вернёт yourself, на какие один из аргументов сообщения, а на какие вернёт новый объект того же типа, что и получатель сообщения
Получается, что на практике никакого единообразия и в помине нет. Хотя мы ещё даже не добрались до сообщений, результат обработки которых нам не нужно ждать. В Smalltalk есть такие? Или поток выполнения всегда ждёт ответа на сообщение?
То, что они только маскируются под объекты.
Я дополнил своим уточнением…
Это да. И это показывает, что числа — это не совсем объекты даже в Smalltalk. На самом деле закос под объекты очень сильно увеличивает когнитивную нагрузку при программировании.
Для сравнения, в Elixir акторы при получении сообщения могут менять своё состоянии. А данные — наоборот всегда неизменны.
P.S. Ну а в ответ на сообщение addAll вернуть аргумент — это вообще саботаж )))
«Но не в обоих случаях d равно первоначальному значению a.»
«Take care that addAll: also returns its argument, and not the receiver!»
С чего это вдруг?
Исправленная версия: