Функциональное программирование: дурацкая игрушка, которая убивает производительность труда. Часть 1

Автор оригинала: Ilya Suzdalnitski
  • Перевод
Возможно, вы уже слышали о так называемом «функциональном» программировании. Возможно, вы даже подумываете о том, что вам стоит его как-нибудь попробовать.

Ни в коем случае этого не делайте!



Функциональное программирование полно недочётов, оно не подходит для реальных проектов. Его применение приведёт к резкому падению производительности труда. Почему это так? Давайте выясним.

▍→ Часть 2

Функциональное программирование не может удовлетворить многогранным корпоративным требованиям



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

Полагаю, вышеприведённый текст особенно понятным не выглядит. Но совсем скоро всё встанет на свои места.

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

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

import { filter, first, get } from 'lodash/fp';

const filterByType = type =>
  filter( x => x.type === type );

const fruits = [
  { type: 'apple', price: 1.99 },
  { type: 'orange', price: 2.99 },
  { type: 'grape', price: 44.95 }  
];

const getFruitPrice = type => fruits =>
  fruits
  |> filterByType(type)
  |> first
  |> get('price');

const getApplePrice = getFruitPrice('apple');

console.log('apple price', getApplePrice(fruits));

Если это всё ничего кроме злости у вас не вызывает, то знайте, что вы в этом не одиноки!

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

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

Функциональные программные решения не выдерживают проверку временем



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

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

class Fruit {
  constructor(type, price) {
    this.type = type;
    this.price = price;
  }
}

class AbstractFruitFactory {
  make(type, price) {
    return new Fruit(type, price);
  }
}

class AppleFactory extends AbstractFruitFactory {
  make(price) {
    return super.make("apple", price);
  }
}

class OrangeFactory extends AbstractFruitFactory {
  make(price) {
    return super.make("orange", price);
  }
}

class GrapeFactory extends AbstractFruitFactory {
  make(price) {
    return super.make("grape", price);
  }
}

class FruitRepository {
  constructor() {
    this.fruitList = [];
  }

  locate(strategy) {
    return strategy.locate(this.fruitList);
  }

  put(fruit) {
    this.fruitList.push(fruit);
  }
}

class FruitLocationStrategy {
  constructor(fruitType) {
    this.fruitType = fruitType;
  }

  locate(list) {
    return list.find(x => x.type === this.fruitType);
  }
}

class FruitPriceLocator {
  constructor(fruitRepository, locationStrategy) {
    this.fruitRepository = fruitRepository;
    this.locationStrategy = locationStrategy;
  }

  locatePrice() {
    return this.fruitRepository.locate(this.locationStrategy).price;
  }
}

const appleFactory = new AppleFactory();
const orangeFactory = new OrangeFactory();
const grapeFactory = new GrapeFactory();

const fruitRepository = new FruitRepository();
fruitRepository.put(appleFactory.make(1.99));
fruitRepository.put(orangeFactory.make(2.99));
fruitRepository.put(grapeFactory.make(44.95));

const appleLocationStrategy = new FruitLocationStrategy("apple");

const applePriceLocator = new FruitPriceLocator(
  fruitRepository,
  appleLocationStrategy
);

const applePrice = applePriceLocator.locatePrice();

console.log("apple", applePrice);

Как видите, вся основная функциональность здесь качественно абстрагирована. Этот — цельный код.

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

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

Серьёзный менеджмент нуждается в серьёзных возможностях



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

Как известно любому настоящему проект-менеджеру, трудящемуся в корпоративной среде, реальную ценность для бизнеса представляют лишь новые возможности приложений.

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

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

Следующий пример ясно показывает неполноценность функционального программирования. Использование этой методологии слишком сильно упрощает рефакторинг:

// до рефакторинга:

// calculator.js:
const isValidInput = text => true;

const btnAddClick = (aText, bText) => {
  if (!isValidInput(aText) || !isValidInput(bText)) {
    return;
  }
}


// после рефакторинга:

// inputValidator.js:
export const isValidInput = text => true;

// calculator.js:
import { isValidInput } from './inputValidator';

const btnAddClick = (aText, bText, _isValidInput = isValidInput) => {
  if (!_isValidInput(aText) || !_isValidInput(bText)) {
    return;
  }
}

Если вас воротит от простоты этого рефакторинга — знайте, что такие ощущения испытываете не только вы. До рефакторинга было шесть строк кода, после стало семь строк? Это что — шутка?

Сравним это с нормальным рефакторингом объектно-ориентированного кода:

// до рефакторинга:
public class CalculatorForm {
    private string aText, bText;
    
    private bool IsValidInput(string text) => true;
    
    private void btnAddClick(object sender, EventArgs e) {
        if ( !IsValidInput(bText) || !IsValidInput(aText) ) {
            return;
        }
    }
}


// после рефакторинга:
public class CalculatorForm {
    private string aText, bText;
    
    private readonly IInputValidator _inputValidator;
    
    public CalculatorForm(IInputValidator inputValidator) {
        _inputValidator = inputValidator;
    }
    
    private void btnAddClick(object sender, EventArgs e) {
        if ( !_inputValidator.IsValidInput(bText)
            || !_inputValidator.IsValidInput(aText) ) {
            return;
        }
    }
}

public interface IInputValidator {
    bool IsValidInput(string text);
}

public class InputValidator : IInputValidator {
    public bool IsValidInput(string text) => true;
}

public class InputValidatorFactory {
    public IInputValidator CreateInputValidator() => new InputValidator();
}

Именно так выглядит настоящее программирование! До рефакторинга было девять строк кода, а после стало 22. Для выполнения этой работы требуется приложить больше усилий. Это заставит программистов, работающих на компанию, дважды подумать перед тем, как ввязываться в подобную авантюру.

Ущербная концепция декларативного подхода к программированию



Так называемые «функциональные» программисты неоправданно гордятся собой из-за того, что пишут декларативный код. Но тут нечем гордиться — подобный код лишь создаёт иллюзию продуктивности.

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

Взглянем на хорошо абстрагированный объектно-ориентированный код:

class CountryUserSelectionStrategy {
  constructor(country) {
    this.country = country;
  }
  
  isMatch(user) {
    return user.country === this.country;
  }
}

class UserSelector {
  constructor(repository, userSelectionStrategy) {
    this.repository = repository;
    this.userSelectionStrategy = userSelectionStrategy;
  }
  
  selectUser() {    
    let user = null;

    for (const u in users) {
      if ( this.userSelectionStrategy.isMatch(u) ) {
        user = u;
        break;
      }
    }
    
    return user;
  }
}

const userRepository = new UserRepository();
const userInitializer = new UserInitializer();
userInitializer.initialize(userRepository);

const americanSelectionStrategy = new CountryUserSelectionStrategy('USA');
const americanUserSelector = new UserSelector(userRepository, americanSelectionStrategy);

const american = americanUserSelector.selectUser();

console.log('American', american);

Присмотритесь к императивному циклу for (const u in users). Не обращайте внимания на второстепенный шаблонный объектно-ориентированный код, не связанный с выполняемой задачей. Его нужно было включить в программу для того, чтобы сделать этот пример соответствующим жёстким требованиям абстрагирования, предъявляемым любой серьёзной организацией к коду.

Декларативный код, с другой стороны, является слишком конкретным, он, что неправильно, заставляет разработчика ориентироваться на менее важные вещи вроде бизнес-логики. Сравните мощное решение корпоративного уровня, описанное выше, со следующим образцом неполноценного «декларативного» кода:

SELECT * FROM Users WHERE Country=’USA’;

Каждый раз, когда я вижу SQL-код, меня прямо-таки коробит от его декларативности. Почему SQL? Почему бы программистам не использовать адекватные абстракции корпоративного класса и не писать бы нормальный объектно-ориентированный код? Особенно учитывая то, что в их распоряжении уже есть всё необходимое. Это — просто взрыв мозга, иначе и не скажешь.

Моделирование реального мира



Объектно-ориентированное программирование — это гениально. В отличие от «функционального» программирования оно отлично подходит для моделирования объектов реального мира. Это возможно благодаря тому, что ООП поддерживает такие продвинутые технологии, как наследование, полиморфизм и инкапсуляция.

Любой уважающий себя программист должен ежедневно использовать наследование для достижения высокого уровня многократного использования кода. Как уже было сказано, наследование отлично подходит для моделирования реального мира. Кошки, например, всегда наследуют свои свойства и поведение от единственного абстрактного животного из реального мира. Жизнь зародилась в океане несколько миллиардов лет назад. В результате все млекопитающие (включая кошек) унаследовали свойства от некоей первозданной рыбы. Например, говоря объектно-ориентированным языком, это может быть нечто вроде garfield.fishHead — свойства, описывающего рыбью голову кота Гарфилда. То же самое можно сказать и о поведении, что, в терминологии ООП может выглядеть как garfield.swim() (плавание) и garfield.layCaviar() (икрометание). Никого не удивляет то, что кошки так сильно любят принимать ванны и плавать! Люди, в сущности, это то же самое. Человек, если захочет, легко может начать метать икру!

Наши программы всегда должны следовать похожим иерархическим подходам к организации кода. Функциональное программирование неоправданно лишает программистов замечательных конструкций по совместному использованию кода, создателей которых вдохновила сама природа. У этого есть далеко идущие последствия — особенно если речь идёт о серьёзных сложных корпоративных приложениях.

