Elixir: Как выглядит ООП в функциональном языке?

    В последнее время участились статьи и обсуждения на тему прощания с ООП и поиски смысла, который Алан Кэй изначально вкладывал в это понятие.

    Несколько высказываний Кэя для тех, кто пропустил
    I made up the term “object-oriented”, and I can tell you I didn't have C++ in mind

    OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

    I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “messaging”.

    The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be.

    Late binding allows ideas learned late in project development to be reformulated into the project with exponentially less effort than traditional early binding systems (C, C++, Java, etc.)

    I’m not against types, but I don’t know of any type systems that aren’t a complete pain, so I still like dynamic typing.

    В связи с этими обсуждениями, часто всплывает мысль о том, что Erlang/Elixir очень хорошо удовлетворяют критериям, которые Кэй предъявлял к понятию «объектно-ориентированный». Но далеко не все знакомы с этими языками, поэтому возникает непонимание как функциональные языки могут быть более объектно-ориентированными, чем популярные C++, Java, C#.

    В этой статье я хочу на простом примере с exercism.io показать как выглядит ООП на Elixir.

    Описание задачи
    Напишите небольшую программу, который хранит имена школьников, сгруппированные по номеру класса, в котором они учатся.

    В конце концов, вы должны быть в состоянии:

    • Добавить имя школьника в класс
    • Получить список всех школьников, обучающихся в классе
    • Получить отсортированный список всех учащихся во всех классах. Классы должны быть отсортированы по возрастанию (1, 2, 3 и т.д.), а имена школьников — по алфавиту.


    Начнём с тестов, чтобы посмотреть как будет выглядеть код, вызывающий наши функции. Взглянем на тесты, которые Exercism подготовил для Ruby, в котором ООП дошло до того, что даже операторы — это чьи-то методы.

    И напишем аналогичные тесты для Elixir-версии этой программы:
    Code.load_file("school.exs")
    ExUnit.start
    
    defmodule SchoolTest do
      use ExUnit.Case, async: true
      import School, only: [add_student: 3, students_by_grade: 1, students_by_grade: 2]
    
      test "get students in a non existant grade" do
        school = School.new
        assert [] == school |> students_by_grade(5)
      end
    
      test "add student" do
        school = School.new
        school |> add_student("Aimee", 2)
    
        assert ["Aimee"] == school |> students_by_grade(2)
      end
    
      test "add students to different grades" do
        school = School.new
        school |> add_student("Aimee", 3)
        school |> add_student("Beemee", 7)
    
        assert ["Aimee"] == school |> students_by_grade(3)
        assert ["Beemee"] == school |> students_by_grade(7)
      end
    
      test "grade with multiple students" do
        school = School.new
        grade = 6
        students = ~w(Aimee Beemee Ceemee)
        students |> Enum.each(fn(student) -> school |> add_student(student, grade) end)
        
        assert students == school |> students_by_grade(grade)
      end
    
      test "grade with multiple students sorts correctly" do
        school = School.new
        grade = 6
        students = ~w(Beemee Aimee Ceemee)
        students |> Enum.each(fn(student) -> school |> add_student(student, grade) end)
        
        assert Enum.sort(students) == school |> students_by_grade(grade)
      end
    
      test "empty students by grade" do
        school = School.new
        assert [] == school |> students_by_grade
      end
    
      test "students_by_grade with one grade" do
        school = School.new
        grade = 6
        students = ~w(Beemee Aimee Ceemee)
        students |> Enum.each(fn(student) -> school |> add_student(student, grade) end)
        
        assert [[grade: 6, students: Enum.sort(students)]] == school |> students_by_grade
      end
    
      test "students_by_grade with different grades" do
        school = School.new
        everyone |> Enum.each(fn([grade: grade, students: students]) ->
          students |> Enum.each(fn(student) -> school |> add_student(student, grade) end)
        end)
    
        assert everyone_sorted == school |> students_by_grade
      end
    
      defp everyone do
        [
          [ grade: 3, students: ~w(Deemee Eeemee) ],
          [ grade: 1, students: ~w(Effmee Geemee) ],
          [ grade: 2, students: ~w(Aimee Beemee Ceemee) ]
        ]
      end
    
      defp everyone_sorted do
        [
          [ grade: 1, students: ~w(Effmee Geemee) ],
          [ grade: 2, students: ~w(Aimee Beemee Ceemee) ],
          [ grade: 3, students: ~w(Deemee Eeemee) ]
        ]
      end
    end
    

    Если не вдаваться в тонкости написания тестов, то нас больше всего интересуют получившиеся «методы» для работы с «классом» School:

        school = School.new
        school |> add_student("Aimee", 2) # => :ok
        school |> students_by_grade(2) # => ["Aimee"]
        school |> students_by_grade # => [[grade: 2, students: ["Aimee"]]]
    

    Конечно, на самом деле, тут нет ни класса, ни методов, но об этом чуть позже. А пока обратите внимание, насколько это похоже на работу с экземпляром класса в рамках привычных реализаций ООП. Только вместо точки или стрелочки -> используется pipe-оператор |>.

    Хотя сразу признаюсь, для того, чтобы вызвать одну функцию, pipe-оператор обычно не используют, да и сами функции размещаются в модулях, а не в классах. Поэтому более идиоматической записью для Elixir будет:

        school = School.new
        School.add_student(school, "Aimee", 2) # => :ok
        School.students_by_grade(school, 2) # => ["Aimee"]
        School.students_by_grade(school) # => [[grade: 2, students: ["Aimee"]]]
    

    Однако суть от смены синтаксиса не меняется! Идентификатор «объекта» просто передаётся в качестве первого аргумента во все функции. Но ведь в функциональных языках нет объектов. Давайте разберёмся, что же тут происходит на самом деле…

    Дело в том, что все программы, что на Erlang, что на Elixir, строятся на базе OTP, де-факто это часть стандартной библиотеки языка, обеспечивающая ту самую отказоустойчивость и масштабируемость, которой славится Erlang. OTP включает в себя очень богатый и мощный арсенал модулей и поведений (это типа абстрактных классов). Но сегодня мы поговорим только об одном, но очень часто используемом поведении — GenServer. Оно позволяет превратить обычный модуль в своеобразный генератор акторов (легковесные процессы виртуальной машины Erlang).

    Каждый процесс-актор имеет своё изолированное состояние, которое можно изменять или получать информацию из него, исключительно посредством отправки ему сообщений. Если сообщения поступают конкурентно, то они обрабатываются в порядке очереди, исключая даже теоретическую возможность получить race condition на состоянии, хранимом в процессе. Таким образом каждый процесс ведёт себя с одной стороны подобно серверу — отсюда и название GenServer, а с другой стороны подобно объекту — согласно описанию Кэя.

    Он так же, как объект, имеет состояние и предоставляет возможности работы с этим состоянием при помощи обработки сообщений колбеками handle_call (c возвратом ответа) и handle_cast (без ответа). То самое позднее связывание, о котором постоянно говорит Алан. А за отправку сообщений чаще всего отвечают т.н. API-функции, размещенные в том же модуле, но вызываемые из других процессов.

    Процессы полностью изолированы друг от друга вплоть до того, что падение одного процесса не повлияет ни на какой другой, если вы явно не пропишете как оно должно влиять (т.н. стратегия перезапуска).

    Впрочем, хватит слов. Давайте посмотрим, как это выглядит в коде:

    defmodule School do
      use GenServer
    
      # API
    
      @doc """
      Start School process.
      """
      def new do
        {:ok, pid} = GenServer.start_link(__MODULE__, %{})
        pid
      end
    
      @doc """
      Add a student to a particular grade in school.
      """
      def add_student(pid, name, grade) do
        GenServer.cast(pid, {:add, name, grade})
      end
    
      @doc """
      Return the names of the students in a particular grade.
      """
      def students_by_grade(pid, grade) do
        GenServer.call(pid, {:students_by_grade, grade})
      end
    
      @doc """
      Return the names of the all students separated by grade.
      """
      def students_by_grade(pid) do
        GenServer.call(pid, :all_students)
      end
    
      # Callbacks
    
      def handle_cast({:add, name, grade}, state) do
        state = Map.update(state, grade, [name], &([name|&1]))
        {:noreply, state}
      end
    
      def handle_call({:students_by_grade, grade}, _from, state) do
        students = Map.get(state, grade, []) |> Enum.sort
        {:reply, students, state}
      end
    
      def handle_call(:all_students, _from, state) do
        all_students = state
          |> Map.keys
          |> Enum.map(fn(grade) ->
            [grade: grade, students: get_students_by_grade(state, grade)]
          end)
    
        {:reply, all_students, state}
      end
    
      # Private functions
    
      defp get_students_by_grade(state, grade) do
        Map.get(state, grade, []) |> Enum.sort
      end
    end

    Как правило, модуль, реализующий поведение GenServer, делится на 3 части:

    • API — функции для взаимодействия с процессом извне, они вызывают функции модуля GenServer для посылки сообщений, старта/остановки процесса и т.д. А также скрывают от вызывающего кода детали реализации
    • Callbacks — функции, реализующие поведение GenServer: обработка сообщений и т.п.
    • Private functions — вспомогательные функции, которые используются внутри модуля

    При старте процесса мы получаем его идентификатор — pid, который можно потом передавать в качестве первого аргумента API-функциям. Обычно процессы стартуются функцией start_link, это соглашение позволяет удобно описывать целые деревья процессов, которые запускаются одной командой, но тут (для упрощения аналогий) я назвал её new.

    Если у вас в системе есть какой-то system-wide процесс, для которого достаточно одного экземпляра, то можно дать ему имя. В этом случае вы можете обойтись без передачи pid в API-функции, т.к. они смогут отправлять сообщения процессу по имени.

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

    P.S. Таким образом, Elixir позволяет вам применять ООП там, где оно действительно работает, — на верхнем уровне проектирования системы. И при этом не усложнять нижние уровни системы надуманными абстракциями и контрпродуктивными тезисами типа «Всё есть объект».
    Share post

    Comments 410

      0
      school = School.new
      school |> add_student("Aimee", 2)

      Такой код вернет undefined function add_student/3, разве нет? Эликсир ведь не знает, что add_student находится в модуле School?

        +1
        В коде теста (на 6-й строке) явно импортируются используемые функции, поэтому доступ к ним возможен без указания имени модуля:
        import School, only: [add_student: 3, students_by_grade: 1, students_by_grade: 2]
        
        +1
        Если self-параметр передается первым, то пайпы работают не за счет каррирования?
        Вот, например, функция students_by_grade. Первый параметр — школа, второй — оценка. Но функция передается в пайп с оценкой, т. е. вторым параметром.
        Специальный хак для первого параметра? Частичное применение задом-наперед?
          0
          Пайпы — это не каррирование, а синтаксический сахар для инверсии записи вложенных функций.
          c(b(a))) 
          # эквивалентно
          a |> b |> c
          
          c(b(a1, a2), b2, b3)) 
          # эквивалентно
          a1 |> b(a2) |> c(b2, b3)
          
            0
            Я не говорил, что они каррирование, а имел в виду, что привык, что в ML-семействе пайпы обычно определяются примерно так:

            f |> g = g f
            


            И, соответственно, нет никаких специальных правил про первый аргумент и прочее. Впрочем, это не мешает эргономике — просто self-аргумент последний, а не первый.

            Не подкинете ссылок на какие-нибудь заметки о дизайне Elixir? Интересно, почему авторы решили отойти от проторенной дорожки.
              0
              Я думаю, это в-первую очередь связано с тем, что Elixir поддерживает опциональные параметры.
              Ну и как по мне, это логичнее, когда, допустим, функции для работы со списками принимают список в качестве первого аргумента, а не последнего.
              Заметок о дизайне на эту тему я не встречал, но можно почитать в дискуссии в google-группе, например эту.
          –1
          Вообще делать для каждого объекта свой gen_server и как-то с этим работать — это не гуд. Почитайте про ADT ( https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%B5%D0%B1%D1%80%D0%B0%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B9_%D1%82%D0%B8%D0%BF_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85 ).

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

          Нотации для записи вообще не имеют значения:
          что ты напишешь: a->add_something(value)
          что add_something(a, value)
          Всё это дело вкуса.

          Другой вопрос. Многие говорят о вреде глобального состояния (всякие глобальные переменные и т.п.).
          Но почему-то никто не поднимает вопрос, о вреде глобального состояния в контексте объекта.
          Когда с помощью прямых присваиваний типа:
          this->my_property = 1 мы по сути изменяем глобальное свойство в объекте.
          Т.о. когда внутри метода встречаются такого рода вызовы, то метод априоры не может быть чистым. А это уже как раз к вопросу «о глобальных состояниях».
          Чистый метод (функция) всегда берет объект и возвращает новый.

            +1
            Но почему-то никто не поднимает вопрос, о вреде глобального состояния в контексте объекта.

            Может быть, потому что это не глобальное состояние, а локальное?

              0
              Я имею ввиду глобальное по отношению к конкретному методу объекта.
                0

                Проблема в том, что вы таким образом лишаетесь возможности различать "истинно глобальное" состояние — то, которое глобально для всей системы и "слегка глобальное" — которое на самом деле локально для объекта. Учитывая, что локального для метода состояния, считай, и нет (бессмысленно считать незамкнутые переменные состоянием), то это вообще теряем смысл.


                С другой же стороны, если лишить объекты mutable-состояния, то вам придется нарушать инкапсуляцию: каждый метод должен будет оповещать всех его потребителей о том, что он изменил состояние, теперь они должны иметь дело с новым объектом (кстати, а что делать тем, у кого осталась старая ссылка?). И, что веселее, это распространяется по каскаду вверх: у пользователей объекта тоже меняется состояние, об этом тоже надо оповещать, и так до корня. Очень весело. И это мы еще не касались вопросов производительности.


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


                При всем при этом я отдаю должно уважение иммутабельным объектам. Просто они не везде удобны.

                  0
                  Я всего лишь хотел донести мысль что использование актора для всех «объектов», ну очень не хорошая идея.
                  Акторы нужны там, где есть действительно объект который живет своей жизнью.

                  Пример:
                  Вот есть у вас некая ORM. Эта ORM возвращает записи о школьниках. В контексте статьи описывается принцип когда каждый школьник — актор. Но в данном случае, школьник лишь некая запись, с которой можно работать как с объектом, любая функция которая изменяет внутренее состояние школьника, возвращает новый «объект» школьника. Если у вас в системе живет сессия этого школьника, то да, состояние этого актора может включать «объект» школьника для учета внутреннего состояния сессии, но это не значит что каждый «объект» следует делать актором — это вредно.
                    +1
                    В контексте статьи описывается принцип когда каждый школьник — актор.

                    Это где, простите? В статье описана ситуация, когда актор — это реестр школьников.


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

                    Зачем? (не говоря уже о том, что это не "как с объектом" в моем понимании)

                      0
                      школьник лишь некая запись, с которой можно работать как с объектом, любая функция которая изменяет внутренее состояние школьника, возвращает новый «объект» школьника.
                      В Elixir для этого есть структуры, которыми и оперируют «ORM» типа Ecto. В Erlang для этого же есть записи.
                      И как правильно заметил lair в статье нет никакого призыва использовать акторы вместо структур. Зато есть призыв с точностью до наоборот, не тащить ООП на уровень, где оно не нужно. В вашем примере функции будут работать со структурой Школьник, как со структурой, а не как с объектом. Грубо говоря, примут на вход одну структуру, а на выход вернут совсем другую.
                        0
                        Да, и этого я добиваюсь.
                        Чтобы сначала была имплементация модуля для такой структуры с основными операциями, а уже потом gen_server должен содержать минимальный код для приема собщений и вызова соотвествующих операций с данной структурой.
                          +1
                          Так а разве кто-то с этим спорит? В статье вместо структуры Student вообще используется просто строка для упрощения примера. Но от замены на структуру код вообще не изменится, только в паре мест name на student заменить надо будет.
                          Ну и вызывающий код незначительно поменяется:
                          school |> add_student(%Student{name: "Aimee"}, 2)
                          
                0
                Вы неправильно поняли посыл статьи. Не надо всё делать объектами… Это ошибка проектирования. На нижнем уровне объекты только мешают, там гораздо удобнее работать с примитивными типами данных и структурами. В мейнстрим реализациях ООП начинается с идеи переноса объектов реального в программирование, а в итоге сводится к имитации, что строка общается с числами и т.п.

                По поводу состояния, в Elixir оно надёжно спрятано в процессе, никакой рефлексией и уж тем более прямыми присваиваниями до него не достать. Только отправкой сообщений можно опосредованно влиять на состояние. Ну и как в любом функциональном языке все данные неизменяемы. Т.о. ситуация, когда вы получили часть состояния объекта по ссылке, передали в другой метод, а он взял его и изменил, тоже невозможны by design.
                0

                Что будет, если я сделаю вот так?


                pid = Airplane.new
                School.add_student(pid, "Aimee", 2)
                  0
                  Процесс с этим pid упадёт с ошибкой типа
                  (FunctionClauseError) no function clause matching in Airplane.handle_cast/2
                  airplane.ex:38: Airplane.handle_cast({:add, "Aimee", 2}, %{})

                  Вообще Erlang и Elixir стимулирует разработку по принципу «Let it crash». Поэтому чуть что не так — процесс падает и перезапускается с чистого листа одним из супервизоров.
                    0

                    То есть я правильно понимаю, что (а) это ошибка периода выполнения (а не компиляции/статического анализа) и (б) падает не вызывающий процесс, а вызываемый?

                      0

                      Теоретически такое можно отследить каким-нибудь статическим анализом, но я не встречал реализации пока что. И такие ошибки надо отлавливать в тестах.


                      А падает в данном случае зацикленный GenServer. Если add_student внутри использует cast — вызывающий процесс ничего не заметит. Если call — зависит от реализации, но если вы не отлавливаете ошибки — упадёт вызывающий процесс тоже.


                      В любом случае, скорее всего у вас должен быть супервизор который перезапустит или один или оба генсервера.

                        0
                        Да, всё верно. Предельно позднее связывание в действии :-)
                        Если Вы хотите, чтобы процесс не падал в таких ситуациях, можно определить catch-all обработчики сообщений, которые просто проигнорируют «левые» сообщения. Но с учётом того, что подобное может произойти только из-за ошибки/опечатки в коде, лучше пусть падает.
                    0

                    Если вы решите делать такое в реальном коде — функция new не приветствуется.


                    {:ok, school} = School.start_link
                    school |> School.add_student("Aimee", 2) # => :ok
                    school |> School.students_by_grade(2) # => ["Aimee"]
                    school |> School.students_by_grade # => [[grade: 2, students: ["Aimee"]]]
                      0
                      Об этом написано в статье. В четвёртом с конца абзаце.
                        0

                        В принципе да, но мой комментарий не для автора статьи, а для читателей — ещё раз акцентирую внимание и представляю "правильный" кусок кода.

                      –1
                      Таким образом каждый процесс ведёт себя с одной стороны подобно серверу — отсюда и название GenServer, а с другой стороны подобно объекту — согласно описанию Кэя.
                      <…>
                      Таким образом, Elixir позволяет вам применять ООП там, где оно действительно работает, — на верхнем уровне проектирования системы. И при этом не усложнять нижние уровни системы надуманными абстракциями и контрпродуктивными тезисами типа «Всё есть объект».

                      Посыл понятен (в части второй цитаты), но тогда не надо сюда привлекать Кэя, который первым пунктом почему-то (видимо, по наивности своей думал о простоте) поставил именно «надуманный и контр-продуктивный» принцип «все есть объект». А то как-то прям не честно получается :)
                        +4
                        Почему не честно? На уровне архитектуры всё и остаётся объектами, а как там эти объекты внутри реализованы уже другой вопрос. Контрпродуктивным этот тезис стал, когда спустился на уровень примитивных типов и простых структур данных. Там он никогда простоте не служил.
                        Как Вам помогает в проектировании системы то, что 1 — это не 1, а экземпляр класса Integer с внутренним состоянием равным 1, к которому никто по идее не должен иметь доступ напрямую?
                          –1
                          Как Вам помогает в проектировании системы то, что 1 — это не 1, а экземпляр класса Integer с внутренним состоянием равным 1, к которому никто по идее не должен иметь доступ напрямую?
                          Помогает тем, что с Integer-объектом я общаюсь ровно так же, как с любым другим объектом в системе — через сообщения. А что там у него внутрях мне как раз безразлично. Система, где есть несколько разнотипных сущностей, при прочих равных будет сложнее системы, где все сущности одинаковы. Вопрос заключается в «прочих равных» — не факт, что там не будет каких-то («контр-продуктивных») потерь…

                          Вообще, хорошо или плохо, когда все единообразно — вопрос отдельный (не по теме статьи, как я понимаю?). Но то, что ваше определение объектности не совпадает с Кэйевским — факт: убран первый пункт. А если убрать этот пункт, то остается, по сути, только позднее связывание (так как из сообщений уходит получатель). А это было в LISP-е (о чем тот же Кэй неоднкратно говорил). Соответственно, речь таки не идет об ООП в исходном «Кэевском» смысле слова. Получается, скорее, некая имитация «антуража» ООП… Похожий на объектно-ориентированный DSL в внутри ФЯП, что ли? Это, впрочем, совсем не означает, что данный подход хуже — это тема отдельного исследования и обсуждения. Просто в IT/программировании и так все не слишком ясно, не стоит вносить дополнительную путаницу, не так ли?
                            0
                            Помогает тем, что с Integer-объектом я общаюсь ровно так же, как с любым другим объектом в системе — через сообщения.
                            Так а Integer-объект то зачем? Был у нас обычный всем понятный числовой литерал, а стал «объект», у которого внутреннее состояние выставлено наружу. Почему никто не пишет 1.getValue()? Почему возникают проблемы с boxing/unboxing? Потому что нет тут никакого единообразия. Потому что число — это число, а не объект. Вот и получается, что в теории хотели упростить, а на практике только усложнили.

                            ваше определение объектности не совпадает с Кэйевским — факт: убран первый пункт.
                            Насколько я понимаю, Вы ссылаетесь на Early History Of Smalltalk. Но там нигде не написано, что список идей для реализации интерпретаторов Smalltalk, является определением ООП. Не всё, что обязательно для Smalltalk, является обязательным для ООП. Иначе мы подменяем общую идею конкретной реализацией.
                              +1
                              Но там нигде не написано, что список идей для реализации интерпретаторов Smalltalk, является определением ООП. Не всё, что обязательно для Smalltalk, является обязательным для ООП

                              Уиии! (И да, я с вами согласен)


                              В какой-то момент Стефан Рам заинтересовался этим вопросом, и пристал к Кэю с уточнением. Вот ответ:


                              OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.

                              (еще там есть смешная приписка "It can be done in Smalltalk and in LISP")

                                –2
                                … И мы с автором уже разбирали этот ответ и обсуждали детали в «домашнем» чате. Надеюсь, не придется все повторять теперь и здесь :)
                                –1
                                Был у нас обычный всем понятный числовой литерал, а стал «объект», у которого внутреннее состояние выставлено наружу.
                                1. Насчет состояния наружу — вы крепко ошибаетесь. Либо не на те системы смотрите. Фишка как раз в том, что никакого состояния наружу не торчит: объекту можно посылать сообщения и использовать его в качестве аргумента в сообщениях других объектов. А вот вести он себя будет в соответствии со своим значением — как и полагается объекту. Если вам это все не видно на примере целого числа, возьмите другой пример: null или true/false. Реализация их как объектов дала возможность не только сильно упростить синтаксис того же Smalltalk, но и позволяет делать красивые дизайны там, где необъектные аналоги прилично портят картину. Впрочем, это можно увидеть и на примере чисел — почитайте (а лучше попробуйте на практике), как в Smalltalk-е реализовано преобразование между «целыми короткими», «длинными», «очень длинными», дробями и т.д. Если это не удобно и элегантно, тогда что?
                                2. Литералом же (в объектной системе, кстати, тоже объектом) 1 будет являться до момента компиляция (в зависимости от реализации, может еще таковым стать в момент представления пользователю) — это уже не зависит от языка и парадигмы (см. определение термина).
                                3. Ну, и насчет «обычный» и «всем понятный… В объектной системе получателями и аргументами сообщения являются объекты (а что еще?). И если у вас все объекты — тут как раз все понятно и обычно. Но вот как только у вас появляются не-объекты, все сразу становится сложнее: надо придумать отдельный(-ые) механизм(-ы) для работы с такими сущностями и передачи таких сущностей.


                                Но там нигде не написано, что список идей для реализации интерпретаторов Smalltalk, является определением ООП.
                                Это уже не раз обсуждалось — в том числе совсем недавно на Хабре. Разумеется, каждый может выискивать нужные ему нюансы и придираться к словам. Можно разделить понятия „объект“ и „объектно-ориентированное программирование“. Можно утверждать, что если человек, работал над некоторой идеей (название для которой он и придумал) и получил некий результат, то этот результат совсем не отражает эту самую его идею. Но если этого не делать, то слова The first three principles are what objects „are about“—how they are seen and used from „the outside“ и The last three—objects from the inside—were tinkered with in every version of Smalltalk (and in subsequent OOP designs) четко отделяют принцип от реализации: первое — что такое объекты вообще, второе — как мы их реализовали в нашей системе. Обратите также внимание на то, что когда мы пытаемся определить нечто (в данном случае термин „объект“), мы едва ли будем говорить „все является этим нечто (объектом)“. Эта фраза явно относится к чему-то большему… В общем, надо сильно постараться, чтобы, прочитав данную книгу, заявить, что это не принципы ООП.
                                Ну, и, наконец, есть аргумент „от здравого смысла“ — я его изложил выше. Если убрать данный пункт, окажется, что никакого ООП нет, так как все уже было до этого. Но тогда и смысла в статье нет.
                                  –1
                                  Насчет состояния наружу — вы крепко ошибаетесь.
                                  Зачем Вы спорите с очевидными вещами? То, что внутреннее состояние 1 равно 1, видно без каких-либо сообщений. То же самое с null, true и false.

                                  Вообще идея и реализация — это всегда разные вещи. Реализация может демонстрировать идею, но не ограничивать. Да и есть цепляться за эти 6 пунктов, то они не выполняются вообще нигде, даже в Smalltalk:
                                  Apparently, «everything isn't an object» afterall — some things are primitives.
                                  © Heart Of Smalltalk

                                  все уже было до этого
                                  Ничто не ново под Луною…
                                    –1
                                    Зачем Вы спорите с очевидными вещами? То, что внутреннее состояние 1 равно 1, видно без каких-либо сообщений.
                                    Жаль, что для вас это «очевидно».
                                    Отступление
                                    Как-то мы пытались объяснить человеку необходимость использования алгоритма определения попадания точки внутрь многоугольника; он не соглашался: это же очевидно — либо внутри, либо снаружи!
                                    Чтобы «увидеть» это самое внутреннее состояние, объект надобно спросить (послать сообщение). Чтобы сравнить с другим объектом — надо послать сообщение. Чтобы увеличить… А вот увеличить нельзя! :)

                                    Так, вы с чем спорите-то? Что ООП подразумевает, что все является объектом? Хорошо, тогда включите в определение ООП принципы взаимодействия объектов с не-объектами. Посмотрим, что за определение получится.

                                    Да и есть цепляться за эти 6 пунктов, то они не выполняются вообще нигде, даже в Smalltalk
                                    Да, рано или поздно мы дойдем до электронов, которые объектами не являются. И сообщения рано или поздно превратятся в вызовы или команды перехода в процессоре. Это повод отказаться от всех принципов концепции?
                                      +2
                                      ООП относится к верхнему уровню абстракции в системе, именно на этом уровне всё является объектом… Что там внутри объектов происходит ООП не описывает и не интересуется, потому что объект действует как чёрный ящик, принимающий и отсылающий сообщения. Всё! Дальше уже идут ненужные фантазии на тему ООП внутри объектов. Фантазий на эту тему достаточно много, но все они провальные и приводящие к усложнению простых вещей. И именно от них Алан постоянно пытается откреститься )))
                                        0
                                        Где проходит черта между «верхним» и «нижним» уровнем абстракции?
                                          0
                                          Примерно там же, где между API и его реализацией )))
                                          Обработчик сообщения, в принципе, может обращаться к другим объектам, но не обязан. А когда вы запихиваете ООП на нижние уровни, то у вас и сообщения — это объекты, состоящие из объектов, и обработчик сообщений обязан работать с другими объектами. Вот именно в этот момент «всё есть объект» уничтожает базовые идеи ООП.
                                            0
                                            Не уходите от ответа. API — это уже сама по себе граница готового объекта (приложения, если понимать термин дословно). А вот когда я разработчик, и у меня стоит задача не только это самое API сформировать, но и реализовать. А внутри у меня — каша из чего-то там и объектов. Где мне начинать использовать объекты, а где нет? Как мне не-объекты превратить в объекты и наоборот (когда надо)? Насколько это усложнит мою задачу (значительную часть времени я буду думать не о решении ее, а о там как получить нужные сущности)?

                                            На самом деле, ответ я вам уже предлагал (не здесь): граница проходит по языку. Если я работаю с объектной системой, я хочу с ней общаться на объектном языке. Если я работаю с «функционально-ориентированной» системой, я буду общаться на функциональном языке. Если же я работаю с гетерогенной системой, мне нужно знать несколько языков. Вы считаете, это хорошо?
                                              +3
                                              На самом деле, ответ я вам уже предлагал (не здесь): граница проходит по языку. Если я работаю с объектной системой, я хочу с ней общаться на объектном языке. Если я работаю с «функционально-ориентированной» системой, я буду общаться на функциональном языке. Если же я работаю с гетерогенной системой, мне нужно знать несколько языков. Вы считаете, это хорошо?

                                              Это плохо, но не потому, что вам надо знать несколько языков, а потому, что вы считаете, что с системой надо общаться на том языке, на котором она написана — что противоречит банальному сокрытию информации. А в реальности с системой надо общаться с помощью того API, которое она предоставляет — и, что любопытно, многие системы, имеющие внутри себя сплошную функциональщину, снаружи выглядят вполне объектно.

                                                0
                                                Так, в соответствии с принципом «не все есть объект», API получается разнородным. И мне надо всегда помнить и себя одергивать: здесь объект и сообщения, а здесь — уже функция и тип (или что-то еще).

                                                Это кстати, даже в синтаксисе языков прекрасно видно, хотя никто не замечает так как привыкли. Но привычки они не всегда полезны.
                                                  +2
                                                  Так, в соответствии с принципом «не все есть объект», API получается разнородным. И мне надо всегда помнить и себя одергивать: здесь объект и сообщения, а здесь — уже функция и тип (или что-то еще).

                                                  Проблема в том, что вы не можете сделать все API в соответствии с принципом "все есть объект". Соответственно, эта разнородность сохранится — как внутри API, так и между разными API. А вот сделать API вида "сервис — объект, сообщения — не объекты" — легко, причем я не могу себе представить API, которое на это не ложится. А эта "разнородность" (а) минимальна и (б) семантически легко объяснима (поэтому не добавляет понятийной сложности).

                                                    0
                                                    Проблема в том, что вы не можете сделать все API в соответствии с принципом «все есть объект».
                                                    Доказательства?
                                                      +1

                                                      Попробуйте сделать API для физически распределенной системы, где сообщения будут объектами (с настоящим, а не вырожденным, поведением).


                                                      Да, система мало того, что физически распределенная, так еще и ее узлы написаны на разных языках. Типичный случай кровавой ынтырпрайз-ынтыграции.

                                                        0
                                                        Мне ли вам объяснять, что если я попробую и у меня не получится, это все равно не доказывает невозможности построения такой системы в принципе? Так что доказательство остается за вами. Как и пояснение, что такое «объекты с настоящим, а не вырожденным поведением».
                                                          +1
                                                          Мне ли вам объяснять, что если я попробую и у меня не получится, это все равно не доказывает невозможности построения такой системы в принципе?

                                                          Если бы вы попробовали, то, возможно, поняли бы, почему это невозможно.


                                                          Но окей, пойдем с конца.


                                                          пояснение, что такое «объекты с настоящим, а не вырожденным поведением».

                                                          Объект, все поведение которого сводится к хранению-передаче (без трансформации) данных. Оно же Anemic Data Model. Иными словами, это когда все операции, поддерживаемые объектом, сводятся к парам setX/getX, где X — какое-то свойство объекта.


                                                          А теперь собственно мыслительный процесс.


                                                          Предположим, у нас есть две системы, одна из них — реестр пользователей, вторая — обработчик уведомлений. Пользователь — это сущность, имеющая атрибуты email, firstName, lastName. При создании пользователя в реестре мы уведомляем обработчик о создании такого пользователя (сообщением created(user)). Одно из сконфигуренных уведомлений в обработчике — это отправка емейла пользователю, причем этот емейл должен содержать полное имя пользователя.


                                                          С точки зрения хорошего дизайна, формирование полного имени — это ответственность сущности "пользователь" (потому что мы можем формировать это имя конкатенацией, или хранить отдельно, или делать что-то третье). Но… если реестр и обработчик уведомлений стоят на разных континентах и написаны на разных языках, у нас просто нет способа передать между ними "пользователя", как объект, имеющий поведение — во-первых, технологически сложно реализовать трансляцию поведения между языками, во-вторых, в этот момент у нас станет объект "пользователь" склонируется, что приведет к расхождениям.


                                                          Окей, давайте передавать не "пользователя", а ссылку на объект "пользователь", по которой и реестр, и обработчик смогут послать любое сообщение, и получить ответ. Но вот незадача… ответ на сообщение getFullName — это что? Объект? Тогда мы попали в замкнутый круг. Или это значение-строка? Но тогда мы нарушили униформность.


                                                          В гетерогенных распределенных системах (посмотрите на то же SOA, посмотрите на Enterprise Integration Patterns) сообщения между системами — это не объекты. У них нет поведения, у них есть только данные. И еще к ним не применимо сокрытие: любой агент на пути, будь то маршрутизатор или адаптер или что угодно еще, может полностью прочитать сообщение, изменить его произвольным образом, и переслать дальше. Или не переслать.

                                                            0
                                                            Если отбросить аргумент «электроны — не объекты», то вся проблема сводится к передаче метаинформации в нужный момент времени, нет?

                                                            Когда реестр уведомляет обработчик о появлени нового пользователя, первый передает второму всю необходимую информацию (да, это не объект — см. выше, но это не проблема). А на стороне обработчика из этой информации строят полноценный объект. Так что никто пропажу объекта не замечает.

                                                            Какая именно информация необходима — само собой, «зависит». Как вы сказали, можно передать один идентификатор и сооружать прокси. Можно при желании передать полное состояние и сказать экземпляр какого класса нужно создать — если они «похожи» (тот же String, к примеру). А при отсутствии нужного класса на удаленной стороне используем либо первый вариант (как основной в нашем несовершенном современном мире), либо (если есть техническая возможность — вдруг таки на обеих сторонах умеют говорить на одном языке… или, в случае с гетерогенными системам, появится в будущем) можно спросить «что за класс?» и поделиться его реализацией. В самом общем случае сегодня второй вариант не прокатывает, но это же не означает принципиальную невозможность?

                                                            Я не имел возможности попользоваться WCF, но «по слухам» считал, что там такая схема и реализована, нет? В Smalltalk-ах примерно так работает OpenTalk (VisualWorks). Или вот — удаленная отладка в Pharo. Это я к тому, что отладчик в Smalltalk весьма такая объектная штука.
                                                              +1
                                                              Если отбросить аргумент «электроны — не объекты», то вся проблема сводится к передаче метаинформации в нужный момент времени, нет?

                                                              Нет, конечно.


                                                              Когда реестр уведомляет обработчик о появлени нового пользователя, первый передает второму всю необходимую информацию (да, это не объект — см. выше, но это не проблема).

                                                              Это как раз проблема, потому что это и нарушает принцип "все — объект".


                                                              А на стороне обработчика из этой информации строят полноценный объект. Так что никто пропажу объекта не замечает.

                                                              Вот только в интерфейсе это не объект, а "вся необходимая информация". А что на стороне обработчика — вы и не знаете (потому что инкапсуляция).


                                                              Можно при желании передать полное состояние и сказать экземпляр какого класса нужно создать — если они «похожи» (тот же String, к примеру)

                                                              Вот только сообщение, посланное объекту на стороне получателя (обработчика в моем примере) не будет получено объектом на стороне отправителя (реестра в моем примере). У вас объект раздвоился.


                                                              Это как раз тот момент, когда то, что вы передали — не объект. Его поведение на разных сторонах не униформно.


                                                              И да, как раз это "зависит" и приводит к не-униформности API: вы что-то передаете с поведением, что-то как ссылку, что-то как значение. Намного проще (и униформнее) все передавать как значение.


                                                              А при отсутствии нужного класса на удаленной стороне используем либо первый вариант

                                                              В нашем технически несовершенном мире это (а) очень медленно и (б) просто передает проблему на следующий уровень.


                                                              Я не имел возможности попользоваться WCF, но «по слухам» считал, что там такая схема и реализована, нет?

                                                              Нет. В WCF как раз реализована схема с передачей чистых данных в том или ином формате. На любой стороне эти данные могут десериализовать в объект с поведением (если эта сторона это умеет), но это будет объект "этой стороны" — а в контракте все равно описана просто структура данных. Иными словами, для WCF-сообщения в контракте указано, какие данные в нем есть, но не описано, какие операции оно умеет.


                                                              В Smalltalk-ах примерно так работает OpenTalk (VisualWorks). Или вот — удаленная отладка в Pharo. Это я к тому, что отладчик в Smalltalk весьма такая объектная штука.

                                                              Это гомогенные среды, в них все иначе устроено.

                                                                0
                                                                Иными словами, для WCF-сообщения в контракте указано, какие данные в нем есть, но не описано, какие операции оно умеет.

                                                                … уточнение: это точно верно для WSDL/SOAP, я не очень помню, как это устроено в случае с пропьетарными биндингами.

                                                                  0

                                                                  Уточнение к уточнению. By Design биндинги в WCF не имеют доступа к передаваемым объектам — им данные достаются уже в сериализованном виде.


                                                                  Если бы кто-то пожелал превратить WCF обратно в Remoting — ему стоило бы смотреть в сторону ContractBehavior и переопределения сериализатора.


                                                                  Кстати, "такая схема" (с разделением на передаваемые и ссылаемые объекты) была реализована в Remoting, но от нее отказались как от неудобной.

                                                                    0
                                                                    Кстати, "такая схема" (с разделением на передаваемые и ссылаемые объекты) была реализована в Remoting, но от нее отказались как от неудобной.

                                                                    Я ее застал, и именно поэтому у меня мороз по коже от этой идеи.

                                                                  0
                                                                  Это как раз проблема, потому что это и нарушает принцип «все — объект».
                                                                  Он в любом случае рано или поздно нарушается (мы это давно установили, да и секрета в этом никогда не было). Весь вопрос в том, рано или поздно.
                                                                  Вот только в интерфейсе это не объект, а «вся необходимая информация». А что на стороне обработчика — вы и не знаете (потому что инкапсуляция).
                                                                  Простите, не понял, о каком интерфейсе речь, и что мне нужно знать на стороне обработчика?
                                                                  Вот только сообщение, посланное объекту на стороне получателя (обработчика в моем примере) не будет получено объектом на стороне отправителя (реестра в моем примере).
                                                                  Почему же оно не будет получено-то? У нас же есть слой, передающий сообщения по сети?
                                                                  У вас объект раздвоился.
                                                                  На каждой из сторон я имею дело с объектом. Про то, что он раздвоился (детали реализации) никому из пользователей объекта знать не надо.
                                                                  Это как раз тот момент, когда то, что вы передали — не объект. Его поведение на разных сторонах не униформно.
                                                                  При правильной реализации на обеих сторонах может быть очень даже униформно. А правильную реализацию, кстати, можно при старте приложения согласовать.
                                                                  И да, как раз это «зависит» и приводит к не-униформности API: вы что-то передаете с поведением, что-то как ссылку, что-то как значение. Намного проще (и униформнее) все передавать как значение.
                                                                  Какая разница как я что-то передаю? Это детали реализации. Я могу вообще байт-код передавать. Могу нужную виртуальную машину на ту сторону загрузить.
                                                                  В нашем технически несовершенном мире это (а) очень медленно и (б) просто передает проблему на следующий уровень.
                                                                  Мы же говорим про принципиальную возможность, не так ли? Пункт (а) не релевантен. А что значит «передает проблему на следующий уровень»? Рано или поздно системы должны начать разговаривать «на одном языке». И даже, вроде бы, не важно когда именно — это детали реализации, от разработчиков на обеих сторонах они должны быть скрыты.
                                                                  Это гомогенные среды, в них все иначе устроено.
                                                                  Во! Может быть проблема-то как раз в том, чтобы реализовать объекты на том же уровне абстракции, где среды начинают понимать друг друга (становятся гомогенными)? Другими словами «протащить» объекты максимально далеко в бутстрэппинг? Что эквивалентно вопросу «минимально возможной объектной системы»? Не об этом говорит тот же Кэй, когда расхваливает Internet (в противовес Web-у)?

                                                                    +1
                                                                    Другими словами «протащить» объекты максимально далеко в бутстрэппинг?
                                                                    Кстати, Erlang позволяет посылать сообщения процессам на удалённых нодах, их даже никуда тащить не надо и тем более раздваивать. Вот только ответ на сообщение придёт в виде данных. Что весьма удобно на практике, но не вписывается в Вашу теоретическую концепцию.
                                                                      0
                                                                      Простите, не понял, о каком интерфейсе речь, и что мне нужно знать на стороне обработчика?

                                                                      Об API. И на стороне обработчика вам не надо знать, на каком языке написан реестр и наоборот.


                                                                      Почему же оно не будет получено-то? У нас же есть слой, передающий сообщения по сети?

                                                                      Потому что вы подняли копию объекта на стороне получателя, и теперь сообщения принимает она.


                                                                      На каждой из сторон я имею дело с объектом. Про то, что он раздвоился (детали реализации) никому из пользователей объекта знать не надо.

                                                                      Не надо?


                                                                      repository.createUser(user)
                                                                      notificationEngine.notifyCreation(user) //notificationEngine - удаленный
                                                                      user.firstName = "newName" //в notificationEngine он изменился или нет?
                                                                      
                                                                      > При правильной реализации на обеих сторонах может быть очень даже униформно.
                                                                      
                                                                      Не в реальном мире.
                                                                      
                                                                      > Могу нужную виртуальную машину на ту сторону загрузить.
                                                                      
                                                                      Тем самым нарушив право той стороны на собственную реализацию.
                                                                      
                                                                      > Рано или поздно системы должны начать разговаривать «на одном языке». 
                                                                      
                                                                      И этот язык, неизбежно, данные.
                                                                      
                                                                      > Может быть проблема-то как раз в том, чтобы реализовать объекты на том же уровне абстракции, где среды начинают понимать друг друга (становятся гомогенными)?
                                                                      
                                                                      Это эквивалентно "запретить всем писать на не-ООП-языках". Спасибо, но нет.
                                                                      
                                                                      > Не об этом говорит тот же Кэй, когда расхваливает Internet (в противовес Web-у)?
                                                                      
                                                                      Вот только в интернете между узлами гуляют данные, а не объекты. Что как бы лишний раз подтверждает.
                                                                        0
                                                                        Потому что вы подняли копию объекта на стороне получателя, и теперь сообщения принимает она.
                                                                        А потом транслирует на другую сторону, там сообщения принимают, обрабатывают (не зная, откуда они поступили), возвращают ответ, который транслируется назад, и там создается впечатление, что ответил мне тот объект, которому я послал исходное сообщение.
                                                                        Тем самым нарушив право той стороны на собственную реализацию.
                                                                        Что это за право? :) У компьютерных систем уже появились права?
                                                                        И этот язык, неизбежно, данные.
                                                                        А данных будет достаточно? Они не предполагают общей семантики? Вот вам данные: 124 343 32 23 123. Что я попросил вас сделать? :) Это не говоря о том, что и последовательность электрических сигналов превратить в «данные» без предварительной договоренности не выйдет. Так что общий язык не с данных начинается, увольте.
                                                                        Это эквивалентно «запретить всем писать на не-ООП-языках». Спасибо, но нет.
                                                                        Почему же всем и так категорично? Я понимаю русский язык. Это не мешает мне худо-бедно понимать английский. И какой-нибудь JSON спокойненько передается по TCP/IP так же, как SNMP.
                                                                        Вот только в интернете между узлами гуляют данные, а не объекты. Что как бы лишний раз подтверждает.
                                                                        Это только кажется, что проблему снимают именно данные. См. выше про общий язык. Это пока предпочитают об интерпретации данных договориться заранее и обходными маршрутами. Когда-нибудь придумают, как договариваться на ходу и по той же сети.

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

                                                                          То есть это опять прокси, а не "передать полное состояние и сказать экземпляр какого класса нужно создать — если они «похожи» (тот же String, к примеру)".


                                                                          Что это за право?

                                                                          Мое право как разработчика использовать ту технологию, которую я считаю более оправданной для решения задачи.


                                                                          Так что общий язык не с данных начинается, увольте.

                                                                          Я не сказал, что он с них начинается, я сказал, что он ими заканчивается.


                                                                          Почему же всем и так категорично?

                                                                          Потому что для не-ООП-сервиса поддерживать ООП для сообщений нелогично.


                                                                          Когда-нибудь придумают, как договариваться на ходу и по той же сети.

                                                                          Или не придумают.


                                                                          (На всякий случай напоминаю: мы же обсуждаем принципиальную невозможность.)

                                                                          Я имею привычку считать, что пока возможность не доказана — ее и нет. Особенно учитывая количество вложенных усилий.


                                                                          Вы предлагаете именно гомогенизировать среду. Это, конечно, похвально, но мне это банально не нравится.

                                                                            0
                                                                            То есть это опять прокси, а не «передать полное состояние и сказать экземпляр какого класса нужно создать — если они «похожи» (тот же String, к примеру)».
                                                                            Тогда я не понял, какой мы вариант обсуждаем, и в чем вы видите проблему?
                                                                            Я не сказал, что он с них начинается, я сказал, что он ими заканчивается.
                                                                            Тогда надо определиться, где начало, а где конец :)

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

                                                                            Я имею привычку считать, что пока возможность не доказана — ее и нет.
                                                                            К сожалению, эта привычка противоречит банальной логике. А оставаясь в ее рамках, …
                                                                            Когда-нибудь придумают, как договариваться на ходу и по той же сети.
                                                                            Или не придумают.
                                                                            … мы договорились: не определено, что опровергает исходный тезис.

                                                                              0
                                                                              Тогда я не понял, какой мы вариант обсуждаем, и в чем вы видите проблему?

                                                                              Я обсуждаю как раз вариант "создадим экземпляр класса на стороне получателя". Не прокси.


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

                                                                              Динамически переключаться между stateful- и stateless-моделями? Свежо предание...


                                                                              мы договорились: не определено, что опровергает исходный тезис.

                                                                              Извините, но мы не договорились. Договоренность требует согласия двух сторон, а моего согласия вы не получили.


                                                                              Вы пока так и не предложили решения, позволяющего гетерогенным системам иметь полностью униформное объектное поведение.

                                                                                0
                                                                                Я обсуждаю как раз вариант «создадим экземпляр класса на стороне получателя». Не прокси.
                                                                                Если объект не завязан на «контекст», который невозможно перетащить на другую сторону — никаких проблем не вижу. Если же таки завязан, то проксируем этот самый контекст.
                                                                                Извините, но мы не договорились.
                                                                                Ну как же, вы же признали: «Или не придумают». То есть, допустили возможность? :) Ладно, я думаю мы здесь друг друга поняли и нет смысла дальше препираться. Вы отсутствие подтверждения существования считаете доказательством отсутствия. Соответственно, черных лебедей не существовало, пока их не обнаружили в Австралии (если не врут на этот счет). И Земля была плоской, пока не доказали, что она имеет форму апельсина :)
                                                                                  0
                                                                                  никаких проблем не вижу.

                                                                                  … а дублирование объекта?


                                                                                  Ну как же, вы же признали: «Или не придумают». То есть, допустили возможность?

                                                                                  Нет, не допускал. Я продолжаю считать, что идея тотальных объектно-ориентированных API невозможна (в гетерогенных средах), потому что какое-то API будет возвращать примитивные значения (иначе вы не сможете выполнить примитивные операции).

                                                                                    0
                                                                                    … а дублирование объекта?
                                                                                    — деталь реализации, скрытая от пользователей (разработчиков) на обоих «концах».
                                                                                      0

                                                                                      Да как же она скрыта, если ее надо учитывать при программировании?

                                                                                        0
                                                                                        Учитывать надо только в момент создания объекта, представляющего удаленную сторону, что это именно удаленный объект (и соответствующим образом его сконфигурировать)… Хотя и это можно «скрыть» при желании. Все остальное «прозрачно» — ничем не отличается от обычной разработки.
                                                                                          0
                                                                                          Учитывать надо только в момент создания объекта, представляющего удаленную сторону, что это именно удаленный объект (и соответствующим образом его сконфигурировать)…

                                                                                          "объект, представляющий удаленную сторону" — это обработчик уведомлений (и у него как раз прокси). А "раздвоившийся" объект — это пользователь, которого туда передали.


                                                                                          Повторюсь, проблема в том, что — в рамках распределенной системы — вы не знаете, с каким объектом вы работаете: локальным, удаленным (через прокси) или раздвоившимся.

                                                                                            0
                                                                                            «раздвоившийся» объект — это пользователь, которого туда передали.
                                                                                            Он раздвоился только для наблюдателя со стороны. Для того, кто его передал, ничего не изменилось — объект как получал (откуда-то) сообщения, так и продолжает их получать. Для того, кому передали — то же самое: появился объект, я могу с ним работать как и с остальными; а где он на самом деле находится и как внутри устроен — мне «по барабану».
                                                                                            вы не знаете, с каким объектом вы работаете: локальным, удаленным (через прокси) или раздвоившимся
                                                                                            «Не знаю и знать не хочу!» — основной принцип и признак «правильного» ООП :)
                                                                                              0

                                                                                              Вот только вы зря думаете, что вам по-барабану, где находится объект. Это известная ошибка в связи с разработкой распределенных систем — иллюзия, что можно работать с удаленным объектом как с локальным (наоборот — можно).


                                                                                              В частности, вы не знаете, в каких случаях — при одном и том же вызове — состояние объекта меняется, а в каких — нет. Вы, конечно, не хотите об этом знать, но это незнание приведет к прекрасным побочным эффектам.

                                                                                                0
                                                                                                Побочные эффекты лишь говорят о несовершенстве используемых инструментов (в частности фреймворка, обеспечивающего удаленное взаимодействие объектов; но не только его). Вы же сами говорили про инкапсуляцию как одном из основных (едва ли не основном?) принципов ООП. А тут, оказывается, надо лезть внутрь объекта. Не сходится?
                                                                                                  0

                                                                                                  Так потому и не сходится, что в момент перехода к распределенным системам инкапсуляция на сообщениях перестает работать. Что и говорит нам о плохой применимости ООП в этом месте.

                                                                                                    0
                                                                                                    Так потому и не сходится, что в момент перехода к распределенным системам инкапсуляция на сообщениях перестает работать. Что и говорит нам о плохой применимости ООП в этом месте.
                                                                                                    Тогда с этого места поподробнее: что значит «инкапсуляция перестает работать в распределенных системах». Особенно в контексте того же Erlang (который, как я понял, инкапсулирован «по самое не хочу»).
                                                                                                      0

                                                                                                      Так в Erlang то, что вы посылаете (сообщение) — не объект. И именно такая модель (системы — объекты, сообщения — нет) наиболее жизнеспособна.

                                                                                                        0
                                                                                                        Но вы-то про инкапсуляцию говорили, а не про объекты? Вы меня запутать хотите?
                                                                                                          0

                                                                                                          У сообщения инкапсуляция минимальна. Его поведение тривиально, предсказуемо и известно снаружи.

                                                                                                            0
                                                                                                            Видимо, все-таки хотите запутать :) — я перестал понимать ход вашей мысли.
                                                                                                              0

                                                                                                              Ход моей мысли очень прост: в распределенной системе есть неотъемлимая (inherent) сложность, которая приводит к тому, что использование объектов-с-поведением в качестве сообщений становится нерентабельно (как технически, так и когнитивно). Я не знаю ни одной акторной системы, где сообщения имели бы собственное поведение.


                                                                                                              И это приводит к тому, что удобным униформным решением для API становится ситуация, когда системы являются объектами, а вот сообщения между ними — не являются.

                                                                                                                0
                                                                                                                То есть, проблема именно в сообщениях? Просто ранее была
                                                                                                                проблема в том, что — в рамках распределенной системы — вы не знаете, с каким объектом вы работаете: локальным, удаленным (через прокси) или раздвоившимся.
                                                                                                                Они как-то связаны?
                                                                                                                  0
                                                                                                                  То есть, проблема именно в сообщениях?

                                                                                                                  Да, проблема именно в сообщениях.


                                                                                                                  Они как-то связаны?

                                                                                                                  Это одна и та же проблема.


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


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

                                                                                                                    0
                                                                                                                    В какой замкнутый круг? Я по-прежнему не понимаю! Доказательства не намечаются, есть какие-то общие рассуждения. При условии, что мы не сходимся в базовых логических основаниях, на данный момент не вижу смысла в продолжении обсуждения по данной теме.
                                                                                                                      +1
                                                                                                                      В какой замкнутый круг?

                                                                                                                      Да в простой.


                                                                                                                      Рано или поздно для какой-то операции вам нужны локальные данные. Не удаленный объект, которому вы послали сообщение, и он что-то сделал, а локально присутствующие данные. И либо у вас какие-то данные локальные, а какие-то — удаленные, и тогда у вас нет униформности, либо у вас все данные… какие?

                                                                                                                        0
                                                                                                                        Локальные данные — они же внутри объекта?
                                                                                                                          0

                                                                                                                          Если следовать идее "все объект" — то да.

                                                                                                                            0
                                                                                                                            Так где нарушается «униформность»?
                                                                                                                              0

                                                                                                                              Там, где у вас одни данные — локальные, а другие — удаленные.

                                                                                                                                0
                                                                                                                                Эти данные — внутри объекта. А работаю я с объектами извне. А извне все объекты одинаковы.
                                                                                                                                  0

                                                                                                                                  Ах если бы. Во-первых, у вас принципиально разная стоимость вызова (что ведет к chatty vs chunky). Во-вторых, вы не можете передать удаленный объект в произвольную систему (потому что вы не знаете, имеет ли эта произвольная система доступ к этому объекту).

                                                                                                                                    0
                                                                                                                                    Я собираю систему из таких объектов, которые соответствуют предъявляемым к этой системе требованиям. При необходимости я создаю нужные объекты.
                                                                                                                                      0

                                                                                                                                      И поведение этих объектов — именно благодаря реалистичным требованиям — будет разнородным. О чем и речь.

                                                                                                                                        0
                                                                                                                                        Поведение будет разным (поэтому и объекты разные), но однородным — они все остануться объектами и будут отвечать на сообщения.
                                                                                                                                          +2

                                                                                                                                          Вот только степень разницы, вызванная требованиями (в частности, как мы только что выяснили, [не]изменяемость состояния), намного выше, чем разница "отвечает на сообщение/или нет".


                                                                                                                                          Собственно, если просто закрыть глаза на то, что DTO не имеют поведения — то да, все так и выглядит, "все есть объекты", только некоторые объекты не такие, как другие объекты.

                                                                                                                                            0
                                                                                                                                            «все есть объекты», только некоторые объекты не такие, как другие объекты.
                                                                                                                                            Почти все объекты — не такие (в том или ином смысле) как другие объекты. И существенно не такие. И при этом все они однородны — отвечают на сообщения.

                                                                                                                                            И, кстати, оформилась хорошая мысль: можно «необъекты» присыпать сахарочком, чтобы они выглядели как объекты — путь мейнстрима; а можно «необъекты» реализовать в виде объектов — путь исходного ООП?
                                                                                                                                              0

                                                                                                                                              Угу, осталось определиться, чем "сахарочек" отличается от "реализовать в виде объектов".

                                                                                                                                                0
                                                                                                                                                Например, тем, что в первом случае необъекты, хоть и засыпанные сахаром, таки присутствуют в языке — и пользователю так или иначе приходится с ними иметь дело.
                                                                                                                                                  0

                                                                                                                                                  Пример?

                                                                                                                                                    0
                                                                                                                                                    Примитивные типы. Например, int.
                                                                                                                                                      0

                                                                                                                                                      … и чем int в C# отличается от SmallInteger в Smalltalk?

                                                                                                                                                        0
                                                                                                                                                        Начнем с того, что int — это тип, а SmallInteger — это объект?
                                                                                                                                                          0

                                                                                                                                                          Ну нет. SmallInteger — это класс, а значит — тоже тип. Конкретное x в x := 1 — это объект.

                                                                  0
                                                                  Чисто теоритически, в порядке бреда:
                                                                  ссылку на объект «пользователь», по которой и реестр, и обработчик смогут послать любое сообщение, и получить ответ. Но вот незадача… ответ на сообщение getFullName — это что? Объект? Тогда мы попали в замкнутый круг. Или это значение-строка? Но тогда мы нарушили униформность.

                                                                  Допустим, DCOM alike принцип. Если мы хотим униформности, то мы у себя генерируем объект вида «ответ» и пересылаем ссылку на него с запросом. Часть, ответственная за пользователя, генерирует полное имя и обращаясь к нашему объекту-ответу, устанавливает его внутреннее содержание.
                                                                  И все равно, блин, «ссылка» будет примитивом.
                                                                    –1

                                                                    Во-первых, ссылка будет примитивом. А во-вторых, если все объекты удаленные, то нам нужна сквозная доступность всех узлов от всех узлов.

                                                                      +1
                                                                      Доступность предполагается. Необязательно всех, достаточно двух попарно обменивающихся частей системы, их специфических для данного вида обмена интерфейсов.
                                                                      Но ссылка да — будет примитивом. С другой стороны, это будет единственный примитив. Не то что бы мне это было интересно, просто к слову пришлось по ходу чтения вашей дискуссии.
                                                                        –1
                                                                        Доступность предполагается. Необязательно всех, достаточно двух попарно обменивающихся частей системы, их специфических для данного вида обмена интерфейсов.

                                                                        А вот нет, к сожалению. Вот пошел я в (удаленный) сервис, вызвал на нем операцию, хочу ее результат сохранить в (удаленную) БД. Сервис я вижу, БД я вижу, но друг друга они не видят. Как результат операции попадет в БД?

                                                                          +1
                                                                          Ну очевидно, что раз мы запросили сервис, то и результат получим мы, а не база. А уж в базу кинем мы.
                                                                          Если вы хотите DMA-alike поведение, то да, придется им друг друга увидеть.
                                                                            –1

                                                                            Так что мы кинем в базу-то? Если единственное, что мы можем получить в виде результата — это ссылка?

                                                                              +1
                                                                              Нет, ссылку на интерфейс объекта-результата на нашей стороне мы отправили сервису. Он воспользовался (посредством ссылки) объектом-результатом, который у нас находится.
                                                                                –1

                                                                                Смотрите (давайте в обратном порядке, так немного очевиднее).


                                                                                1. Я иду в удаленную БД: person = getPerson(id)
                                                                                2. Она вернула мне ссылку на объект на ее стороне (потому что ничего другого она сделать не может)
                                                                                3. В person именно эта ссылка
                                                                                4. Я вызываю (удаленный) мейлер sendEmail(person, text)
                                                                                5. person — это все еще ссылка на БД. Для мейлера она бессмысленна (у него нет доступа к БД)

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

                                                                                  +1
                                                                                  Смотрите (давайте в обратном порядке, так немного очевиднее).

                                                                                  Я иду в удаленную БД: person = getPerson(id)
                                                                                  Она вернула мне ссылку на объект на ее стороне (потому что ничего другого она сделать не может)
                                                                                  В person именно эта ссылка

                                                                                  Не совсем так, но даже в этом случае мы можем создать наш объект на пункте 0 и попросить его скопировать значение (состояние) объекта по ссылке person.
                                                                                  Я думал примерно о:
                                                                                  1. Создаем объект-ответ: textual_response = new TextualResponse;
                                                                                  2. Делаем запрос, передавая ссылку на наш объект: getPerson(id, textual_response)
                                                                                  3. База заполняет ответ на своей стороне: remote_host.textual_response.setvalue(...)
                                                                                  И далее. Технически будет обмен примитивами все равно — БД должна запихнуть строчку в наш textual_response объект, но это может быть что-то типа friend полей/методов, которые доступны только внутри объектов или при помощи 3й сущности-объекта, которая может работать с внутренним представлением и копировать состояние объектов.

                                                                                  … а еще я внезапно понял, что для этой системы невозможно реализовать равенство по значению. Или я что-то путаю?

                                                                                  Тут через 3й объект-супервизор см. выше.

                                                                                  Это длинная и нафиг ненужная цепочка, просто абстракция ради абстракции. Но раз уж мы говорим о том, что это-де технически невозможно, то все же можно придумать такую систему. Просто в качестве разминки ума, на деле кому это надо.
                                                                                    –1
                                                                                    Не совсем так, но даже в этом случае мы можем создать наш объект на пункте 0 и попросить его скопировать значение (состояние) объекта по ссылке person.

                                                                                    Понимаете, вы предлагаете скопировать значения объекта. А что делать с поведением?


                                                                                    База заполняет ответ на своей стороне: remote_host.textual_response.setvalue(...)

                                                                                    В этот момент то, что будет внутри, придется передать как примитив (и по значению). То есть мы все равно нарушили идею о том, что все есть ссылка.

                                                                                      +1
                                                                                      Понимаете, вы предлагаете скопировать значения объекта. А что делать с поведением?

                                                                                      Поведение у них по определению одинаково — это объекты одного типа. Я о конечных данных, если что, а не каких-нибудь active record'ах.

                                                                                      В этот момент то, что будет внутри, придется передать как примитив (и по значению). То есть мы все равно нарушили идею о том, что все есть ссылка

                                                                                      Не совсем. если мы делаем что-то типа objectA.cloneFrom(objectB), где объекты имеют доступ к внутреннему состоянию при клонировании, это будет внутренняя кухня, нас это не интересует — с точки зрения интерфейса у нас объект инициализировался объектом.
                                                                                        –1
                                                                                        Поведение у них по определению одинаково — это объекты одного типа.

                                                                                        Ээээ, это только в том случае, если у нас с обеих сторон одна и та же среда выполнения.


                                                                                        Не совсем. если мы делаем что-то типа objectA.cloneFrom(objectB), где объекты имеют доступ к внутреннему состоянию при клонировании, это будет внутренняя кухня, нас это не интересует — с точки зрения интерфейса у нас объект инициализировался объектом.

                                                                                        Для этого cloneFrom должен быть системным методом, недоступным для переопределения. Так?

                                                                                          +1
                                                                                          Ээээ, это только в том случае, если у нас с обеих сторон одна и та же среда выполнения.

                                                                                          Необязательно. Допустим, у нас есть рамочная конвенция поведения, на которую мы расчитываем (одинаковый ответ на одинаковые воздействия).

                                                                                          Для этого cloneFrom должен быть системным методом, недоступным для переопределения. Так?

                                                                                          Допустим, базовый объект, самого нижнего уровня в системе, имеет cloneFrom изначально. Или он работает, например, через сериализацию по public properties.
                                                                                            –1
                                                                                            Допустим, у нас есть рамочная конвенция поведения, на которую мы расчитываем (одинаковый ответ на одинаковые воздействия).

                                                                                            Для любого объекта? На любой реализации? Синхронно одинаковая везде? Утопия же.


                                                                                            Допустим, базовый объект, самого нижнего уровня в системе, имеет cloneFrom изначально.

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


                                                                                            Или он работает, например, через сериализацию по public properties.

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

                                                                      –1

                                                                      … а еще я внезапно понял, что для этой системы невозможно реализовать равенство по значению. Или я что-то путаю?

                                                        0
                                                        Где мне начинать использовать объекты, а где нет? Насколько это усложнит мою задачу (значительную часть времени я буду думать не о решении ее, а о там как получить нужные сущности)?
                                                        Если исходить из определения «объект — это изолированный процесс со своей областью памяти» и учитывая, что в ФП никакого shared state не бывает в принципе, то все ваши вопросы выглядят надуманными и на практике возникают исключительно редко. По крайней мере за год программирования на Elixir у меня ни разу не было дилеммы «что делать процессом, а что нет». Т.е. ни на сколько это не усложнит вашу задачу, исходя из практики — только упростит.

                                                        Как мне не-объекты превратить в объекты и наоборот (когда надо)?
                                                        Никогда не надо. Не бывает задачи превратить процесс в, допустим, список или наоборот — список в процесс.
                                                          0
                                                          Что такое Map в вашем коде?

                                                          Подскажите, где здесь ООП, а где ФП?
                                                              all_students = state
                                                                |> Map.keys
                                                                |> Enum.map(fn(grade) ->
                                                                  [grade: grade, students: get_students_by_grade(state, grade)]
                                                                end)
                                                          

                                                            +1

                                                            Map — это модуль в терминологии Elixir.


                                                            А приведенный вами фрагмент кода — полностью функциональный.

                                                              0
                                                              Собственно, мне нечего добавить, кроме ссылки на доку
                                                              lair уже абсолютно правильно ответил.
                                                                0
                                                                То есть, объектность кончается на уровне описания «интерфейса»? Внутри «объекта» у меня объектов больше нет? А если мне все-таки нужен объект, я за это должен платить созданием процесса? И, обращаясь к объекту, я перехожу на «объектный язык», а как только пришел ответ — возвращаюсь на функциональный?
                                                                  +2
                                                                  Да, всё верно. Внутри «объекта» у нас сплошное ФП :-)
                                                                  Впрочем если хотите некую композицию «объектов», то это тоже не вопрос, стартуйте нужный процесс из обработчика сообщения.
                                                                  Плата за создание процесса не так велика, оверхэд примерно того же порядка как при создании объектов в Java.
                                                                  В общем, смысл в том, чтобы в качестве объектов рассматривать только те сущности, которые реально живут какой-то своей жизнью. У которых есть понятный жизненный цикл или миссия «бесконечно» обрабатывать некоторые запросы.
                                                                  Всё остальное — это просто данные, которые обрабатываются при помощи ФП.
                                      0
                                      I made up the term “object-oriented”, and I can tell you I didn't have C++ in mind

                                      Видимо, даже внеся значительный вклад в «изобретение будущего», предсказать его все равно невозможно.
                                        +1
                                        Просто некоторые под будущим понимают нечто светлое и красивое :)
                                        +3
                                        «Как выглядит ООП в функциональном языке?» — почти что также как и в любом другом…
                                          –1
                                          … почти также как в любом другом необъектном :)
                                          +1
                                          Чуть не забыл! Мое внимание привлекла эта фраза:
                                          в котором ООП дошло до того, что даже операторы — это чьи-то методы
                                          Поясните, пожалуйста, что вы имели ввиду?
                                            0
                                            Имеется в виду, что нет такой разницы между операторами и методами, как в Java или в C#, например можно написать так:
                                            [1, 2, 3, 4, 5].<<(2.+(2.*(2))) # => [1, 2, 3, 4, 5, 6]
                                            
                                              +1
                                              А почему дошло? В ООП изначально понятие операции (над значением определенного типа) заменили на сообщение (посылаемое объекту). А метод — это просто реализация, описывающая как объект реагирует на конкретное сообщение.

                                              Собственно, это еще раз про чистоту, которую мы обсуждаем в соседней ветке, если я правильно понял? Тогда данную можно закрыть.
                                                0
                                                А почему дошло? В ООП изначально понятие операции (над значением определенного типа) заменили на сообщение (посылаемое объекту).
                                                Потому что это тонкий стёб над мейнстрим-реализациями ООП.
                                            0
                                            Я вообще не понимаю, зачем вам терминология ООП в данном случае.
                                            Ну есть у вас сущность и набор операций над ней, причем тут ООП?
                                              0
                                              Спасибо! Вы просто и коротко написали то, что я долго и нудно пытаюсь сказать!
                                                0
                                                А почему бы нет? На мой взгляд отличная реализация метафоры, которая легла в основу ООП:
                                                biological scheme of protected universal cells interacting only through messages that could mimic any desired behavior

                                                Тем более мейстрим-реализации ООП свели ценность этой аналогии к нулю, не обеспечивая реальную защиту внутреннего состояния и увлёкшись странной идеей, что ядро клетки — это тоже клетка, митохондрия — это тоже клетка, рибосома — тоже клетка и т.д… Но нет, внутреннее состояние клетки — это не набор других клеток. В биологии у вас не получится спуститься на уровень кварков и заявить, что кварк — это тоже клетка :-)
                                                  –1
                                                  My biology major had focused on both cell metabolism and larger scale morphogenesis with its notions of simple mechanisms controlling complex processes and one kind of building block able to differentiate into all needed building blocks.


                                                  Вообще, интересная манера: схватиться за одну фразу (к тому же метафорическую) и на ней строить какие-то теории, ссылаясь на автора фразы, но при этом игнорируя 99,999% его труда. Тогда и статью надо назвать: «Как выглядит метафора клеток в функциональном языке»
                                                    +1
                                                    Ну, able to — это не то же самое, что has to.
                                                    У Вас свои стойкие предубеждения и Вы интерпретируете всё в пользу Вашей точки зрения. Вплоть до того, что «initial premises in designing the Smalltalk interpreter» внезапно превращаются в определение ООП. И все, кто не согласен, с Вашей интерпретацией книги про раннюю историю Smalltalk, сразу признаются ничего не понимающими в ООП, похоже, включая и самого Алана )))
                                                    Вот его мнение от 2010 года в тему этой статьи:
                                                    Many of Carl Hewitt’s Actors ideas which got sparked by the original Smalltalk were more in the spirit of OOP than the subsequent Smalltalks. Significant parts of Erlang are more like a real OOP language than the current Smalltalk, and certainly the C based languages that have been painted with “OOP paint”.
                                                    пруф
                                                      –2
                                                      Не надо путать предубеждения и убеждения (основанные на чем-то, и это далеко не одна упомянутая книга, уж поверьте). У меня есть своя точка зрения и я имею не меньше прав отстаивать ее, чем вы — свою. И аналогично вашим придиркам к словам я могу сказать что «spirit of OOP» это не то же самое, что «OOP». А уж «main ideas that were in accord with the initial premises in designing the interpreter» вдруг превращаются в сами «initial premises» — извините, не у меня. Да и аппелировать к Кэю приходится не от хорошей жизни — без ссылок на авторитеты почему-то никто не хочет даже задуматься над сутью идеи.

                                                      Кроме того, я-то как раз не считаю C#, Java или даже Smalltalk идеальными выразителями идей OOP. Если вы (или кто-то еще) покажете эти самые «parts of Erlang», которые «more like a real OOP language than the current Smalltalk» — я буду вам (или кому-то еще) очень признателен. Серьезно. Но пока что как-то не получилось. Может быть, в данном случае, потому что вы уделяете внимание каким-то не тем вопросам? В общем, из наших с вами дискуссий мне пока не удалось понять, чем же Erlang лучше (проще, мощнее) того же Smalltalk-а. Но это, разумеется, мои проблемы.

                                                      Ну, и еще один вопрос. Где и кого я лишь на основании несогласия с моей точкой зрения назвал ничего не понимающим в ООП?! Если такое было, я приношу тому человеку свои глубочайшие и искренние извинения. Вот, мне интересно, только честно: до наших с вами дискуссий вы много времени посвятили тому, чтобы вникнуть в суть ООП и его настоящие базовые идеи?
                                                        +2
                                                        Согласен, у Вас есть такое же право на свою точку зрения. Просто меня удивляет, как Вы интерпретируя и опираясь на то, что пишет Кэй, приходите к несогласию с ним же. Я не говорю, что Elixir или Erlang лучше реализуют ООП, чем Smalltalk, это просто разные реализации. Идея в том, что все преимущества ООП можно использовать и в этих языках. При этом ещё и получив все преимущества ФП на уровне реализации.
                                                        Вот ещё Вам цитата из совсем свежего:


                                                        Что Вы ещё хотите понять про Erlang/Elixir в плане ООП, мне уже непонятно. Для полноценного сравнения со Smalltalk, если Вы его ждёте, мне пришлось бы освоить Smalltalk. Поэтому я сравниваю их с языками, которые мне уже хорошо известны, типа Ruby или C#. И они в плане соответствия идеям Кэя сильно проигрывают Elixir, хоть и достаточно полно (особенно Ruby) реализуют «Всё есть объект».

                                                        Вот, мне интересно, только честно: до наших с вами дискуссий вы много времени посвятили тому, чтобы вникнуть в суть ООП и его настоящие базовые идеи?
                                                        Достаточно много. Где-то 3 года назад заинтересовался истоками ООП.
                                                          0
                                                          Просто меня удивляет, как Вы интерпретируя и опираясь на то, что пишет Кэй, приходите к несогласию с ним же.
                                                          А в чем несогласие-то?
                                                          Что Вы ещё хотите понять про Erlang/Elixir в плане ООП, мне уже непонятно.
                                                          Ничего страшного — мне тоже непонятно. :) Просто вы попробовали. Для кого-то этого было достаточно. А мне вот оказалось мало. Мое понимание ООП не изменилось, и в него сказанное не вкладывается. Нет, я честно пытался не «победить в споре», а понять вашу точку зрения. Но «не сходится». Smalltalk (с поправками на реальную жизнь) — он сходится. А здесь — пока нет.
                                                          Про сравнение со Smalltalk, разумеется, и речи нет. Возможно (нет, точно!), это мне надо взять и попробовать плотно Erlang. А если вопрос был не риторическим, то хорошей заманухой был бы показ интересных, нетривиальных задач с красивым ООП-решением. А то ведь (вы помните, исходное обсуждение в нашем чате?) даже ваши коллеги по Erlang-у выражали сильное недоумение по поводу ООП-программирования на это языке.
                                                            0
                                                            А в чем несогласие-то?
                                                            Ну вот Вы говорите:
                                                            Соответственно, речь таки не идет об ООП в исходном «Кэевском» смысле слова. Получается, скорее, некая имитация «антуража» ООП…

                                                            А он про то же самое говорит, что это один из двух путей понять «real OOP». По-моему тут явное противоречие Вашей точки зрения с точкой зрения Кэя.

                                                            показ интересных, нетривиальных задач с красивым ООП-решением
                                                            ну, это надо что-то из open source посмотреть, не писать же нетривиальный проект чисто в демо-целях )))

                                                            даже ваши коллеги по Erlang-у выражали сильное недоумение по поводу ООП-программирования на это языке.
                                                            Не забывайте, что имеет место сильная зашоренность на тему ООП. Для многих ООП == [«Инкапсуляция», «Наследование», «Полиморфизм»]. Для некоторых вообще ООП — это там где классы. В этом плане я согласен, с nwalker, когда говоришь ООП, никогда точно не знаешь, что подумал собеседник.
                                                              0
                                                              Я бы хотел добавить огонька в дискуссию :)
                                                                0
                                                                Ну, прикольное видео… Только оно как раз скорее про Java, а не про ООП в изначальном смысле. Я досмотрел до момента, когда он нарисовал дерево супервизоров и сказал, что нормальная инкапсуляция возможна только в таком случае, но так никто не пишет. Забавно, что на Erlang/Elixir практически только так и пишут, даже есть отладочный инструмент, который эти деревья визуализирует, observer называется.
                                                                image
                                                                0

                                                                Заметим, кстати, что в модели акторов есть и инкапсуляция, и полиморфизм.

                                                                  0
                                                                  Есть. Но без усердного пиара. А так, инкапсуляция в плане сокрытия гораздо лучше как раз в модели акторов реализована на мой взгляд.
                                                                  Ещё и композиция есть. Наследование же давно уже не в почёте xD
                                                                    0
                                                                    Учитывая «широту трактовки» этих терминов — не удивительно.
                                                                    0
                                                                    Ну вот Вы говорите:
                                                                    Соответственно, речь таки не идет об ООП в исходном «Кэевском» смысле слова. Получается, скорее, некая имитация «антуража» ООП…

                                                                    А он про то же самое говорит, что это один из двух путей понять «real OOP». По-моему тут явное противоречие Вашей точки зрения с точкой зрения Кэя.
                                                                    Не, вы, пожалуйста, не путайте, и не перемещайте слова из одного контекста в другой. Я охотно допускаю, что Erlang с моделью акторов может развивать идею ООП или, по крайней мере, помогать ее лучше понять. Этот язык давно стоит в моем списке на изучение… слишком давно, к сожалению. И мне очень интересно, что там Кэй обнаружил (это интервью я читал почти сразу после его появления), и посмотреть действительно ли это круто на практике. Другое дело: я не смог этого понять из вашей статьи. Собственно, я уже об этом писал.
                                                                    Планка того как может работать ООП после Smalltalk-а поднята довольно высоко. И, к примеру, чистота там играет довольно существенную роль. По крайней мере, очень часто наиболее красивые и мощные решения получаются там, где «все есть объект». И напротив: много проблем возникает именно там, где от этого принципа отходят. А уж после мейнстримовых языков к отрицанию данного принципа относишься оооочень настороженно :)
                                                                    Не забывайте, что имеет место сильная зашоренность на тему ООП.
                                                                    Именно против этой зашоренности я и возражаю! И поймите мое этакое разочарование(: вы начинаете с разбора «истоков», что просто замечательно, но в итоге скатываетесь туда же, куда и мейнстрим, да еще и подгоняете цитаты и определения под это дело. Я прекрасно понимаю, что и меня обвиняют в том же ;) И мне данная дискуссия помогла понять, что в отличие от споров с поклонниками триады «инкапсуляция–наследование–полиморфизм» (которые зачастую даже не хотят четко сформулировать свою позицию), мы как раз сходимся в том, что ООП шире и мощнее, чем попытка его пародии в мейнстримовом сознании. Пытаться трактовать цитаты в данном случае — скорее всего, путь в никуда. Тем более, ограничиваясь только цитатами Кэя — учитывая, что он таки не один работал над проблемой… Судя по всему (или даже наверняка), даже в коллективе разработчиков того же Smalltalk были разные точки зрения.

                                                                    В общем, если вы примите совет, то я бы на вашем месте попробовал найти хорошие примеры использования «силы» :) настоящего ООП в Erlang-е.
                                                                      0
                                                                      В принципе я понял Вашу позицию. Для Вас «всё есть объект» имеет некий практический смысл. А в моей картине мира это лишь интересная теоретическая идея, типа абсолютно чистого функционального языка.
                                                                      На практике зачастую вырождающаяся в «объекты ради объектов» и «чистоту ради чистоты». Как говорится, гладко было на бумаге, да забыли про овраги.
                                                                        0
                                                                        А как насчет «чистоты ради простоты»?
                                                                          +1
                                                                          Ну да, оборачивать всё в монады и смиряться с побочными эффектами, куда уж проще… Хотя допускаю, что при определенном типе мышления, и это может стать простым.
                                                                          Однако что одна, что другая идея на практике не реализуема на 100%. Программа без побочных эффектов никому не нужна. То же самое и с объектами, программа, которая работает только с объектами — это вещь в себе, она не пишет в файлы, не рендерит странички в html, не принимает пользовательский ввод, не пишет в БД (хотя может ООСУБД ещё кто-то использует?) и т.д.
                                                                            0
                                                                            Как ни странно, разрабатывая все на том же Smalltalk-е, я на практике имею дело только с объектами. А про монады и слышать не хочу :) И при этом я могу и писать в файлы, и делать веб-приложиения, и работать с БД (и с реляционными, и с нереляционными, в том числе и с объектными — да используют, и весьма успешно), и даже принимать пользовательский ввод (как это ни удивительно для интерактивной среды). :)
                                                                              0
                                                                              Это не странно. Это просто неизбежное нарушение концепции «всё есть объект». Не можете Вы послать объекты ни в файл, ни в реляционную БД, там будет что угодно: строки, бинарные последовательности, числа, массивы, но только не объекты :-)
                                                                              Наличие хотя бы автораспаковывания/упаковывания данных необходимо для всех вышеперечисленных задач.
                                                                                0
                                                                                Да мне не интересно, что там «внутри», для меня — все это объекты, поскольку работаю я с ними всеми как с объектами. И никакого нарушения, если не буду залезать слишком глубоко и придираться, не обнаружу. Это в Smalltalk-е. Даже когда буду не то что коллекцию обрабатывать, а банальный цикл или даже «if» писать — все равно буду объектам слать сообщения. Вот ни на секундочку не прекращу… А в Erlang-е — и обнаружу, и буду постоянно переключаться между ООП и ФП (если я вас правильно понял).

                                                                                :) Вы меня реально за идиота принимаете? Уже не в первый раз притом :) Вы действительно считаете, что я не понимаю, что рекурсия где-то должна кончится? что не имею представления о том, как работает современный компьютер? что не понимаю, что рекурсия должна где-то закончится?
                                                                                  0
                                                                                  Да мне не интересно, что там «внутри»
                                                                                  Вот! Именно это я Вам вчера и писал!
                                                                                  ООП не интересует что там внутри. В нашем случае внутри акторов: ФП и обычные типы данных.

                                                                                  Даже когда буду не то что коллекцию обрабатывать, а банальный цикл или даже «if» писать — все равно буду объектам слать сообщения.
                                                                                  И какому же объекту уйдёт ваше while или if сообщение? Я понимаю, что можно фантазию подключить и выдумать вспомогательные объекты. Вопрос только нафига?

                                                                                  Вы меня реально за идиота принимаете? Уже не в первый раз притом :) Вы действительно считаете, что я не понимаю, что рекурсия где-то должна кончится?
                                                                                  Я просто не понимаю, почему Вы считаете принципиальным на каком моменте эта рекурсия закончится.
                                                                                  Вот на мой взгляд, добавление к числу объектного поведения капитально усложняет работу с числами, зачем тогда мне рекурсивно превращать их в объекты?
                                                                                  Попробуйте в ООП-стиле написать код для вычисления суммы квадратов натуральных чисел от 1 до n. Для примера реализация на Elixir:
                                                                                  Enum.reduce(1..n, &(&1*&1 + &2))
                                                                                  
                                                                                    0
                                                                                    ООП не интересует что там внутри.<…>Я просто не понимаю, почему Вы считаете принципиальным на каком моменте эта рекурсия закончится.
                                                                                    У меня есть ощущение, что вы просто не хотите это понять :) Могу ошибаться. Но все же очень просто. Есть ведь разница между «меня не интересует» и «ООП не интересует»? Я сомневаюсь, что ООП обладает сознанием, но даже если так, то со мной своими переживаниями не делится. А вот меня действительно мало интересует устройство объектов — до тех пор, пока они остаются объектами и мне не приходится постоянно переключать «режим» сознания при работе с сущностями различного рода. Понимаете? Граница проходит либо по языку либо «где-то за ним» — и это существенно. В первом случае мне приходится разговаривать на двух языках одновременно — и это сложнее, чем на одном (во втором случае). В Smalltalk-е граница совсем чуть-чуть залезает в язык, что изредка мешает.
                                                                                    И какому же объекту уйдёт ваше while или if сообщение?
                                                                                    (someObject checkCondition) ifTrue: […] ifFalse: [].
                                                                                    [someObject checkCondition] whileTrue: […].
                                                                                    
                                                                                    Соответственно, if уходит экземпляру подкласса Boolean (там отдельно есть True и False), а while — блоку.
                                                                                    Попробуйте в ООП-стиле написать код для вычисления суммы квадратов натуральных чисел от 1 до n.
                                                                                    (1 to: n) inject: 0 into: [:sum :each | sum + each squared]
                                                                                    Для примера реализация на Elixir:
                                                                                    Enum.reduce(1..n, &(&1*&1 + &2))
                                                                                    
                                                                                    Вы уверены, что это сработает правильно? Видимо, в Smalltalk reduce работает иначе, приходится фокусничать:
                                                                                    (0 to: n) reduce: [:a :b | a + b squared]. 
                                                                                    
                                                                                      0
                                                                                      Да, можно же с 1, ведь 1*1 = 1. Но все равно фокус :)
                                                                                        0
                                                                                        Ok, пусть будет так:
                                                                                        Enum.reduce(1..n, 0, &(&1*&1 + &2))
                                                                                        

                                                                                        чтобы без фокусов )))
                                                                                          0
                                                                                          Я имею ввиду, что, если последовательность аргументов совпадает с их порядков в коллекции, то, вроде бы, надо написать &1 + &2*&2. Нет? А то сумма будет все время возводится в квадрат, а добавляться будет очередное число без квадрата. Нет?
                                                                                        0
                                                                                        Есть ведь разница между «меня не интересует» и «ООП не интересует»? Я сомневаюсь, что ООП обладает сознанием, но даже если так, то со мной своими переживаниями не делится.
                                                                                        Не придирайтесь… Это всего лишь сокращение для «Во время применения ООП нас не интересует»

                                                                                        мне не приходится постоянно переключать «режим» сознания при работе с сущностями различного рода. Понимаете?
                                                                                        Честно говоря, не понимаю в чём проблема. Мы в жизни постоянно переключаемся между сущностями различного рода, можно сказать, это дефолтное восприятие человека. Вы по-любому переключаетесь, когда говорите о массивах, строках, числах. Просто делаете вид, что это типа тоже объекты. Если бы можно было бы оставаться всегда на уровне объектов, не переключаясь, Вы бы названия обычных типов данных вообще забыли бы.

                                                                                        (0 to: n) reduce: [:a :b | a + b squared].
                                                                                        Кхм, и в каком месте тут униформность?
                                                                                        если вызов метода записывается как
                                                                                        object method: arg
                                                                                        то что такое квадратные скобки? что такое a + b? что такое b squared? что такое ":a :b |"?
                                                                                        Даже реализация такого микропримера совсем не униформна, о каком отсутствии «переключений» тогда вообще говорить…
                                                                                          0
                                                                                          что такое a + b?
                                                                                          Объекту в a посылается сообщение + b.
                                                                                          что такое b squared?
                                                                                          Объекту в b посылается сообщение squared.
                                                                                          что такое квадратные скобки?
                                                                                          Синтаксический сахар для создания объекта, представляющего лексическое замыкание.
                                                                                          что такое ":a :b |"?
                                                                                          Синтаксический сахар для помещения в лексическое замыкание объектов, представляющих переменные с соответствующими именами.

                                                                                          Я уже много раз говорил: Smalltalk не идеален с точки зрения чистоты ООП (да и с некоторых других точек зрения). И, к примеру, последний синтаксис реально выбивается из концепции посылки сообщений — в процессе программирования это прерывает поток.

                                                                                          Но: 1) все то же самое можно (несколько длиннее) написать на «чистых» сообщениях; 2) все то же самое (с некоторым усложнением парсера и, возможно, ВМ) можно реализовать примерно на таком же синтаксисе при сохранении «чистых» сообщений.

                                                                                          Просто Smalltalk в какой-то момент оказался (о чем жаловался тот же Кэй) сложным и соответственно инертным в развитии. Есть веские причины полагать, что основным виновником стала как раз обсуждаемая тема: от объектов отказались слишком рано на пути от верхних уровней абстракции к нижним уровням реализации. Сделать это пришлось, как я понимаю, главным образом в угоду производительности. Не забываем — это середина и вторая половина 70-х с соответствующими скоростями и объемами памяти в компьютерной технике. Скажем, уже в Self (середина 80-х) от части таких проблем уже удалось уйти. Думаю, если бы мейнстрим и массовые сознание не властвовали над нами, давно уже могло бы развиться и дальше.
                                                                                            0
                                                                                            Понимаете, наличие синтаксического сахара говорит о том, что концепция в чистом виде не слишком удобна и/или трудна для понимания. И тут встаёт выбор «вам шашечки (чистоту концепции) или ехать (возможность эффективно решать задачи)».
                                                                                            То, что теоретически что-то возможно, не делает это автоматически удобным для всех. C Lisp похожая ситуация, концепция изящна и удивительно проста. Но для легкости восприятия требуется либо отходить от привычного мышления, либо увешиваться синтаксическим сахаром.
                                                                                            Поэтому ни Smalltalk, ни Lisp и не захватили мейнстрим… среднестатистическому программисту банально не удобно с любой униформностью. Да и в физической реальности нас окружает наблюдаемое многообразие, а униформность остаётся теоретической концепцией.
                                                                                              0
                                                                                              Понимаете, наличие синтаксического сахара говорит о том, что концепция в чистом виде не слишком удобна и/или трудна для понимания.
                                                                                              … или для реализации.
                                                                                              И тут встаёт выбор «вам шашечки (чистоту концепции) или ехать (возможность эффективно решать задачи)».
                                                                                              Если бы тут было «исключающее или», мы бы до сих пор программировали на ассемблере. Вы продолжаете настаивать на «чистоте» как некоторой никому не нужной блажи. Из ваших высказываний получается, что логика такая: нет систем, которые бы я знал и где соблюдался этот принцип => принцип не нужен (да и не реализуем). Если вы так и не видите язъяна здесь, что ж… я признаю свою бессилие :)
                                                                                              Поэтому ни Smalltalk, ни Lisp и не захватили мейнстрим… среднестатистическому программисту банально не удобно с любой униформностью.
                                                                                              Давайте не будем касаться это темы: вы не в курсе, почему Smalltalk «не завхватил мейнстрим», да и «среднестатистический программист» не излагал вам свои проблемы.
                                                                                                0
                                                                                                … или для реализации.
                                                                                                В Lisp же принцип реализован, можно униформно программировать на голом AST, а синтаксический сахар всё равно добавляют.

                                                                                                Из ваших высказываний получается, что логика такая: нет систем, которые бы я знал и где соблюдался этот принцип => принцип не нужен
                                                                                                Не совсем, во-первых системы есть. А во-вторых, логика такая: увеличение уровня чистоты принципа не даёт соразмерного увеличения практической пользы. И даже наоборот отход от чистоты частенько позволяет решать задачи эффективнее.
                                                                                                К примеру, b squared — чище, b * b — понятнее
                                                                                                a +: b — чище (только не работает почему-то),
                                                                                                a + b — понятнее.
                                                                                                  0
                                                                                                  К примеру, b squared — чище, b * b — понятнее
                                                                                                  Ну, напишите там b * b — это тоже сообщение. Только squared как раз показывает намерение и понятнее, вы же сами сформулировали: «сумма квадратов», а не «сумма произведений на самого себя».
                                                                                                  увеличение уровня чистоты принципа не даёт соразмерного увеличения практической пользы
                                                                                                  От принципа, я так понимаю, это по-вашему не зависит?
                                                                                                    0
                                                                                                    Ну, напишите там b * b — это тоже сообщение.
                                                                                                    Я хотел написать b *: b, чтобы униформно было, но оказалось, что так нельзя (

                                                                                                    Только squared как раз показывает намерение и понятнее, вы же сами сформулировали: «сумма квадратов»
                                                                                                    С этим согласен… Только вот мне интересно, для кубов тоже библиотечный метод есть? )

                                                                                                    От принципа, я так понимаю, это по-вашему не зависит?
                                                                                                    Это уже философия… но вообще да, если принцип направлен на предельную минимизацию кол-ва концепций.
                                                                                                    Потому что на каком-то этапе наступает конфликт с другим принципом: «Всё должно быть так просто, как только возможно, но не проще»
                                                                                                      0
                                                                                                      Я хотел написать b *: b, чтобы униформно было, но оказалось, что так нельзя (
                                                                                                      В синтаксисе Smalltalk-а предусмотрено три вида сообщений (в их названиях можно проследить функциональные корни, кстати — получатель считается как аргумент): унарные (без аргументов; squared — как раз пример такого), бинарные (ровно один аргумент; + и * — примеры) и «с ключевыми словами» (сколько угодно аргументов; 1 to: 10 do: [] — два аргумента: 10 и []).

                                                                                                      А что значит «униморфно»? Не знаю такого слова.

                                                                                                      Только вот мне интересно, для кубов тоже библиотечный метод есть? )
                                                                                                      Может и есть — зависит от реализации, я точно не помню. Но вся фишка в том, что если «нету, а нужен», то просто берете и добавляете. Мы этого, вроде как, даже не касались? «Все объект» — означает в том числе и то, что я могу изменить в системе все что угодно. В том числе «библиотечные» объекты. Но вы же скажете, что это не нужно…
                                                                                                        +1
                                                                                                        «Все объект» — означает в том числе и то, что я могу изменить в системе все что угодно.

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

                                                                                                          0
                                                                                                          Можно иметь систему, в которой все объект, но при этом ничего не мочь изменить.
                                                                                                          Зависит от нашего желания. Если у нас «все объект», и мы заложили в понятие объекта (не в смысле определения) возможность менять его поведение (что логично для системы, в которой мы собираемся что-то разрабатывать), то мы сможем это делать с любым объектом (если, опять же — по своему желанию — не захотим это запретить). На случай если захочется здесь развернуть дискуссия — я не собираюсь в этот тезис «упарываться». По факту в (более-менее) «нормальных» объектных системах (Smalltalk, Self — как минимум) это так.
                                                                                                            0
                                                                                                            Зависит от нашего желания.

                                                                                                            Вот именно, что зависит. И если у нас было такое желание, то мы его реализовали, и с постулатом "все есть объект" оно не связано.


                                                                                                            . Если у нас «все объект», и мы заложили в понятие объекта (не в смысле определения) возможность менять его поведение (что логично для системы, в которой мы собираемся что-то разрабатывать

                                                                                                            А вот для меня это не логично. Я хочу, чтобы части системы были стабильны, и их нельзя было менять.

                                                                                                              0
                                                                                                              Я хочу, чтобы части системы были стабильны, и их нельзя было менять.
                                                                                                              Ну и не меняйте.
                                                                                                                0

                                                                                                                Нельзя было.

                                                                                                                  0
                                                                                                                  Запретите.
                                                                                                                    0

                                                                                                                    … вот и нет возможности менять поведение любого объекта.

                                                                                                                      0
                                                                                                                      Это было ваше желание — соответственно ваша проблема. Собственно, в мейнстриме так и делают, а потом борются с созданными проблемами.
                                                                                                                        0

                                                                                                                        Вот только у меня нет проблемы: я не считаю, что отсутствие возможности поменять стандартную библиотеку — проблема.


                                                                                                                        Ну и да, напомню, что это обсуждение началось с того, что тезис "все есть объект" означает возможность что угодно поменять. Согласитесь, что это верно только в том случае, если вы можете поменять любой объект, а не всегда.

                                                                                                                          0
                                                                                                                          Да пожалуйста! Если вам нравится делать одно и то же много раз — это не связанные вещи.

                                                                                                                          А вот если вам вдруг покажется логичным сделать один объект с «базовой функциональностью», включающей возможность создавать новые объекты с отличным от исходного поведением (а значит, менять их) — исходный тезис вдруг может показаться более осмысленным. Но я не настаиваю.
                                                                                                                            0

                                                                                                                            … а если мне покажется логичным сделать один модуль с базовой функциональностью, включающей возможность создавать новые модули с отличающимся поведением — то у меня "все есть модули", а возможность изменения осталась.


                                                                                                                            Вообще, связывать ООП и легкость повторного использования — это как раз та ловушка, которая приводит к "ООП — это наследование".

                                                                                                                              0
                                                                                                                              А я где-то утверждал, что возможность все изменить есть только в ООП?
                                                                                                                                0

                                                                                                                                Нет, вы говорили, что из тезиса "все есть объект" автоматически вытекает изменяемость чего угодно.

                                                                                                                                  0
                                                                                                                                  И я объяснил почему я допустил такую неточность, не так ли?
                                                                                                          0
                                                                                                          А что значит «униморфно»? Не знаю такого слова.
                                                                                                          А почему Вы меня об этом спрашиваете? Я это слово не употреблял.

                                                                                                          я могу изменить в системе все что угодно. В том числе «библиотечные» объекты. Но вы же скажете, что это не нужно…
                                                                                                          Мне этого в Ruby хватило выше крыше. Поверьте, на практике при разработке крупных проектов это ни фига не круто.
                                                                                                            0
                                                                                                            А что значит «униморфно»? Не знаю такого слова.

                                                                                                            А почему Вы меня об этом спрашиваете? Я это слово не употреблял.
                                                                                                            Прошу прощения, «обчитался» :)
                                                                                                            Поверьте, на практике при разработке крупных проектов это ни фига не круто.
                                                                                                            «Вы просто не умеете их готовить» :) Либо зря обобщаете свой негативный опыт Ruby.
                                                                                                              +1
                                                                                                              Проблема не в «готовке», а в том, что десятки программистов, не входящие в одну команду, меняют одни и те же классы.
                                                                                                              Приходится всякие хитрости придумывать. Чтобы очередное обновление зависимостей не сломало эти переопределения.
                                                                                                              А как эта проблема решается в Smalltalk?
                                                                                                                0
                                                                                                                Я TDD-шник, и побочный эффект этого — тесты — обычно помогают. Smalltalk здесь особо не выделяется, разве что TDD в нем проходит с минимальным «скрипом». А во стольном — все как и везде.
                                                                                                                  0
                                                                                                                  Не, погодите, что значит «как везде»? Далеко не везде можно переопределить любой метод/функцию из чужой библиотеки, не прибегая к форкам и пулл-реквестам )
                                                                                                                  Тесты — ok, допустим у нас 200% покрытия и они даже упали:
                                                                                                                  1) Как быстро понять, что дело именно в переопределении? Вы ведь не пишете тесты на стандартную библиотеку и на все зависимости.
                                                                                                                  2) Как быстро найти какая из зависимостей в этом виновата?
                                                                                                                    0
                                                                                                                    Далеко не везде можно переопределить любой метод/функцию из чужой библиотеки, не прибегая к форкам и пулл-реквестам )
                                                                                                                    Везде есть код, который не контролирует автор и который при обновлении может сломать то, что автор написал.
                                                                                                                    1) Как быстро понять, что дело именно в переопределении? Вы ведь не пишете тесты на стандартную библиотеку и на все зависимости.
                                                                                                                    Тест есть на поведение моего объекта, непосредственно зависящего от не моего объекта.
                                                                                                                    2) Как быстро найти какая из зависимостей в этом виновата?
                                                                                                                    Я сталкивался с таким когда-то в VisualWorks — там (насколько я помню) приходилось искать. Но метод же — объект. Нет проблем протащить туда информацию о том, откуда растут ноги. Так ли это в Pharo — надо уточнить. Почему-то больших проблем с этим не возникало — может из-за (меньшего) размера сообщества, может из-за того что не злоупотребляют.
                                                                                              0
                                                                                              Честно говоря, не понимаю в чём проблема. Мы в жизни постоянно переключаемся между сущностями различного рода, можно сказать, это дефолтное восприятие человека.
                                                                                              Вот, как раз некоторые считают, что дефолтное состояние человека — воспринимать окружающий мир и все сущности в нем как объекты: идентифицировав объект, мы начинаем пытаться на него воздействовать и смотреть, что же будет в ответ. Вроде как это более общая метафора, чем выполнение операций (из наперед заданного, строго ограниченного множества), поскольку я могу попытаться что угодно сделать с объектом — нет ограничений по возможным операциям. Все это спорно, конечно…
                                                                                              Вы по-любому переключаетесь, когда говорите о массивах, строках, числах. Просто делаете вид, что это типа тоже объекты. Если бы можно было бы оставаться всегда на уровне объектов, не переключаясь, Вы бы названия обычных типов данных вообще забыли бы.
                                                                                              Я переключаюсь только между ожидаемым поведением разных объектов и его соответствием тем задачам, которые я хочу решить. Я не переключаюсь между разными способами общения с объектами. Если возникает необходимость так делать, приходится отвлекаться — поток прерывается — решать задачу становится сложнее и дольше. Да, большинство из нас привыкло работать в таком режиме — это не значит, что такой режим лучший.

                                                                                              После долгого (многомесячного, скажем) программирования на Java (где тоже постоянно приходится скакать, так как сам язык «мало-объектный») к Smalltalk привыкаешь минут за 15 и с удовольствием. После даже часового программирования на Smalltalk обратно к Java привыкаешь гораздо дольше и, что самое противное, с неудовольствием. И сразу чувствуешь, как падает производительность — постоянно с объектного мышления по схеме «найти подходящий объект и передать ему нужное сообщение» приходится перепрыгивать на языковые конструкции. Если «к хорошему привыкаешь быстро» говорят не зря, то становится понятным что есть хорошо, а что не очень.

                                                                                              Но, да — это мои личные ощущения. Более объективных обоснований (кроме попыток формально «поймать простоту» — но там пока мне самому не все ясно) у меня нет. Так что, разумеется, есть полное право не соглашаться и продолжать совмещать «типы и объекты». А для меня этот момент является хоть и не самым сильным, но все же аргументом против подробного знакомства с Erlang для изучения ООП.
                                                                                                0
                                                                                                Вот, как раз некоторые считают, что дефолтное состояние человека — воспринимать окружающий мир и все сущности в нем как объекты: идентифицировав объект, мы начинаем пытаться на него воздействовать и смотреть, что же будет в ответ.
                                                                                                Как минимум, мы отличаем объекты с поведением (акторы) от объектов без поведения (данные) и принципиально по-разному к ним относимся. Благодаря чему у нас не возникает дилеммы «человек пишет ручкой» или «ручка пишет, используя человека».
                                                                                                  0
                                                                                                  Как минимум, мы отличаем объекты с поведением (акторы) от объектов без поведения (данные) и принципиально по-разному к ним относимся. Благодаря чему у нас не возникает дилеммы «человек пишет ручкой» или «ручка пишет, используя человека».
                                                                                                  Ручка у вас — это данные?
                                                                                                    0
                                                                                                    Да, структура данных. Она обладает свойствами, но не обладает собственным поведением.
                                                                                                      0
                                                                                                      А какие свойства есть у ручки?
                                                                                                        –1
                                                                                                        Да полно у неё свойств, а какие переносить в программу зависит от задачи. Можно, например, тип и цвет.
                                                                                                          0
                                                                                                          Ну, вы же сами предложили этот пример. Очень показательно может быть, я считаю. Поэтому предлагаю его проработать.

                                                                                                          Если вы не против, то давайте все-таки уточним набор свойств, а так же что понимается под типом?
                                                                                                            0
                                                                                                            Под типом подразумевается тип ручки, допустим: с колпачком или с кнопкой. В зависимости от него актор выберет стратегию работы с ручкой, ну или кинет исключение, если он не умеет работать с ручкой неопознанного типа.
                                                                                                            А по поводу набора свойств, чем Вас не устраивает предложенный? Для большинства реальных применений ручек указанного набора свойств вполне хватает. Естественно, у неё как у любого физического объекта очень много свойств: масса, объём, длина, диаметр корпуса, цвет корпуса и т.д. Но это всё очень редко имеет значение. Так что «тип и цвет чернил» вполне достаточный набор.
                                                                                                              0
                                                                                                              Хорошо. А если я захотел дать возможность писать карандашом? пером с чернильницей? палочкой на глиняных таблицах? «пальцем на запотевшем стекле трамвая»?… Я для каждого нового «типа» ручки должен обучать актор работать с ним?
                                                                                            –1
                                                                                            > Попробуйте в ООП-стиле написать код для вычисления суммы квадратов натуральных чисел от 1 до n. Для примера реализация на Elixir:
                                                                                            Попробуйте в функциональном стиле описать работу с пользовательским интерфейсом. Я понимаю, тут постановка задачи — показать несостоятельность ООП подхода, но, например, плюсы
                                                                                            Number Range::reduce(Range range, std::function[Number(Number,Number)],Number init = Number(0)) {
                                                                                            Number val(init); for (i: range) val = function (val, ш); return val;
                                                                                            }
                                                                                            //struct fn { Number operator() (Number a, Number b) {return a + b*b;} };
                                                                                            auto fn = [](Number a,Number b){return a + b*b;};
                                                                                            Range::reduce(Range(1,100),fn); // fn());
                                                                                            Очень лаконично всё описывают.
                                                                                              +2
                                                                                              Я понимаю, тут постановка задачи — показать несостоятельность ООП подхода
                                                                                              Нет, Вы вообще не поняли, что мы в этой ветке обсуждаем (невозможность униформности на всех уровнях системы), и даже статью, видимо, не дочитали.

                                                                                              но, например, плюсы
                                                                                              В Вашем примере куча не-ООП… 0 — это не объект в плюсах, for — не метод, +, * и даже return — это тоже не методы. Так что на униформную ООП-реализацию это точно не тянет.
                                                                                              Очень лаконично всё описывают.
                                                                                              А вот это Вы зачётно пошутили )))
                                                                                              +3
                                                                                              Вот на мой взгляд, добавление к числу объектного поведения капитально усложняет работу с числами, зачем тогда мне рекурсивно превращать их в объекты?

                                                                                              Очень не хотел влезать в спор, но все же хочу кое-что сказать по этому поводу. Лично для меня, придание примитивным типам объектности, всегда имело смысл не в том, чтобы, собственно, работать с примитивными типами, как с объектами, а в том, чтобы иметь возможность работать с объектами, как с примитивными типами. Просто если в языке нет разницы между примитивным типом и объектом (а основной смысл добавления объектности к примитивному типу в том, чтобы стереть эту разницу), то я могу создать свой тип, который со стороны сможет вести себя так же как, например, дробное число (реализовывать все его интерфейсы), а внутри все будет устроено так, как мне надо. Вот не понравилось мне, что есть только float и double. Мало мне показалось. Написал свой тип и назвал triple. И он ничем не «хуже» встроенных типов. Язык получается расширяемым. Можно так же добавить quadruple, quintuple и т.д. пока не надоест.
                                                                                              Что касается усложнения примитивных типов, то лично я его не заметил. Серьезно, я ни разу не замечал, чтобы мне, например, в C# делать арифметику со встроенными типами было сложнее, чем в Си. Внутри типы может быть и сложнее устроены, но мне-то, как разработчику, пишущему на этом языке, что с того? Я работаю с ними не внутри, а снаружи, как с черными ящиками. Зато, если понадобится использовать нестандартный числовой тип, то в том же Си будет существенное синтаксическое отличие. Вот решили мы отказаться от чисел с плавающей точкой, и до последнего все расчеты делать в простых дробях. По сути, все операции над пользовательской структурой (числитель/знаменатель) придется делать с помощью функций. А в C# достаточно заменить тип у переменных, а все остальное останется как было. Куда уж проще? Я не говорю, что без ООП подобного не добиться. И даже не говорю, что подобный функционал необходим. Просто не вижу проблем от того, что простые типы стали объектами. Зато у нас появляется унификация. Все типы равны между собой. И даже встроенные типы не «равнее» пользовательских. По-моему это здорово.
                                                                                                +2

                                                                                                … вот только для описанного вами не надо, чтобы примитивные типы были объектами. Надо, чтобы в языке было как можно меньше операций, на которые вы не можете повлиять. В частности, в вашем случае вы можете определить математические операции (но, к сожалению, не операции в модуле Math) не только для встроенных математических типов, но и для своих.


                                                                                                Все типы равны между собой.

                                                                                                Вот в C#-то это точно не так. Чего стоит одна только разница между value types и reference types.

                                                                                                  0
                                                                                                  Надо, чтобы в языке было как можно меньше операций, на которые вы не можете повлиять.
                                                                                                  …а в идеале?…
                                                                                                    0

                                                                                                    А в идеале — ни одной.

                                                                                                  0
                                                                                                  Лично для меня, придание примитивным типам объектности, всегда имело смысл не в том, чтобы, собственно, работать с примитивными типами, как с объектами, а в том, чтобы иметь возможность работать с объектами, как с примитивными типами.
                                                                                                  Хорошее дополнение в дискуссию. На мой взгляд, для расширения примитивных типов хватит и структур, над которыми имеется возможность определить любые операторы. Так ведь?

                                                                                                  я ни разу не замечал, чтобы мне, например, в C# делать арифметику со встроенными типами было сложнее, чем в Си.
                                                                                                  Загляните в IL-код, там нет никаких объектов для чисел. Но не задумываясь о разнице между числами и объектами, Вы легко можете написать код, где в цикле возникнет какой-нибудь box int32. И удар по производительности обеспечен. Смысл не в том, что нельзя работать с числами как с объектами. Смысл в том, что нельзя забывать, что число — это число, а объектом оно только притворяется. Поэтому переключение сознания между объектами и примитивными типами данных неизбежно для написания нормального кода.
                                                                                                  0
                                                                                                  Вот на мой взгляд, добавление к числу объектного поведения капитально усложняет работу с числами,
                                                                                                  Забыл уточнить: на основании чего вы это утверждаете?
                                                                                                    0
                                                                                                    в комментарии выше есть об этом, автоупаковки/распаковки уже достаточно, чтобы сильно усложнить восприятие того, что вообще происходит
                                                                                                      0
                                                                                                      Раз восприятие, значит это относится к программисту по всей видимости? Тогда каким образом это усложняет его восприятие, если он про это даже не знает?
                                                                                                        0

                                                                                                        … если про это не знать, то можно получить много прекрасных побочных эффектов, начиная с уже описанной невозможности блокировки.

                                                                                                          0
                                                                                                          Мы же уже обсуждали эти самые «побочные эффекты»? Или я уже путаюсь? :)

                                                                                                          Если обсуждали, повторюсь: «побочные эффекты» (в широком смысле слова) являются следствием неудачной реализации; при их обнаружении — поправьте ее так, чтобы поведение объектов было ожидаемым. Не всегда это просто. Более того, допускаю, что это и не всегда возможно — но это не доказано. Я на практике таких примеров не встречал.
                                                                                                            0
                                                                                                            при их обнаружении — поправьте ее так, чтобы поведение объектов было ожидаемым

                                                                                                            Легко сказать.


                                                                                                            d = a
                                                                                                            c = a add b

                                                                                                            Чему равно d?


                                                                                                            d = a
                                                                                                            c = a + b

                                                                                                            А теперь?

                                                                                                              0
                                                                                                              d равно a в обоих случаях, если сравнение a c самим собой дает «истина»
                                                                                                                +1

                                                                                                                А вот и нет Если при d = a происходит копирование, а при a + b — увеличение a, то d не равно a.


                                                                                                                Впрочем, более интересный вопрос состоит в том, равны ли d и c.

                                                                                                                  0
                                                                                                                  А вот и нет
                                                                                                                  Так вы пример чистого ООП рассматриваете? У вас d = a — это сообщение = a, посылаемое d? Вы бы формулировали вопросы как-то поаккуратнее и точнее что ли? А то сложно следить за полетом вашей мысли. И создается впечатление, что вы стараетесь просто «подловить» собеседника.

                                                                                                                  В этом случае вообще ничего не гарантируется, да. Одни сплошные «побочные эффекты» в широком смысле этого слова. Но вы-то говорите о тех из них, которые приводят к неправильному поведению софта, не так ли? Придется еще подбирать правильные объекты (которые не будут копировать, когда требуется в переменную положить объект).

                                                                                                                    +1
                                                                                                                    У вас d = a — это сообщение = a, посылаемое d?

                                                                                                                    А это не принципиально. Это может быть как сообщение, так и поведение языка.


                                                                                                                    В этом случае вообще ничего не гарантируется, да. Одни сплошные «побочные эффекты» в широком смысле этого слова. Но вы-то говорите о тех из них, которые приводят к неправильному поведению софта, не так ли?

                                                                                                                    Так побочные эффекты формата "изменилось состояние объекта, от которого я этого не ожидал" — как раз самые страшные (и, кстати, именно поэтому в функциональном программировании так любят неизменное состояние).