Функции всегда должны быть привязаны к объектам



То, что функции должны быть всегда привязаны к объектам, подсказывает нам здравый смысл. Кроме того, это отлично воспроизводит то, что мы можем видеть в реальном мире. В блокнотах имеется встроенный метод «записать». Это метод вызывается каждый раз, когда вы планируете что-то записать в блокнот. Вы можете этого и не осознавать, но и у вас есть методы. Например — нечто вроде .eat(veggies), позволяющий вам есть овощи, и .doHomework(), благодаря которому вы делали домашние задания когда учились в школе. Это — просто здравый смысл. Как иначе ваша мама, когда вы были помладше, заставляла бы вас есть овощи и делать домашние задания? Конечно, она напрямую вызывала эти ваши методы!

Ни одну работу в реальном мире нельзя выполнить, не привлекая профессионального менеджера, координирующего задачи. Его можно представить в виде объекта Manager. Молодёжи, возможно, нужен менеджер, помогающий удовлетворять базовые человеческие потребности, ну, скажем, вроде «смотреть Netflix и отдыхать».

Кто, в конце концов, будет координировать все составные части этого сложного процесса? А люди достаточно умные могут нанять и несколько менеджеров, поступив в точности так, как рекомендует ООП.

В реально мире создание чего-то нового и интересного, кроме того, требует специальной фабрики, которую можно представить в виде объекта Factory. У Леонардо да Винчи, например, была фабрика по производству картин — MonaLisaFactory, а Дональд Трамп строит секретную фабрику WallFactory.

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

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

Функциональное программирование не даёт возможностей для профессионального роста



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

Во-первых, программисту нужно изучить продвинутые ООП-техники вроде наследования, абстракции, инкапсуляции и полиморфизма. Затем нужно хорошо освоить тьму паттернов проектирования (вроде паттерна «Синглтон») и начать использовать их в своём коде. Существует около 30 базовых паттернов проектирования, которые нужно очень хорошо знать. В идеале где-то в процессе изучения паттернов программист должен начать использовать в своём коде различные абстракции корпоративного уровня.

Следующий шаг — ознакомление с технологиями наподобие Domain-Driven Design, и изучение того, как разделять на части монолитные программные проекты. Кроме того, рекомендовано изучение подходящих инструментов для рефакторинга кода вроде Resharper, так как рефакторинг объектно-ориентированного кода — задача нетривиальная.

Для того чтобы достичь достойного уровня в сфере ООП нужно 20-30 лет. Но надо отметить, что даже большинство тех, у кого наберётся 30 лет опыта, не могут считаться настоящими мастерами ООП. Путь ООП-ученика тяжёл и наполнен неопределённостью. Объектно-ориентированного разработчика, в результате, ждёт учёба длиною в жизнь. Это ли не прекрасно?

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

Продолжение следует…

Уважаемые читатели! Что вам больше всего не нравится в функциональном программировании?

RUVDS.com
1 072,98
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 394

    +89

    Слишком толсто. Попробуйте еще раз.

      +8

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


      Ну и да, ФП и правда легче. В ФП паттернов-то всего ничего: IO/Reader/Middleware/..., и большинство из них монады, поэтому их и компоновать как становится понятно, даже если сам паттерн не до конца понятен.

        +16
        Осознал что читаю саркастическую статью, когда в качестве примера ООП кода вытащили какую-то дрянь наружу.
        Автор либо не умеет в ООП, либо пытается рассказать об ООП намеренно ухудшая код.
      +9

      С SQL тут все-таки переборщили: это и правда неудобный язык. Расположение SELECT перед FROM приводит к постоянным использованием имён таблиц перед их объявлением, а это ломает контекстные подсказки в IDE. Не говорю уже о том, что мне вообще неизвестны редакторы SQL, которые бы не тупили и не тормозили.


      Ну и фабрики тут упомянуты зря: после появления делегатов/лямбд/стрелочных функций в известных мне языках они выглядят куда компактнее. Не говоря уже о том, что в представленной задаче шаблон Abstract Factory вообще не нужен.

        +8
        Поэтому мы в DataGrip придумали postifix-completion :) d3nmt5vlzunoa1.cloudfront.net/datagrip/files/2019/03/02-PostfixCompletion.gif
          +2

          Классно, но не интуитивно понятно.

            0
            Согласен. Надо просто знать, что он есть :)
          –1
          делегатов/лямбд/стрелочных функций

          Так это все делалось для приближения ООП к ФП.
            +2

            Но от "приближения" оно не перестаёт быть ООП. Однако, оно перестаёт содержать страшные классы, введенные ради всего одного метода.

              +1
              Где я утверждал, что это для замены ООП? В данном случае в современных языках у нас есть выбор придерживаться чистого ООП, без вставок ФП или немного комбинировать оба подхода, чтобы добиться хороших результатов.
                +1

                Если вы не утверждали, что ООП перестало быть ООП — то непонятно с чем вы спорите.

                  +2

                  Это больше карго-культ, если честно. Смысл вышек и самолетов… Простите, лямбд и стрелочных функций не в том, чтобы просто быть.

                  +2
                  Но это не нечто новое, привнесенно-ФПшное. Комбинирование ООП подхода с самыми обычными императивными функциями/процедурами без нужды держать классы ради одного метода без сохранения контекста есть, скажем, в том же Object Pascal-Delphi…
                    0

                    Вы сейчас точно мне отвечаете?

                      0
                      Воспринимайте это как дополнение, а не возражение.
                0
                если ты сказал про селект. То куда удобнее, компактнее и читабольшее исользовать синтаксис ФП например стрима:
                select(actor, id, last_name -> text) -> filter(text -> text.startswith('A')).
                При этом фича ФП будет также присвоение функции имени переменной, то есть с возможностью использовать опять:
                aactor_queuery = select(actor, id, last_name -> text) -> filter(text -> text.startswith('A'))
                aactor_queuery -> filter(...)

                также рекурсия что то вроде:
                select_rec = select(table, id,last_name, next_id) -> filter(next_id in select_rec.id)
                +7
                Только на последнем абзаце понял, что это стёб.
                  +7
                  я это понял вот на этом абзаце:

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


                  Ну не то чтобы так вот сразу понял — в конце концов в мире полно двоечников, которые не смогли толком освоить университетский курс математики для гуманитарных специальностей… Просто интига после этой фразы как то совсем пропала… она, интрига начала сильно болеть на фразе

                  Функциональное программирование полно недочётов, оно не подходит для реальных проектов


                  ибо реальных проектов реализованных на ФЯП как грази… но всегда ведь можно сказать, что они все эти проекты реализованы с использованием неподходящих инструментов — в смысле можно сказать, и тебя за это не арестуют и не наложат штраф за потстрекательство к… к чему нибудь.

                  Но вот после математики которая не находит применения в реальном мире, становится ясно, что либо автор глубоко и безнеадежно болен, либо он имеет университетский диплом юмориста-затейника, но уже много лет не может получить работу по специальности — чтобы определить точнее, нужно доситать статью, а мне уже лень
                    0
                    Я после первого примера ФП кода понял, что что-то не так O_o
                    +10
                    Астрологи объявили неделю жЫрных набросов. Объемы сальной прослойки в районе пуза увеличиваются вдвое.

                    P.S. Годно, очень годно! Ждем часть 2.
                      +4
                      Хороший стеб однако.

                      Но тем не менее, не стоит быть фанатиком чего-то одного. Думаю, всегда можно найти компромис.
                        +45

                        Вот вы попробовали потроллить, а функциональное программирование на самом деле плохо годится для реальных приложений. Примеры:


                        • ФП работает с "чистыми" функциями. Но реальное приложение работает с файлами, БД, внешними сервисами, которые нарушают требования "чистоты", в итоге приходится придумывать костыли
                        • в ФП переменные иммутабельны. Если у вас есть массив из 100 пользователей, и вы должны одному обновить рейтинг, вы должны сделать полную копию массива. И так на каждое изменение. Это негативно влияет на производительность и потребление памяти.(пример: реакт, где на любой чих пересоздается стейт заново и хорошо, если он у вас маленький).
                        • в ФП переменные иммутабельны, а структуры это не объекты и они передаются по значению, а не по ссылке. Там, где вы в PHP пишете $user->updateKarma(), в ФП вы должны писать user2 = updateKarma(user), и не забыть заменить старые копии user на user2 во всех местах кода, во всех коллекциях и списках. Удачи!
                        • в ФП нет исключений. В нормальном программировании вы просто пишете действия подряд, если произойдет ошибка, выбросится исключение и оставшиеся не будут выполняться. В ФП вам приходится лепить костыли, делая типы вроде Maybe и делая "пропуск" функции, если ей передано Maybe с ошибкой внутри.
                        • в ФП нет ООП, которым удобно представлять объекты реального мира. Вместо этого там приходится делать разрозненные структуры и функции для работы с ними — то, для замены чего и придуман ООП. А ведь в ФП вы еще не можете модифицировать объект, с которым работаете.
                        • в ООП функуция это просто последовательность шагов: 1) проверь, что такого емайла нет 2) добавь запись в БД 3) отправь письмо для подтверждения почты. В ФП же так не принято, а принято комбинировать функции в составные функции, из-за чего разбор кода превращается в кошмар (registrator = (formData) => combine(formValiadtor(rules), fieldExtractor('email'), emailNotInDbChecker(db), emailToDbAdder(db), emailSender(db, sendService)).

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


                        var attrs = ['id', 'name', 'age'];
                        attrs.forEach((attr) => {
                        root['get' + attr] = (x) => root[x];
                        }


                        Удачи вам найти определение функции getName поиском.


                        А ФП вроде Хаскелл позволяет создать 10 вариантов функции с одинаковым именем, но разным типом аргументов. Удачи вам найти при рефакторинге, какая из функций (раскиданных по разным файлам) вызывается в том или ином случае.


                        Но, конечно, в программах для вычисления чисел Фибоначчи ФП не знает себе равных.

                          +8
                          в ФП нет исключений

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


                          Любимый паттерн разработчиков ФП. и JS

                          Первый раз такое вижу.


                          Удачи вам найти определение функции getName поиском.

                          Если речь идет о JS — то пишем getName в консоли и дальше браузер сам найдет эту функцию.


                          А ФП вроде Хаскелл позволяет создать 10 вариантов функции с одинаковым именем, но разным типом аргументов

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

                            0
                            Вы забыли, что JS сегодня не только в браузерах работает.
                              +4

                              Запускаем ноду с ключом --inspect и подключаемся через chrome://inspect...

                              0
                              Если речь идет о JS — то пишем getName в консоли и дальше браузер сам найдет эту функцию.
                              О чём вы? Она ведь в замыкании. Никакой функции в глобальном скоупе нету. И что нам это даст? Ну найдём мы её в консоли, что дальше?
                                0
                                О чём вы? Она ведь в замыкании. Никакой функции в глобальном скоупе нету.

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


                                И что нам это даст? Ну найдём мы её в консоли, что дальше?

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

                                  +1
                                  Но ведь откуда-то мы её получили, раз уж вопрос о поиске определения вообще возник? Вот там где получили, ставим точку останова, и теперь она в текущем скоупе есть.
                                  Звучит как безумно удобно, класс! Не то, что ctrl+click в IDE, гадость какая

                                  Напомню, что это функция может вызываться только раз в 3 месяца, когда полная Луна находится в фазе льва. Будете ждать фазу льва, чтобы отдебажить или просто поменяете время на компе?
                                    0
                                    Звучит как безумно удобно, класс! Не то, что ctrl+click в IDE, гадость какая

                                    С другой стороны, ctrl+click в IDE хорошо работает только в пределах проекта, а в javascript можно точно так же увидеть исходники многих библиотек.


                                    Напомню, что это функция может вызываться только раз в 3 месяца, когда полная Луна находится в фазе льва. Будете ждать фазу льва, чтобы отдебажить или просто поменяете время на компе?

                                    Напомню, что нам не нужно ждать пока функция будет вызвана, нам нужно ждать пока она станет видима.

                                      0
                                      в javascript можно точно так же увидеть исходники многих библиотек.
                                      В которые тоже можно прыгнуть по ctrl+click. Если они нормально написаны, конечно.

                                      Напомню, что нам не нужно ждать пока функция будет вызвана, нам нужно ждать пока она станет видима.

                                      1. Что тоже может зависеть от фазы Луны
                                      2. Если функция вызывается только через строковый параметр, то мы можем банально не знать, где она вызывается, пока она не будет вызвана.

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

                                        Если мы не знаем ни где функция вызывается, ни где она определена — то мы вообще не знаем про её существование. Так откуда в таком случае исходная задача взялась?

                                          0
                                          Если в проекте принято вызывать функции через конкатенацию строк, то у нас появляется проклятая медаль с двумя сторонами:
                                          1. Мы не знаем все функции, которые можем вызвать из данного места.
                                          2. Мы не знаем все места, из которых может быть вызвана данная функция.

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

                                            А где вы увидели вызов функции через конкатенацию строк? Я вижу лишь объявление трёх шаблонных функций-акцессоров.

                              +6
                              У Вас получилось чуть потоньше, чем у автора, но всё еще есть куда стремится. Я бы поменял про исключения. Все-таки ```catch(AnyException e) { log(e); }``` – это говнокод даже по меркам современного индус-триального программирования.
                                0

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

                                +1
                                ФП работает с "чистыми" функциями. Но реальное приложение работает с файлами, БД, внешними сервисами, которые нарушают требования "чистоты", в итоге приходится придумывать костыли

                                ООП работает с "объектами". Но реальное приложение работает с файлами, БД, внешними сервисами, которые реализованны на чистом С, в итоге приходится придумывать костыли


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

                                Приложение на хаскелле который делает "копию на каждый чих" скорее всего будет производительнее вашего мутабельного варианта на Java/C#/… И да, copy elision, даже С++ умеет.


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

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


                                в ФП нет исключений.

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


                                в ФП нет ООП, которым удобно представлять объекты реального мира.

                                Это шутка?


                                в ООП функуция это просто последовательность шагов

                                Да, только в ООП у вас 100500 зависимостей IFooBaz у которых тоже миллион зависимостей, что-то куда-то как-то передается, и проследить флоу становится нереально. Особенно если мы какой-нибудь DI заюзаем, с сессионным резолвом зависимостей и т.п. Ух как весело становится.


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

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


                                var attrs = ['id', 'name', 'age'];
                                attrs.forEach((attr) => {
                                root['get' + attr] = (x) => root[x];
                                }

                                Удачи вам найти определение класса реализующего обновление рута поиском.


                                А ООП вроде Java позволяет создать 10 классов наследующихся от одного интерфейса, но разными имплементациями. Удачи вам найти при рефакторинге, какой из классов (раскиданных по разным файлам) вызывается в том или ином случае.
                                  0
                                  Приложение на хаскелле который делает "копию на каждый чих" скорее всего будет производительнее вашего мутабельного варианта на Java/C#/…

                                  Приведите бенчмарк что ли в подтверждение своих слов.


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

                                  А что вы делаете, когда выясняете, что приложение тормозит? Или скажете, что приложения написанные на ФП языке не тормозят?


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

                                  Как видим, асинхронное программирование — та ещё ерунда.

                                    0
                                    А что вы делаете, когда выясняете, что приложение тормозит?

                                    Профилирую, как это делается в любых языках.

                                      0

                                      Дальше-то вы что делаете?

                                        0

                                        Зависит. Чаще всего меняю алгоритм, реже добавляю/убираю {-# LANGUAGE Strict #-} в каком-нибудь из модулей, ещё реже начинаю играться с аннотациями строгости конкретных функций и байндингов. Пару раз за всю практику переписывал код в ST.

                                          0

                                          То есть вам нужно знать как ваш код компилируется, чтобы подшаманить и он компилировался во что-то более эффективное. И не факт, что эти шаманства не дадут обратный эффект при обновлении компилятора. В JS это постоянная беда. JIT компилятор даже между запусками может всё по разному соптимизировать исходя из кучи эвристик. Идиомы типа "передача по ссылке" хотя бы детерминированы.

                                            0

                                            Нет, зачем мне знать, как он компилируется? Речь ведь о семантике выполнения. Компилятор может сделать лучше, если он сам выведет более эффективную strictness annotation, но хуже он не сделает точно.

                                    0
                                    ООП работает с "объектами". Но реальное приложение работает с файлами, БД, внешними сервисами, которые реализованны на чистом С, в итоге приходится придумывать костыли

                                    "Чистый C" часто очень ООПный. Обычно есть "create"/"open"/"new", "delete"/"remove"/"close" и зоопарк функций, принимающих первым аргументом хэндл. Различие с ООП исключительно синтаксическое. Перекладывается подобный API из сишного в ООПный и обратно очень тонкими синтаксическими прослойками.


                                    Приложение на хаскелле который делает "копию на каждый чих" скорее всего будет производительнее вашего мутабельного варианта на Java/C#/… И да, copy elision, даже С++ умеет.

                                    Есть разные алгоритмы, и где-то выгоднее иммутабельность, а где-то она убивает производительность. Если нужна максимальная скорость, алгоритм всё равно будут писать на C++ или подобном языке.

                                    +8
                                    1. Любая работа с ресурсами — это костыли. Вообще все функции кроме чистых — это костыли. Либо это dispodsble, либо это RAII — все одно. Вот только ФП не создает иллюзии, что ты работаешь безопастно.
                                    2. Точно. Это позволяет программисту почаще задумываться о структурах данных, которые он использует — например в вашем примере с пользователями это был бы связный список или хеш, которые были бы практически столь же эффективны в io нагруженных приложениях. Но вот для складывания матриц в GPU ФП скорее всего не подходит.
                                    3. Благодаря function as first class, сопоставлению с образцом и грамотному написанию кода в ФП в большинстве случаев не используется операция присваивания (=) — "переменные" не нужны.
                                    4. Исключения — это костыль. В отличие от монады. И это математически доказал Дейкстра в своих основах структурного программирования, которые исключения жестко нарушают. Кроме того, "костыли" в виде Maybe уже написаны за нас. Использовать их — не сложнее чем написать ключевое слово raise.
                                    5. ФП не противоречит ООП. Кроме того, вызовы u.foo() и foo(u) ничем ни отличаются. Это хорошо видно к примеру в питоне, где метод первый аргументом принимает в обязательном порядке self. Модификация объекта — это головная боль с того момента, как программе появляется многопоточность. А она появляется во всех "серьезных" программах.
                                    6. Это называется декларативное программирование — вершина грамотной архитектуры и композиции программных систем. Приучить мозг, который ходит по шагам, к понимаю декларативных описаний занимает несколько недель — если конечно захотеть.
                                      0

                                      Что, даже линейные типы — костыли? :-)

                                        +1
                                        Любая работа с ресурсами — это костыли.

                                        Ну тогда в ФП есть костыли, ведь оно умеет работать с ресурсами. Как и любой другой язык, весь все они позволяют читать из файлов. Тогда определение безопасное, я уж думал, под костылем понимается что-то плохое.


                                        Точно. Это позволяет программисту почаще задумываться о структурах данных, которые он использует — например в вашем примере с пользователями это был бы связный список или хеш, которые были бы практически столь же эффективны в io нагруженных приложениях. Но вот для складывания матриц в GPU ФП скорее всего не подходит.

                                        Сколько матриц на ООП вы сегодня сложили? Сириузли, когда у вас проблемы с производительностью, вы и ООП использовать не будете, потому что известная AoS vs SoA проблема.


                                        Благодаря function as first class, сопоставлению с образцом и грамотному написанию кода в ФП в большинстве случаев не используется операция присваивания (=) — "переменные" не нужны.

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


                                        4

                                        По этому пункту нет возражений, спасибо за уточнение.


                                        ФП не противоречит ООП.

                                        ФП как правило является подмножеством ООП в некотором смысле. любая функция ФП будет валидной функцией в ООП, но не наоборот, из-за требований к ссылочной прозрачности.


                                        6

                                        Не понял, к чему это. Декларативное описание, конечно, всегда лучше, потому что позволяет думать о бизнес-логике, а не бойлерплейте. Спасибо за еще одно уточнение.

                                          –1
                                          когда у вас проблемы с производительностью, вы и ООП использовать не будете, потому что известная AoS vs SoA проблема.

                                          Если я поравильно понял о чём вы, то это проблема лишь языков типа Java. В нормальных языках объекты можно размещать прямо в массиве. Не без ограничений, конечно, но всё же.

                                            0

                                            Нет, это прежде всего проблема для языков типа Си. В Java с этим все настолько плохо, по умолчанию, что проблема AoS vs SoA уходит на второй план. Но годится в качестве примера важности порядка размещения элементов в памяти.

                                              0

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

                                                0

                                                Ну массив из полиморфных объектов в C++ сделать не получится. А если не делать их полифорными, то ООП тут не при чём.

                                                  0
                                                  1. Можно и полиморфный массив сделать.
                                                  2. ООП и полиморфизм ортогональные понятия.
                                                    0
                                                    Можно и полиморфный массив сделать.

                                                    Как это сделать в C++?


                                                    ООП и полиморфизм ортогональные понятия.

                                                    Нет, это не так. Без полиморфизма не будет ООП.

                                                      0
                                                      Как это сделать в C++?

                                                      Через Variant или как он там в C++ называется.


                                                      Нет, это не так. Без полиморфизма не будет ООП.

                                                      Вполне себе будет. Суть ООП в инкапсуляции, а не морфизмах.

                                                        0
                                                        Через Variant или как он там в C++ называется.

                                                        Покажите код, очень интересно.


                                                        Суть ООП в инкапсуляции, а не морфизмах.

                                                        Если убрать инкапсуляцию, то можно договориться, что поля, которые начинаются с _ или кончаются на _, трогать нельзя, как это делалось в php в джаваскрипте и по моему в питоне. И будет ООП.


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

                                                          0
                                                          Покажите код, очень интересно.

                                                          https://dlang.org/phobos/std_variant.html#VariantN


                                                          Если убрать инкапсуляцию, то можно договориться, что поля, которые начинаются с или кончаются на , трогать нельзя, как это делалось в php в джаваскрипте и по моему в питоне. И будет ООП.

                                                          Это вы говорите про сокрытие. Инкапсуляции она ортогональна. https://ru.wikipedia.org/wiki/%D0%98%D0%BD%D0%BA%D0%B0%D0%BF%D1%81%D1%83%D0%BB%D1%8F%D1%86%D0%B8%D1%8F_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)


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

                                                          Не кончится. Кончится лишь полиморфизм.

                                                            0

                                                            Мало того, что в ответ на просьбу показать код на C++ вы показали.код на D, так он ещё и не создаёт массив полиморфных объектов ))


                                                            Это вы говорите про сокрытие. Инкапсуляции она ортогональна.

                                                            Не вопрос, можно сделать объект, в котором вообще нет данных. И, если он полиморфный, то это будет всё ещё ООП.


                                                            Не кончится. Кончится лишь полиморфизм.

                                                            Вы знаете ООП системы в которых не используется полиморфизм?

                                                              0
                                                              на просьбу показать код на C++ вы показали.код на D

                                                              Аналогичный код на C++ вы можете написать самостоятельно. Шаблоны это позволяют.


                                                              он ещё и не создаёт массив полиморфных объектов

                                                              Он создаёт полиморфный контейнер. Можете сделать массив этих контейнеров — получится полиморфный массив.


                                                              можно сделать объект, в котором вообще нет данных. И, если он полиморфный, то это будет всё ещё ООП

                                                              А я говорил, что существование объектов без поведения или без состояния — это не будет ооп?


                                                              Вы знаете ООП системы в которых не используется полиморфизм?

                                                              1C :-D

                                                                0
                                                                Аналогичный код на C++ вы можете написать самостоятельно. Шаблоны это позволяют.

                                                                Мог бы, я бы не спрашивал )). Можно пример создания массива полиморфных объектов, которые наследуются от абстрактного класса Shape?


                                                                А я говорил, что существование объектов без поведения или без состояния — это не будет ооп?

                                                                Вы говорили, что суть ООП инкапсуляции. Значит если её убрать — будет уже не ООП?


                                                                1C :-D

                                                                С козырей зашли? ))

                                                                  0

                                                                  Вы правда думаете, что я сейчас попрусь вспоминать этот убогий C++, чтобы вам что-то доказать?


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

                                                                    0
                                                                    Вы правда думаете, что я сейчас попрусь вспоминать этот убогий C++, чтобы вам что-то доказать?

                                                                    Действительно. Тогда можно пример создания массива полиморфных объектов, которые наследуются от абстрактного класса Shape, только на D?


                                                                    Так же и инкапсуляция никуда не девается, если объекту пока что не нужно состояние.

                                                                    А как тогда продемонстрировать, что суть ООП в инкапсуляции?

                                                                      0

                                                                      https://run.dlang.io/is/5GIyYv


                                                                      Это следует из определения объекта.

                                                                        0

                                                                        Спасибо за пример. Вот эта строчка всё портит.


                                                                        alias AnyShape = Algebraic!(Cyrcle,Square,Rect);

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


                                                                        shapes[0].x.writeln; 

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


                                                                        Это следует из определения объекта.

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

                                                                          0

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

                                                                            0

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

                                                                              +1
                                                                              Не соглашусь — если у объекта нет внутренних данных, сохраняемого контекста, то такой объект практически бессмысленен. Это просто набор функций с namespace'ом. КМК.
                                                                                –1

                                                                                Слышали про паттерн "Посетитель"?

                                                                                  0
                                                                                  Я недаром сказал «практически» (в значении «почти»). Да, есть случаи, как упомянутый вами, когда такие объекты нужны из чисто технических соображений и подобный объект является базовым, используется полиморфизм; но, пмсм, то же может быть достигнуто без объектов, на функциях в большинстве случаев. Особенно учитывая ход дискуссий в комментариях о том, нужно ли наследование как обязательный признак, или нет.
                                                                            –2
                                                                            Получается, что при добавлении ещё одного потомка нужно будет её поправить.

                                                                            Ужас-то какой..


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

                                                                            Это уже ваши фантазии.


                                                                            написать shapes[0].x.writeln; не получается

                                                                            https://run.dlang.io/is/asBYYV


                                                                            вызвать методы тоже не выйдет.

                                                                            А давайте вы поверите мне на слово, что всё это тоже не реализуемо?


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

                                                                            Главное, что клиентскому коду не надо знать что там внутри объекта.

                                                                              0
                                                                              Ужас-то какой… [что смысл полиморфизма в том, чтобы можно было добавлять реализации не внося такие правки] Это уже ваши фантазии

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


                                                                              А давайте вы поверите мне на слово, что всё это тоже реализуемо?

                                                                              Охотно верю, возможности языка впечатляют.


                                                                              Главное, что клиентскому коду не надо знать что там внутри объекта.

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

                                                                                0
                                                                                любой программист бы сильно удивился, если бы в инструкции к применению библиотеки предлагалось добавить в неё типы, которые он собирается использовать

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


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

                                                                                Не знать как устроен объект и работать с разными объектами — ортогональные вещи. У вас какая-то беда с логическим мышлением. Мне с вами не интересно общаться.

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

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


                                                                                  Не знать как устроен объект и работать с разными объектами — ортогональные вещи. У вас какая-то беда с логическим мышлением.

                                                                                  Вы сначала сказали, что важно, чтобы в объекте были методы и поля и это самое важное. А потом сказали, что важно, чтобы клиентский код не знал, как устроен объект. Что важнее?

                                                                              0
                                                                              Тогда можно пример создания массива полиморфных объектов, которые наследуются от абстрактного класса Shape
                                                                              Если нужно именно это, а не массив из вообще любых типов, то это типовая задача в плюсах.

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

                                                                              A variant, который выше предлагалось использовать, предназначен для других случаев — когда нужно хранить несовместимые между собой типы. Например, shape, string и double.
                                                                                0

                                                                                Мы обсуждали вопрос хранения объектов единым массивом, а не массива ссылок на объекты, находящиеся где попало.

                                                                                  0

                                                                                  Кстати, вопрос, один элемент массива будет требовать памяти столько, сколько занимает наибольший тип? Или они каким-то чудом ещё и пакуются?

                                                                                    0
                                                                                    один элемент массива будет требовать памяти столько, сколько занимает наибольший тип?
                                                                                    В моем примере на плюсах будет занимать столько, сколько по факту занимают объекты, плюс оверхед на указатели.

                                                                                    В примерах с variant выше само собой будет занимать как наибольший тип, плюс оверхед на идентификатор типа.
                                                                                      0

                                                                                      То есть вы развели тут дискуссию, даже не сходив по ссылкам? https://habr.com/ru/company/ruvds/blog/462483/#comment_20480627

                                                                                        0

                                                                                        Ну вы же развели дискуссию о том, что можно сделать на С++ массив полиморфных объектов, хотя код можете показать только на D )))


                                                                                        Однако на всякий случай скажу, что я со ссылками ознакомился, но решил на всякий случай уточнить. Вдруг в случае с коллекциями там есть какая-то магия, кто знает.

                                                                                          0
                                                                                          Справедливости ради, сделать свою коллекцию с «магией» в С++/D не сложно, но придется распрощаться с произвольным доступом к элементам. Ну или городить к ней вспомогательную структуру, которая будет мапить индексы в смещения элементов, но тогда полученное будет уже не совсем массивом.
                                                                                      0
                                                                                      Мы обсуждали вопрос хранения объектов единым массивом, а не массива ссылок на объекты, находящиеся где попало.
                                                                                      Никакого уточнения про хранение по ссылкам или значению я не обнаружил, обсуждался именно сабтайп-полиморфизм для массивов. Который во всех приличных ООП языках делается через ссылки, и я не вижу никакого практического смысла реализовывать его иначе.

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

                                                                                        Всё началось с обсуждения AoS vs SoA проблемы.


                                                                                        https://habr.com/ru/company/ruvds/blog/462483/#comment_20477977


                                                                                        И продолжилось обсуждением in-place расположением объектов в массиве.


                                                                                        https://habr.com/ru/company/ruvds/blog/462483/#comment_20478075

                                                                                          0
                                                                                          А как массивы ссылок противоречат проблеме AoS vs SoA? Она ведь подразумевает не только выбор между буквальными расшифровками аббревиатур, она куда шире.

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

                                                                                          Что важнее, под AoS очень редко рассматривается массив variant, потому что это максимально неэффективный способ хранить объекты разного размера в массиве. Если уж отказываться от ссылочности, то в сторону множества однотипных массивов, как в ECS.

                                                                                          По второй ссылке нет ничего про «in-place». Зачем вы меня отправили по ссылкам в другом сообщении я вообще не понял, я и так по ним ходил.
                                                                  0
                                                                  Через Variant или как он там в C++ называется.

                                                                  Через variant не получится, это ж не тот полиморфизм (и вообще не полиморфизм, а ADT на костылях).

                                                                    0

                                                                    Один и тот же код работает работает с разными типами. Это полиморфизм по определению.

                                                                      +1

                                                                      Во-первых, не один и тот же. Чаще всего при обработке variant'а у вас по отдельной ветке на каждый, собственно, вариант (а если и нет, то это параметрический полиморфизм).


                                                                      Во-вторых, если у меня есть код на хаскеле типа


                                                                      data Shape = Circle | Rectangle | Triangle
                                                                      
                                                                      describe :: Shape -> String
                                                                      describe Circle = "it's so round!"
                                                                      describe Rectangle = "yay rectangle"
                                                                      describe Triangle = "yay triangle"

                                                                      то это что, полиморфизм, по-вашему?

                                                                        0
                                                                        Чаще всего при обработке variant'а у вас по отдельной ветке на каждый

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


                                                                        У вас больше одного, у меня другого — бывает. Но даже если полиморфного кода у вас мало, он от этого не перестаёт быть полиморфным.


                                                                        это параметрический полиморфизм

                                                                        А параметрический полиморфизм — это внезапно уже не полиморфизм? Или как это работает?


                                                                        у меня есть код на хаскеле

                                                                        А я не понимаю хаскель.

                                                                          0
                                                                          А параметрический полиморфизм — это внезапно уже не полиморфизм? Или как это работает?

                                                                          Контекст дискуссии намекал (по крайней мере, мне), что речь о сабтайпинг-полиморфизме.

                                                                            0

                                                                            Речь шла о создании массива, который может содержать разные типы данных.

                                                                          0
                                                                          Во-вторых, если у меня есть код на хаскеле типа

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

                                                                            0

                                                                            Это вопрос писанины на клавиатуре:


                                                                            data Shape = Circle CircleData | Rectangle RectData | Triangle TriData

                                                                            для соответствующих типов CircleData, RectData, TriData. Хранит ли массив [Shape] разные типы данных?

                                                                              0
                                                                              Это вопрос писанины на клавиатуре:

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


                                                                              Хранит ли массив [Shape] разные типы данных?

                                                                              Конечно же, нет. Он хранил бы разные типы данных, если бы в массиве были значения типов CircleData/ReactData/TriData. Но там вместо них значения типа Shape.
                                                                              Если вы напишите ф-ю, которая возвращает некоторый CircleData, то вы результат этой ф-и в массив ваш положить не сможете. Вам надо будет этот CircleData дата скормить конструктору, который вернет Shape.

                                                                              0

                                                                              Но на том же С++ std::variant точно так же является типом-суммой, а не типом-объединением.

                                                                                0
                                                                                Но на том же С++ std::variant точно так же является типом-суммой, а не типом-объединением.

                                                                                Для суммы int + int != int, разве это выполняется для variant?

                                                                                  0

                                                                                  Конечно же выполняется, тип std::variant<int,int> отличается от просто int.


                                                                                  Более того, его можно создать как std::variant<int,int>( std::in_place_index<0>, 10), а можно как std::variant<int,int>( std::in_place_index<1>, 10) — и эти два значения будут различимыми.

                                                                                    0
                                                                                    Конечно же выполняется, тип std::variant<int,int> отличается от просто int.

                                                                                    А, ну ок. Вообще я перепутал и думал, что мы про union говорим.

                                                                0

                                                                А вас не смущает, что та же статья в Википедии приводит примеры из Си?

                                                                  0

                                                                  Какая статья?

                                                                    0

                                                                    https://en.wikipedia.org/wiki/AoS_and_SoA


                                                                    А вы о какой подумали?

                                                                      0

                                                                      О той, которая на том самом сайте, которая идёт после какой-то.


                                                                      Собственно мы о разных проблемах говорили.

                                                          +2
                                                          Но вот для складывания матриц в GPU ФП скорее всего не подходит.

                                                          Кстати, внезапно подходит, см. фреймворк accelerate.

                                                            +1
                                                            Вообще-то Дейкстра изначально такого про исключения не говорил. И все-таки, они не эквивалентны goto, что бы там ни говорили.
                                                              0

                                                              Есть неплохая статья на тему, почему эксепшны не очень хороши.

                                                                0
                                                                Да я и не настаивал, что они хороши по всем пунктам. Но у них есть своя, скажем так, ниша. И по сравнению с goto это все-таки шаг вперед. Был.
                                                                  0

                                                                  Безусловно.

                                                              +2
                                                              Либо это dispodsble, либо это RAII — все одно. Вот только ФП не создает иллюзии, что ты работаешь безопастно.

                                                              Почему абстракции, позволяющие безопасно работать с ресурсами, вы называете иллюзией?


                                                              Исключения — это костыль. В отличие от монады. И это математически доказал Дейкстра в своих основах структурного программирования

                                                              Можно ссылку на это математическое доказательство?


                                                              Модификация объекта — это головная боль с того момента, как программе появляется многопоточность.

                                                              Нет там никакой головной боли при использовании правильных абстракций.


                                                              Это называется декларативное программирование — вершина грамотной архитектуры и композиции программных систем.

                                                              ФП не является декларативным по определению. В ФП описывается не результат, а функция генерации результата из входных параметров.

                                                              –4

                                                              Ложь в каждом из пунктов.

                                                                +1

                                                                [х] дискуссия с переводом

                                                                  +6
                                                                  ФП работает с "чистыми" функциями. Но реальное приложение работает с файлами, БД, внешними сервисами, которые нарушают требования "чистоты", в итоге приходится придумывать костыли

                                                                  Нет, не с чистыми в смысле отсутствия эффектов. Чистота функций — это про то, что эффекты функции объявлены в её сигнатуре. IO — ни в коей мере не костыли.


                                                                  Если у вас есть массив из 100 пользователей, и вы должны одному обновить рейтинг, вы должны сделать полную копию массива. И так на каждое изменение. Это негативно влияет на производительность и потребление памяти.

                                                                  Если вам не нужна старая копия массива, то GC её тут же подберёт, и массив даже из nursing area (и, как следствие, из кеша процессора) выбраться не успеет.


                                                                  Ну и есть линейные типы, которые тут тоже помогают.


                                                                  Там, где вы в PHP пишете $user->updateKarma(), в ФП вы должны писать user2 = updateKarma(user), и не забыть заменить старые копии user на user2 во всех местах кода, во всех коллекциях и списках. Удачи!

                                                                  А это, кстати, интересный вопрос. Ну, просто почему-то так оказывается, так пишется ФП-код, что это не является проблемой.


                                                                  В нормальном программировании вы просто пишете действия подряд, если произойдет ошибка, выбросится исключение и оставшиеся не будут выполняться. В ФП вам приходится лепить костыли, делая типы вроде Maybe и делая "пропуск" функции, если ей передано Maybe с ошибкой внутри.

                                                                  В ФП с do-нотацией вы тоже пишете функции подряд, и оставшиеся не будут выполняться. Пропуск выполняется за вас.


                                                                  А ФП вроде Хаскелл позволяет создать 10 вариантов функции с одинаковым именем, но разным типом аргументов.

                                                                  Правда? Можно пример?


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

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


                                                                  Но, конечно, в программах для вычисления чисел Фибоначчи ФП не знает себе равных.

                                                                  Или в компиляторах. Или в статических анализаторах. Или в скрейперах. Или в веб-серверах.

                                                                    0
                                                                    Чистота функций — это про то, что эффекты функции объявлены в её сигнатуре. IO — ни в коей мере не костыли.

                                                                    У вас какое-то очень своеобразное представление о чистоте.
                                                                    https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D1%82%D0%BE%D1%82%D0%B0_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8

                                                                      +1

                                                                      Ну так IO-функция каждый раз возвращает один и тот же экшн и является детерминированной. В чём расхождение?

                                                                        0

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

                                                                          +1

                                                                          Чем оно неверное-то? Чем функция, возвращающая описание действия, не чистая?

                                                                            –4

                                                                            Два разных текста с двумя разными смыслами. Дальнейших разъяснений от меня можете не ждать.

                                                                      0
                                                                      Или в компиляторах. Или в статических анализаторах. Или в скрейперах. Или в веб-серверах.

                                                                      BigData еще.
                                                                      +2

                                                                      Вот это уже гораздо тоньше, чуть сам не кинулся объяснять, где вы неправы :)

                                                                        +2

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

                                                                        0
                                                                        Толсто.

                                                                        ФП работает с «чистыми» функциями. Но реальное приложение работает с файлами, БД, внешними сервисами, которые нарушают требования «чистоты», в итоге приходится придумывать костыли

                                                                        И как это мешает бизнес-логику реализовать на чистых функциях, а ввод-вывод оставить грязным функциям? IO-монада, там, вот это все.
                                                                        в ФП переменные иммутабельны. Если у вас есть массив из 100 пользователей, и вы должны одному обновить рейтинг, вы должны сделать полную копию массива. И так на каждое изменение. Это негативно влияет на производительность и потребление памяти.(пример: реакт, где на любой чих пересоздается стейт заново и хорошо, если он у вас маленький).

                                                                        Лишний повод почитать структуры данных. Immutable hash trie (через который в ФП реализуются immutable массивы) решает проблему.
                                                                        в ФП нет исключений. В нормальном программировании вы просто пишете действия подряд, если произойдет ошибка, выбросится исключение и оставшиеся не будут выполняться. В ФП вам приходится лепить костыли, делая типы вроде Maybe и делая «пропуск» функции, если ей передано Maybe с ошибкой внутри.

                                                                        Зато есть Either (это в scala), Option и до черта всякого остального. Пропуск функции делать не нужно, монада сделает это за вас.
                                                                        в ФП нет ООП, которым удобно представлять объекты реального мира. Вместо этого там приходится делать разрозненные структуры и функции для работы с ними — то, для замены чего и придуман ООП. А ведь в ФП вы еще не можете модифицировать объект, с которым работаете.

                                                                        ФП плох потому, что в нем нет ООП)
                                                                        Вообще есть до черта всего. Алгебраические типы данных. Coproduct-ы. Функторы. Аппликативные Фнукторы. Монады, в конце-то концов. Полугруппы, моноиды, группы. Этим всем вполне можно представлять объекты реального мира.
                                                                        в ООП функуция это просто последовательность шагов: 1) проверь, что такого емайла нет 2) добавь запись в БД 3) отправь письмо для подтверждения почты. В ФП же так не принято, а принято комбинировать функции в составные функции, из-за чего разбор кода превращается в кошмар (registrator = (formData) => combine(formValiadtor(rules), fieldExtractor('email'), emailNotInDbChecker(db), emailToDbAdder(db), emailSender(db, sendService)).

                                                                        Это на уровне бреда. Вполне можно и последовательные вычисления организовывать, мешает-то кто?
                                                                          +2
                                                                          Immutable hash trie (через который в ФП реализуются immutable массивы) решает проблему.

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

                                                                            –3
                                                                            а ввод-вывод оставить грязным функциям? IO-монада, там, вот это все.

                                                                            Наличие стандартной затычки красноречиво свидетельствует о протечке абстракции.


                                                                            Лишний повод почитать структуры данных. Immutable hash trie (через который в ФП реализуются immutable массивы) решает проблему.

                                                                            Вот и почитайте сколько стоит работа с этими структурами по сравнению с обычным массивом. Как по памяти, так и про процессору.


                                                                            Зато есть Either (это в scala), Option и до черта всякого остального. Пропуск функции делать не нужно, монада сделает это за вас.

                                                                            А в языках с поддержкой исключений — компилятор делает эту монаду и Either за вас. Причём в нормальных реализациях ещё и cost-free, без 100500 условных переходов, которые так любит конвейер процессора.


                                                                            до черта всего. Алгебраические типы данных. Coproduct-ы. Функторы. Аппликативные Фнукторы. Монады, в конце-то концов. Полугруппы, моноиды, группы. Этим всем вполне можно представлять объекты реального мира.

                                                                            Можно и из буханки хлеба сделать троллейбус. Только зачем?

                                                                              –1
                                                                              А в языках с поддержкой исключений — компилятор делает эту монаду и Either за вас.

                                                                              И за меня выражение вроде «попытайся выполнить вот этот список действий и верни первое успешное» нагенерит для каждого конкретного типа ошибки? Или надо будет самому обвязки писать?

                                                                                +1

                                                                                Не очень понял о чём вы.

                                                                                0
                                                                                «я не пробовал, но осуждаю»?
                                                                                  0

                                                                                  Пробовал.

                                                                              0
                                                                              Мне кажется, что настоящая проблема ФП не в его определенных недостатках, скажем так, а в том, что представление о нем зачастую сильно искажено, что хорошо видно на ваших примерах.

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

                                                                              Ну да, иммутабельность чего-то стоит, несомненно. Но в тоже время она почти даром дает нам например такие вещи, как возможность версионирования значения, и соответственно, undo.

                                                                              И так почти по всем пунктам.

                                                                              И что еще характерно, почти все пункты уже аргументированно разобрали, а комментарий все же имеет +24. Это я не к тому, что он плохой — по большей части тут рассмотрены вполне осмысленные вопросы, которые стоит себе задавать. Но он односторонний — а значит в конечном счете будет как-то способствовать распространению все тех же искаженных представлений об ФП.
                                                                                0
                                                                                Ну да, иммутабельность чего-то стоит, несомненно. Но в тоже время она почти даром дает нам например такие вещи, как возможность версионирования значения

                                                                                Да вполне терпимо стоит. По асимптотике функциональные структуры данных такие же, как и мутабельные. Зато параллелизм из коробки.
                                                                                  0
                                                                                  Терпимо — это с точки зрения производительности наверное? Но вообще эти структуры — они несколько сложнее, откровенно говоря. Так что их разработка тоже чего-то да стоила.
                                                                                +1

                                                                                Что-то это уже для меня слишком тонко. Где тут шутка, а где реальное мнение? :)

                                                                                  0
                                                                                  А кто просит писать на чисто функциональном языке? Я например пишу на swift, кое-где применяю паттерны из ФП (без фанатизма, где реально удобно), но это не отменяет ООП-шного стиля там, где он работает лучше. Мне кажется, дискуссия на тему «ФП полностью убьет ООП» является специальной олимпиадой, так же как «коробка передач убьет двигатели, они больше не нужны»
                                                                                  +2

                                                                                  Что было раньше: декларация или операция?

                                                                                    +10

                                                                                    Декларация. «И сказал Бог: да будет свет. И стал свет».

                                                                                      +3

                                                                                      А один евангелист рекурсии дополняет: "В начале было Слово, И Слово было у Бога, И Слово было Бог"

                                                                                        –4
                                                                                        Ни к чему такое писать. Разве без этого никак?
                                                                                    +1
                                                                                    Кстати говоря, если измерять «производительность труда» в LoC за единицу времени — то заголовок вполне себе верный.
                                                                                      +9
                                                                                      В этой статье присутствует ложная дихотомия функционального программирования и ООП. Эти подходы совершенно не исключают друг-друга.

                                                                                      Совсем непонятно про наследование. В каком месте функциональное программирование отменяет концепцию наследования???

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

                                                                                        Если вы уберете наследование из ООП вы потеряете всё ООП.
                                                                                        Если вы уберете требования чистоты из ФП вы потеряете всё ФП.


                                                                                        В этом и суть, я не вижу возможностей для "промежуточного" подхода, который бы работал. Заигрывания в паттерн матчинг или там функции высшего порядка это мишура, которая используется в ФП, но никак его не определяет.


                                                                                        Да и зачем их смешивать? Какая концепция в ООП позволяет решить класс задач, которые в ФП не решаются легко? Наследование? Тайпклассы справляются с этим больше, композиция считается лучше даже самими ООП идолами вроде Фаулера. Инкапсуляция? Это просто термин для создания абстракций, оно существует в любой парадигме начиная с процедурных алголов, это не прерогатива ООП. Полиморфизм? Существует и в ФП, параметрический работает точно так же, как и подтиповой.


                                                                                        В итоге, что остается от ООП? Какая концепция в нем есть, которая дает какие-то ощутимые преимущества?

                                                                                          +7
                                                                                          Если вы уберете наследование из ООП вы потеряете всё ООП.

                                                                                          Нет, это не так, ООП потеряется только если убрать полиморфизм. Наследование там не главное.

                                                                                            0

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

                                                                                              +3

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


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


                                                                                              В ФП центровым понятием является функция, функция должна быть чистой. Если функции не чистые, то уже не ФП, а элементы ФП


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

                                                                                                0
                                                                                                В ФП центровым понятием является функция, функция должна быть чистой. Если функции не чистые, то уже не ФП, а элементы ФП

                                                                                                Это ИМХО бессмысленная интерпретация, если относиться к ней формально. Даже в С все функции чистые, просто все функции живут в IO.

                                                                                                  0
                                                                                                  как это все чистые? Чистота определяется тем, что на одни и те же параметры функция выдаст один и тот же результат. Что такое параметры в С, думаю, не надо здесь объяснять. Так же, думаю не надо рассказывать здесь про существование статических переменных внутри функции, а, значит, что IO тут не совсем не существенно.
                                                                                                    0

                                                                                                    Давайте немножко издалека начнём.


                                                                                                    Чистая ли функция putStrLn :: String -> IO (), просто выводящая строку на экран?
                                                                                                    Чистая ли функция


                                                                                                    greet :: IO ()
                                                                                                    greet = do
                                                                                                      str <- getLine
                                                                                                      putStrLn $ "Hello, " <> str

                                                                                                    ?


                                                                                                    Это ведь хаскель, а в хаскеле все функции чистые, не так ли?


                                                                                                    Чистая ли функция


                                                                                                    stateful :: MonadState MyState m => m Int
                                                                                                    stateful = do
                                                                                                      myState <- get
                                                                                                      pure $ if something myState then 1 else 2

                                                                                                    ?

                                                                                                      +2

                                                                                                      Смысл в том, что функция putStrLn не выводит на экран ничего, а создает соответствующий IO. А println именно что выводит, она не создает никакую структуру, которую можно потом проинтерпретировать.


                                                                                                      Натянуть на глобус си и считать его чистым при большом желании можно, но зачем? Все понимают, о чем идет речь.

                                                                                                        +1
                                                                                                        А println именно что выводит, она не создает никакую структуру, которую можно потом проинтерпретировать.

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


                                                                                                        Натянуть на глобус си и считать его чистым при большом желании можно, но зачем?

                                                                                                        Чтобы понять, что контроль над эффектами важнее, чем какая-то там не очень понятная чистота, которую разные люди понимают по-разному (и не всегда приходят при этом к логическим противоречиям).

                                                                                                          0

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

                                                                                                            +1
                                                                                                            Это неважно, есть очевидный изоморфизм между живущими в IO программами на хаскеле и нечистыми программами на какой-нибудь джаве (может, даже на С есть, но мне лень анализировать низкоуровневые свойства С).

                                                                                                            Изоморфизм есть только в слчае лени, а её нет.


                                                                                                            Если вы сделаете let _ = putStrLn то ничего не произойдет. Если вы сделаете () _ = println(...) то эффект будет.


                                                                                                            Точно так же вы можете дропнуть весь IO при желании, и его эффекты не сработают.


                                                                                                            Отправиться в прошлое в си и отменить печать в консоль вы не сможете.

                                                                                                              0
                                                                                                              Если вы сделаете let _ = putStrLn то ничего не произойдет. Если вы сделаете () _ = println(...) то эффект будет.

                                                                                                              Потому что это разные вещи: оттого, что я в С объявлю функцию void _() { printf(...); }, тоже ничего не произойдёт. И лень тут ни при чём, в очень «энергичном» идрисе первая строка тоже ничего не выведет.


                                                                                                              Точно так же вы можете дропнуть весь IO при желании, и его эффекты не сработают.

                                                                                                              Что эквивалентно невызову функции.


                                                                                                              Ну и кстати, по-вашему, запросит ли следующая функция что-то с клавиатуры или нет?


                                                                                                              stupid = do
                                                                                                                str <- getLine
                                                                                                                print "yay"
                                                                                                                +1

                                                                                                                Но я в хаскеле не объявил функцию, а вызвал её.


                                                                                                                Что эквивалентно невызову функции.

                                                                                                                Вызов функции в хаскеле эквивалентен невызову в си? Окей, но мне сложно назвать это "похожими вещами", не говоря про "Одно и то же".

                                                                                                                  +1
                                                                                                                  Но я в хаскеле не объявил функцию, а вызвал её.

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


                                                                                                                  Вызов функции в хаскеле эквивалентен невызову в си?

                                                                                                                  Создание байндинга, который в итоге нигде не участвует в цепочке IO.

                                                                                                                    –1
                                                                                                                    Чтобы вызвать, надо вот эту вот стрелочку налево писать, которая <-

                                                                                                                    Уточнение: на самом деле, чтобы вызвать, надо вернуть результат из main. Ну или передать в unsafePerformIO или что-то подобное.


                                                                                                                    В этом плане монада IO неожиданной оказывается работающей аналогично асинхронным функциям из Питона.

                                                                                                                      0

                                                                                                                      Только ИО синхронно выполняется. ИО в грязном языке — это просто лямбда. А unsafePerformIO — простой функциональный вызов.

                                                                                                                        0

                                                                                                                        Но поведение все равно похоже.

                                                                                                              0

                                                                                                              Как провести изоморфизм, если хаскель разделяет a и IO a, а си – нет?

                                                                                                                –1

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


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

                                                                                                            0
                                                                                                            Если строго говорить, то чистая. И возвращает она последовательность действий «1) прочитать строку; 2) вывести строку, используя результат первого действия»

                                                                                                            Собственно main :: IO () в haskell — это просто выражение, которое описывает определенные действия из N возможных. Описание это формируется при помощи композиции (монадической композиции, скажем так), ряда функций вида a -> IO b.

                                                                                                            Вот и все. Далее, на основании этого описания компилятор, собственно, генерирует машинный код (или код ассемблера, или код на промежуточном языке, таким, как C).
                                                                                                              0

                                                                                                              Ну так и код на С просто описывает определённые действия из N возможных. И при одном и том же исходном состоянии машины он тоже совершит одни и те же действия.


                                                                                                              Любую функцию на С вида T foo(Arg1, ..., ArgN) вы можете представить себе как foo :: Arg1 -> ... -> ArgN -> IO T и не потерять вообще ничего.

                                                                                                                0
                                                                                                                Речь ведь не про состояние машины, а про вызове функций с одними и теми же параметрами.

                                                                                                                В C можно написать функцию
                                                                                                                T foo (Arg1, ..., Arg N),
                                                                                                                которую, если вызывать с одними и теми же значениями Arg1 и ArgN, то она будет всегда возвращать разные значения. Это и есть нарушение чистоты функции. Про «состояние машины» речь не идет совсем.
                                                                                                                  0

                                                                                                                  На хаскеле тоже можно написать функцию foo :: Arg1 -> ... -> ArgN -> IO T, которая при вызове с одними и теми же параметрами будет возвращать разный результат.


                                                                                                                  Тут, конечно, дъявол кроется в деталях и, как обычно, в определениях: что именно значит «возвращать результат»?


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

                                                                                                          0

                                                                                                          Дело даже не в IO, а в том, что в Си можно вызвать функцию, передав туда указатель на структуру, потом изменить поле структуры, переданной ранее в функцию, потом вызвать ту же функцию, передав туда тот же указатель и получить другой результат.

                                                                                                            0

                                                                                                            Ну, собственно, аналогичный вопрос. Как насчёт хаскель-функции, живущей в MonadState, чистая ли она?

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

                                                                                                          Можно, но ФП парадигма не в том, чтобы можно было писать чистые функции, а в том, что она требует писать только чистые функции. А что до возможности их писать в других парадигмах… Ложка дегтя портит всю бочку мёда. Чтобы был мед, дёгтя не должно быть вообще.


                                                                                                          В ФП центровым понятием является функция, функция должна быть чистой. Если функции не чистые, то уже не ФП, а элементы ФП

                                                                                                          Объекты это частные случаи функций, и наоборот. Объект А всегда можно представить как функцию () -> A.


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

                                                                                                          foo<T> — это полиморфизм без использования парадигмы ООП.

                                                                                                            +1
                                                                                                            Объект А всегда можно представить как функцию () -> A.

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

                                                                                                              0

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

                                                                                                                0

                                                                                                                Широко известный в узких кругах Types and Programming Languages. Вроде даже pdf'ка свободно циркулирующая была (но если не найдёте — стукнитесь в личку).


                                                                                                                Особо много матана там нет, самый хардкор там, ИМХО, в главе про метатеорию рекурсивных типов, но она для понимания ООП не обязательна.

                                                                                                              0
                                                                                                              foo — это полиморфизм без использования парадигмы ООП.

                                                                                                              Если вы имеете в виду дженерики или шаблоны, то это не полиморфизм, потому что там нет вызовов полиморфных методов.

                                                                                                                0

                                                                                                                Посмотрите определение. Параметрический и подтиповой полиморфизм — и то и то виды полиморфизма, причем второй частный случай первого.

                                                                                                                  0

                                                                                                                  Я думал, что мы говорим про тот полиморфизм, который используется в ООП. То есть получается про подтиповой. Когда начинаете им пользвоаться — начинаете использовать ООП. Параметрический полиморфизм такой неотъемлемой частью ООП не является.

                                                                                                                    0

                                                                                                                    Полиморфизм в ООП подтиповой, можно реализовать через параметрический, в той же статье это написано.


                                                                                                                    То есть это просто более богатый языковой концепт, как паттерн матчинг против if. В итоге мы можем выразить всё то же самое используя его.

                                                                                                                      +2
                                                                                                                      Полиморфизм в ООП подтиповой, можно реализовать через параметрический, в той же статье это написано.

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

                                                                                                          +1

                                                                                                          Инкапсуляция (не в смысле сокрытия) данных и алгоритмов их обрабатывающих с привязкой их друг к другу by design. Как по мне, то именно это стало основным шагом от структур типа сишных с указателями/ссылками на функции к ООП. Не token = auth.login(auth, username, password), а token = auth.login( username, password).

                                                                                                            –1

                                                                                                            Ну возьмите Раст, ООП там нет, но там тоже будет auth.login(...), ФП никак этому не противоречит.

                                                                                                              –1

                                                                                                              В Rust нет наследования (привычного), а не ООП.

                                                                                                                0

                                                                                                                А в чем еще ООП состоит? Я ниже спросил, никто так и не ответил.


                                                                                                                В моем понимании единственным значимым отличие именно наследование и есть.


                                                                                                                А в расте ни привычного, ни непривычного наследования структур нет.

                                                                                                                  –2

                                                                                                                  Ответ вам дали в этой ветке 4 комментариями выше. Вы ходите кругами.


                                                                                                                  В моем понимании единственным значимым отличие именно наследование и есть.

                                                                                                                  А в моём — инкапсуляция и полиморфизм.


                                                                                                                  А в расте ни привычного, ни непривычного наследования структур нет.

                                                                                                                  Есть наследование интерфейсов-трейтов, и его более чем достаточно.

                                                                                                                    0
                                                                                                                    В Haskell есть инкапсуляция и полиморфизм. Выходит, Haskell — это тоже ООП?
                                                                                                                      0

                                                                                                                      В Haskell, насколько я знаю, инкапсуляция только уровня модулей. Напомню с чего началась ветка:


                                                                                                                      Инкапсуляция (не в смысле сокрытия) данных и алгоритмов их обрабатывающих с привязкой их друг к другу by design.

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

                                                                                                                        0
                                                                                                                        Я поддерживаю ваше начинание дать определения buzzword и разобраться по сути, но давайте сделаем тоже самое и с полиморфизмом. Вы писали:
                                                                                                                        ООП потеряется только если убрать полиморфизм. Наследование там не главное
                                                                                                                        Есть два основных вида полиморфизма: подтиповый и параметрический. Haskell и Rust на полную катушку используют параметрический полиморфизм, при этом по крайней мере Haskell точно не является ООП-языком. Подтиповый полиморфизм невозможен без поддержки наследования на уровне языка. В Delphi/Object Pascal долгое время был только подтиповый полиморфизм, что врочем не мешало этим языкам считаться «ООП».

                                                                                                                        Получается, что с точки зрения полиморфизма ООП от не-ООП отличает как раз присутствием подтипового полиморфизма, который в свою очередь невозможен без наследования.
                                                                                                                          0

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


                                                                                                                          Но даже в этом случае для ООП более чем достаточно тех возможностей, которые дают dyn Trait в Rust или экзистенциальные типы в Haskell, независимо от того как этот тип полиморфизма называется на самом деле.

                                                                                                                            +1
                                                                                                                            Но даже в этом случае для ООП более чем достаточно тех возможностей, которые дают dyn Trait в Rust или экзистенциальные типы в Haskell
                                                                                                                            Что приводит нас к логическому умозаключение что видимо наследование в принципе не так уж необходимо, раз без него можно реализовать ключевые паттерны проектирования.

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

                                                                                                                              Ну да, так и есть. Это удобный механизм, а не необходимая часть ООП.


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

                                                                                                                              Так ведь в чистом ФП все эти "паттерны проектирования" оказываются слабо применимы, а те, что применимы — реализуются совсем другим образом.


                                                                                                                              Не вижу причин не называть их паттернами ООП.

                                                                                                                            0
                                                                                                                            Получается, что с точки зрения полиморфизма ООП от не-ООП отличает как раз присутствием подтипового полиморфизма

                                                                                                                            Получается да


                                                                                                                            который в свою очередь невозможен без наследования.

                                                                                                                            Да нет, наследование не нужно. Нужно позаботиться, чтобы объект реализовал интерфейс и всё.

                                                                                                                              0

                                                                                                                              Ребята, вы забываете, что ООП — это Объектно Ориентированное Программирование. Ничто не мешает реализовать объекты в ФП. Так же как и чистые функции в ИП. Разница лишь в том, на какой тип использования язык ориентирован.

                                                                                                                              +1
                                                                                                                              А на счет инкапсуляции как «использования значения как пространства имен» – это ведь не более чем синтаксический сахар над Foo::method(foo). То есть явно не дотягивает до ключевой особенности, определяющей парадигму.
                                                                                                                                –1

                                                                                                                                Сахар сахаром, но это именно что ключевая особенность!


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


                                                                                                                                Если же синтаксический сахар не учитывать, то надо записывать Си без плюсов как поддерживающий ООП язык, ведь ядро Linux и оконная подсистема WINAPI написаны именно в парадигме ООП!

                                                                                                                                  0

                                                                                                                                  Ну да, по вашему определению получается Си это ООП язык, и вы правы, это не так, но не потому, что там нет сахара вызова методов через точку.

                                                                                                                                    0
                                                                                                                                    Ну это скорее ваше собственное определение, чем общепринятое. Я его понимаю и принимаю. В таком смысле действительно Rust и Go – вполне себе ООП языки.

                                                                                                                                    Тем не менее, такой подход вряд ли удовлетворит обывателей, которые жалуются что даже «ООП не смогли нормально завезти»: в первом нет наследования, а во втором – параметрического полиморфизма.

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

                                                                                                                                      Ну, я тоже считаю, что в Rust «ООП не смогли нормально завезти»: вот как наконец-то появится делегирование реализации, так это мнение и переменится.


                                                                                                                                      Тем не менее, "ненормальное" ООП — это вовсе не отсутствие ООП.

                                                                                                                                        0
                                                                                                                                        Можно пример, чем использование отдельных типажей или Deref/AsRef не угодили?
                                                                                                                                          0

                                                                                                                                          Эмуляция через них ООП это официальный антипаттерн.

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

                                                                                                                                              Ну вот есть типаж о 10 методах (конкретный кейс придумывать лень, но не вижу причин ему не существовать). Нужно сделать реализацию B, которая делает всё как реализация A — но 1 метод из 10 отличается.


                                                                                                                                              На том же C# я бы использовал либо наследование, либо композицию с наследованием — а в Rust я вынужден писать тривиальную реализацию для каждого из 9 методов.

                                                                                                                                                0

                                                                                                                                                Обычно это заменяется выносом 9 общих методов в один тип, и потом использование его из А и В.

                                                                                                                                                  0
                                                                                                                                                  Обычно это заменяется выносом 9 общих методов в один тип, и потом использование его из А и В.

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


                                                                                                                                                  Иначе получается, что у нас есть тип с 9 "общими" методами — и еще 18 написанных вручную методов-делегатов.

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

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


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

                                                                                                                                                  +2
                                                                                                                                                  Вы описали не саму задачу, а подход к решению который вы хотите применить. Из-за этого получится костыль.

                                                                                                                                                  Во-первых, вызывает вопросы существование типажа с десятью не default методами. Я понимаю что это утрированный пример, но это плохой пример. Типаж – это неделимая единица абстракции, из которой нельзя выкинуть ни один метод, не нарушив контракт. Я сомневаюсь что у вас есть настолько сложная абстракция. Как правило типажи ограничиваются 1-4 методами. Так что задумайтесь как поделить типаж на 2-3 отдельных.

                                                                                                                                                  Теперь по поводу вашего подхода. На каком основании вы приняли решение что B наследуется от A? Потому что A существовал в вашей кодо