Как стать автором
Обновить

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

КДПВ настолько классная, что пришлось прочесть статью :)

Прекрасная статья. Всё чётко и без воды.

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

Если вам всё понятно, то можно выжимку из статьи?

У вас токены на LLM закончились что ли?

Может, вы просто троечник, которому мерещится что всё понял, а реально ничего.

Лично мне из статьи неясно

  • Нафига

  • Все ли варианты полиморфизма указаны

Например, в заголовках идёт перечисление типов полиморфизма: перегрузка функций (также известный как ad-hoc полиморфизм), приведение типов (полиморфизм подтипов) и вдруг следущий пункт "Disjoint union и Algebraic data types"

Ответьте, желательно не выходя за пределы статьи, это отдельный вид полиморфизма? Какое отношение к полиморфизму и зачем после первых двух вариантов внезапно это понятие введено?

Полиморфизм, основанный на структуре данных? Обычно такой не упоминают. Является ли этот вариант полиморфизмом в полной мере? Особенно, с учётом что не все языки дают гарантии что switch и перебор образцов гарантируют полноту перечисления вариантов. Может, это попытка полиморфизма, но без гарантий.

И так далее...

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

Ставлю вам двойку по внеклассному чтению. К следующему уроку комментарию, прочитайте, пожалуйста, все 3 статьи, чтобы вести дискуссию более предметно. Никому ваши галюцинации тут не интересны.

Прекрасная статья. Всё чётко и без воды.

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

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

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

 Строго говоря, это не так. У функциии в программировании есть самостоятельное значение. Тут бы ближе по смыслу был термин "операция".

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

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

С этим надеюсь разобрались. Методы - это обычный синтаксический сахар над функциями.

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

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

На самом деле меняет. Например возьмет JS там есть очереди функций, очереди микро задач, макро задач, и ни в одну из них не помещаются, например, арифметические операции.

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

Все эти способы записи просто вариации синтаксиса над вызовом функции. Не смущайтесь увидев знаки + - > и т.д. - от имен обычных функций они ничем не отличаются.

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

Надеюсь теперь стало понятно что оператор - это такая же функция с особым синтаксисом.

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

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

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

Я интереса ради загуглил эту тему и гугл выдал вполне ясный ответ:

Типы полиморфизма: Существуют разные виды полиморфизма

Подтиповой полиморфизм (Polymorphism by Subtypes): Обработка объектов различных классов, которые связаны отношениями наследования.

Параметрический полиморфизм (Parametric Polymorphism): Использование обобщенных типов, которые позволяют работать с данными различных типов, не указывая их конкретно при определении функции.

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

Ниже вы все это так же распишете.

Тип данных представляет из себя множество допустимых значений. Например логический тип это два возможных варианта или true или false представляющих множество { true | false }. Если спрятать такой тип за nullable ссылкой то можно получить уже 3 возможных значения { true | false | null }. Целочисленный 32 битный тип это уже 4 294 967  296 допустимых значений { 2,147,483,648 |…| -1 | 0 | 1 | .. |  2,147,483,647 }.

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

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

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

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

Суть дела не меняет. В контексте обсуждения метод - это такая же функция а нет смысла выделять ему особое внимание.

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

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

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

Как мы выяснили, на самом деле это не так. Далеко не каждый оператор является функцией. Особенно это относится к тем операторам, которые вы перечислили.

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

Да в общем-то нет. Именно из-за того, что теперь полиморфизм - это в том числе и про ООП .

Это, чисто логически, странная точка зрения что более общее понятие про более частное а не наоборот.

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

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

Это как это? Вообще то все примитивные данные типа 32-64-80 бит числа выбраны именно из-за того, что это данные с которыми работает ЦПУ. Это же относится и к операциям.

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

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

А по поводу магии, у вас ее действительно очень много, потому что вы в одну кучу смешали логическую и физическую составляющие.

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

Не так разве? А ассемблер синтаксический сахар над байт кодом)

С точки зрения программиста вся эта закулиса ему недоступна.

Собственно для упрощения работы программиста его и отодвинули от ассемблера. Знаешь ассемблер и С++, поймешь много чего и в других языках.

Поэтому он может предполагать

?????????

Не так разве? А ассемблер синтаксический сахар над байт кодом)

Нет. ЯП как и синтаксический сахар - это самостоятельные понятия.

Собственно для упрощения работы программиста его и отодвинули от ассемблера. Знаешь ассемблер и С++, поймешь много чего и в других языках.

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

?????????

Что?

Что?

Почему программисту недоступна закулиса?

Если мы знаем частоту и количество нужных тактов, мы не можем рассчитать время выполнения??

Потому что для программиста ЦПУ - это набор регистров и команд. Программист сам не управляет конвейером ЦПУ.

О каком времени вы говорите? Количество тактов и есть единица времени выполнения команды.

Программист сам не управляет конвейером ЦПУ.

Ага, а этот конвеер ЦПУ может что-то делать без операционной системы? Программист операционной системы реализует процессы и их взаимодействие с ЦПУ, он наугад все делает?

Или кнопка в диспетчере задач "завершить процесс",на неё не то что программист может нажать и завершить процесс, а даже самый рядовой пользователь и получается повлиять на конвеер ЦПУ?)

Стиральная машинка которая у вас дома и скорее всего на линуксе, кто её работу с процессором настраивал если не программист?)

Нет. ЯП как и синтаксический сахар - это самостоятельные понятия.

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

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

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

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

Хорошо, я загуглил, конвеер ЦПУ распараллеливает выполнение команд, и как это не бьётся с тем что если я зввершу процесс то они не будут выполняться даже в конвеере?))

Опять же вы пишите что закулиса недоступна программисту, но гугл пишет что программисты оптимизировали компиляторы под конвееры в ЦПУ... и кто из вас меня пытается надурить? :)

есть вилка и есть зубчики вилки

Зубчики могут существовать и без вилки, а вот СИНТАКСИЧЕСКИЙ сахар не может без языка, понимаете?))

Без гугла ответите чем агрегация отличается от композиции?)

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

Язык программирования это набор правил. Функция, метод и оператор - это одно и тоже по смыслу, записанное с разным синтаксисом. Почему то по Вашей логике если я напишу:

5 + 5
add(5, 5)
5.add(5)

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

Это как это? Вообще то все примитивные данные типа 32-64-80 бит числа выбраны именно из-за того, что это данные с которыми работает ЦПУ. Это же относится и к операциям.

Почему то вы вбили себе в голову что операторы это что-то особенное.

"Hello" + " world"

Вот это в языке который Вы используете тоже наверное процессором напрямую исполняется? Или это просто имя функции наподобие concat() которой перегрузили знак + ?

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

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

А по поводу магии, у вас ее действительно очень много, потому что вы в одну кучу смешали логическую и физическую составляющие.

Это Вы начала на ASM тему переводить. Не нужно меня приплетать.

Язык программирования это набор правил. Функция, метод и оператор - это одно и тоже по смыслу, записанное с разным синтаксисом.

Именно по смыслу - это разные вещи. Если мы говорим про языки программирования каждая часть речи отличается друг от друга. Это под капотом они могут использовать одинаковые механизмы. Выражайтесь ясно.

 Почему то по Вашей логике если я напишу:

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

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

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

Почему то вы вбили себе в голову что операторы это что-то особенное.

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

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

"Hello" + " world"

Это вот ваше желание все скинуть в одну кучу и приводит к таким вот вопросам.

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

Так я именно на это выше и указал. А вы опять все одну кучу скинули. Зачем?

Это Вы начала на ASM тему переводить. Не нужно меня приплетать.

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

Мне просто любопытно, если в коде увидим что-то типа:

interface makeSound {
  makeSound(): void
}

class Cat implements makeSound {
  makeSound() {
    //...
  }
}

class Dog implements makeSound {
  makeSound() {
    //...
  }
}

function getAnimal(): makeSound {
  return {} as makeSound
}

getAnimal().makeSound()

Мы можем говорить, что данный код использует идеи полиморфизма?
На вид тут вообще все про ООП. И объяснять данную ситуацию в терминах которые не относятся к ООП, потому что: "Как вы могли заметить, полиморфизм - это не про ООП, это история о том как различными способами дать возможность функциям работать гибко с данными разных типов." - по моему, крайне не эффективно.

Именно по смыслу - это разные вещи. Если мы говорим про языки программирования каждая часть речи отличается друг от друга. 

Вне зависимости от того что под капотом вот это 1 + 2 будет называется оператором а вот это add(1, 2) или это add 1 2 функцией, или методом или даже процедурой. То что использует спецсимволы и способы способы записи мы привыкли называть операторами. Вот и все. Что оператор что функция, если обобщить, это некое действие, примененное к 0..n параметрам. Действие это может быть применено каким угодно образом, это к тому что мы называем операторами и функциями не имеет никакого значения. Даже если брать особенности языков программирования, сегодня у оператора одна реализация а завтра другая а послезавтра вообще добавят поддержку своих операторов. Это никак не повлияет на то что вот это мы будет называть оператором 1 + 2, как бы оно не было реализовано, и никак не повлияет на то что это продолжит быть действием к 0..n параметрам. Суть это никак не меняет. Если мы рассуждаем абстрактно то понятие перегрузка одинаково применимо что к оператору что к функции что к методу вне зависимости есть перегрузка того или иного в языке, сама идея не меняется. Мы имеет одну идею - действие может быть перегружено, а чем выражено действие не важно, вместо того чтобы на каждый чих придумывать специальное правило и понятие, потому как в общем смысле разница только в синтаксисе а идея одна и тажа. Если человек понимает эту идею он применит ее в том языке программирования которым пользуемся согласно ее особенностям. Если для Вам тяжело это осознать то я не вижу смысла продолжать одно и тоже.

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

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

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

Мы можем говорить, что данный код использует идеи полиморфизма?На вид тут вообще все про ООП. И объяснять данную ситуацию в терминах которые не относятся к ООП, потому что: "Как вы могли заметить, полиморфизм - это не про ООП, это история о том как различными способами дать возможность функциям работать гибко с данными разных типов." - по моему, крайне не эффективно.

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

И тут заходите Вы:

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

Если до сих пор не понятно, фраза "полиморфизм - это не про ООП" означает что не нужно думать что полиморфизм придумали в ООП и что весь полиморфизм - это полиморфизм подтипов. Полиморфизм - это не про ООП потому что ООП - это про полиморфизм, в том чисел, а не наоборот.

Так, минуточку

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

Это вот вы написали теперь отказываясь от того, что началось. Напоминаю с чего все началось

Строго говоря, это не так. У функциии в программировании есть самостоятельное значение. Тут бы ближе по смыслу был термин "операция".

Теперь вы отказываетесь от "функции" и выбираете "действие". Собственно, что и требовалось показать.

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

А вы точно поняли, что я вам сказал и на что вы отвечаете? Выше же все написано и вот этот ваш абзац с этим никак не вяжется.

Если до сих пор не понятно, фраза "полиморфизм - это не про ООП" означает что не нужно думать что полиморфизм придумали в ООП и что весь полиморфизм - это полиморфизм подтипов. Полиморфизм - это не про ООП потому что ООП - это про полиморфизм, в том чисел, а не наоборот.

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

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

Теперь вы отказываетесь от "функции" и выбираете "действие". Собственно, что и требовалось показать.

Нигде написано что я от чего то отказываюсь

А вы точно поняли, что я вам сказал и на что вы отвечаете?

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

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

А хлеб в который придумали добавлять отруби он про отруби и именно про отруби. У Вас определенно есть чему поучится в обращении с терминологией.

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

Термин функция был выбран потому что он наиболее общеупотребимый и понятный подавляющему большинству читателей. Естественно что в Java или C# правильно говорить метод, и не важно с ресивером он или нет, в Scala вообще есть и функции и методы и последние там опять таки не связаны с ресиверами, в каких то языках есть процедуры, в фп языках есть понятия чистых и монадических функций , но никто в здравом уме не полоскает друг другу мозг в стиле "как вы посмели говоря в контексте Java назвать метод функцией это же совсем другой язык тут так нельзя", люди, особенно кто пишет на не на одном языке, называют как им удобно даже не смотря на то про какой язык они говорят, и что характерно все друг друга понимают. И что еще более важно - суть это никак не меняет.
Был выбран термин функция, потому что так всем удобнее и понятнее без привязки к конкретному языку и именно функция послужила базой для таких вещей как оператор и конструктор, потому что это просто, понятно, непротиворечиво, там устроено в большинстве языков и главное в сути своей этим и является и все понятия с данной моделью соотносятся.
Вы или это не понимаете, ну тогда извените, это статья не для Вас.
Либо все понимаете но зачем-то страдаете фигней.

Если есть что еще обсудить - милости прошу, но этой демагогией у меня желания заниматься более нет.

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

Я просто оставлю это здесь.

А что это за цифры?

За многабукф статьи потерялся практический смысл полиморфизма:

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

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

Какой практический смысл этого?

  1. Гибкость и Расширяемость (Flexibility & Extensibility):

    • Смысл: Вы можете добавлять новые типы объектов (новые "сущности" со своими уникальными "способами проведения"), которые соответствуют существующему интерфейсу, без изменения кода, который этот интерфейс использует.

    • Пример: У вас есть система, обрабатывающая различные типы документов (IDocument с методом process()). Изначально есть PdfDocument и WordDocument. Позже вам нужно добавить ExcelDocument. Вы просто создаете новый класс ExcelDocument, реализуете IDocument, и существующий код обработки документов (который работает с IDocument) автоматически сможет обрабатывать и Excel-файлы. Ему не нужно знать, что появился новый тип.

    • Результат: Система легко адаптируется к новым требованиям.

  2. Уменьшение связанности (Decoupling / Loose Coupling):

    • Смысл: Компоненты системы меньше зависят друг от друга. Клиентский код зависит от стабильной абстракции (интерфейса), а не от изменчивых/расширяемых полных контрактов (в ООП - от конкретных классов).

    • Пример: Модуль оплаты (PaymentProcessor) работает с интерфейсом IPaymentGateway (например, processPayment()). Вы можете легко заменить одну платежную систему (например, StripeGateway) на другую (PayPalGateway), если обе реализуют IPaymentGateway. Сам PaymentProcessor не изменится.

    • Результат: Изменения в одной части системы с меньшей вероятностью "ломают" другие части. Легче заменять компоненты.

  3. Упрощение кода и повышение читаемости (Simplified & More Readable Code):

    • Смысл: Вместо громоздких конструкций if-else if-else или switch для обработки разных типов, вы пишете один общий код, работающий с интерфейсом.

    • Пример:

      // Плохо:
      void handleShape(Object shape) {
          if (shape instanceof Circle) ((Circle)shape).drawCircle();
          else if (shape instanceof Square) ((Square)shape).drawSquare();
          // ... и так далее
      }
      
      // Хорошо (с полиморфизмом):
      void handleShape(IShape shape) {
          shape.draw(); // Единый способ, конкретная реализация вызовется сама
      }
      
    • Результат: Код становится короче, понятнее и менее подвержен ошибкам при добавлении новых типов.

  4. Поддержка Принципа Открытости/Закрытости (Open/Closed Principle - OCP):

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

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

    • Результат: Более стабильная система, которую легче развивать.

  5. Облегчение тестирования (Easier Testing):

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

    • Пример: Тестируя UserService, который использует IUserRepository для доступа к данным, вы можете передать ему MockUserRepository, который возвращает предопределенные данные без реального обращения к базе. UserService будет работать с MockUserRepository точно так же, как с реальным, через интерфейс IUserRepository.

    • Результат: Тесты становятся проще, быстрее и надежнее.

  6. Создание фреймворков и библиотек:

    • Смысл: Полиморфизм позволяет создавать фреймворки, которые определяют "точки расширения" (интерфейсы), а пользователи фреймворка предоставляют свои конкретные реализации.

    • Пример: GUI-фреймворк может определять интерфейс IEventListener с методом onEvent(). Пользователи создают свои обработчики событий, реализуя этот интерфейс. Фреймворк вызывает onEvent() для нужного слушателя, не зная деталей его реализации.

    • Результат: Мощные, гибкие и переиспользуемые программные компоненты.

Итог:

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

Отличная статья! Если честно пока осилил только половину, получил прозрение на одном моменте:

Мономорфная функция — это функция способная применяться к конкретному типу данных.

Полиморфная функция — это функция способная применяется к различным типам данных.

Буквально сегодня писал код и чувствовал легкий зуд от пробела знания в этом моменте, теперь думаю мне станет чуточку легче)

Могу я спросить, что вы нашли "ненормального" в данной статье? Хотелось бы услышать конкретику в ответ от человека (а не LLM текст), который полностью прочел статью, а не только заголовки.

Написал же, только в разных местах.

Идёт очень долгая вводная с терминами "базовые понятия", который эти понятия вводит спорным образом, ладно, перемотал. Потом перечисление типов полиморфизма с прыжками в сторону, потом с тем же стилем заголовка внезапно "Disjoint union и Algebraic data types", без объяснений и с резким сваливанием в крен экзотики математических типов и option, потом имитации option с длинным кодом. Чиво??? Мы о чём? Я судорожно пытаюсь вспомнить как это относится к теме, гугление приводит к тому что термин Disjoint union это то же что и discriminated union, которым пестрят статьи, но первый раз слышу Disjoint union.

Потом вспоминаю классификацию полиморфизма от Карделли, Вегнер, 1985 - дал ссылки внизу. И там этого нет.

Далее что? Если бы этот пункт был написан внятно, я бы убедился: вот ещё один вид, не вписавшийся в старую классификацию. Но пункт убогий и остаётся только непонимание. Что значит внятно: выше было бы определение вот такое сабж, такое не сабж. Никакой проверки, подтверждения не было дано, жрите что дают.

Уф, я уже устал писать.

Дальше уже листал без интереса. Только утиную типизацию прокомментировал

И за длинным и некорректным описанием уже забыл зачем все это нужно. Проще написать самому.

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

Да не бомбит у меня! ©™

Начиналось как шутка

А потом выбесили меня

Хорошо давайте по порядку.
Я упрощу до Algebraic data types чтобы никого не путать.

Идёт очень долгая вводная с терминами "базовые понятия", который эти понятия вводит спорным образом, ладно, перемотал.

Потом вспоминаю классификацию полиморфизма от Карделли, Вегнер, 1985 - дал ссылки внизу. И там этого нет.

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

Полиморфная функция — это функция способная применяется к различным типам данных.

Вот определение вокруг которого построено дальнейшее повествование и прочитав его думаю очевидно что, согласно данной статье, если функция принимает алгебраический тип A | B, то она может применятся и к A и к B. А функция которая может примениться к различным типам - полиморфна. Это предельно простое определение.

Но пункт убогий и остаётся только непонимание.

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

Пояснения по классификации:

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

Основные виды полиморфизма:

  1. Ad-hoc (перегрузка)
    Одна функция, разные реализации для разных типов аргументов.
    Пример (C++ overload):

    cpp

    int abs(int);
    double abs(double);
  2. Ad-hoc (coercion, приведение типов)
    Неявное преобразование одного типа в другой.
    Пример (Java coercion):

    java

    long x = 5; // int → long неявно
    foo(x);
  3. Parametric (параметрический)
    Функция работает одинаково для любых типов, не зная их деталей.
    Пример (Kotlin generic ADT):

    kotlin

    data class Option<T>(val v: T?)
    fun <T> id(x: Option<T>) = x
  4. Subtype (подтип-полиморфизм)
    Функция принимает базовый тип (интерфейс), вызывая методы конкретных подтипов.
    Пример (C# subtype):

    C#

    interface Animal { void Speak(); }
    void talk(Animal a) => a.Speak();

Частные случаи (сводятся к основным, но имеют свои особенности и названия):

  • Haskell type-class (ad-hoc полиморфизм через автоматический выбор реализации):

    haskell

    (==) :: Eq a => a -> a -> Bool  
    1 == 1  -- True  
    'a' == 'b'  -- False
  • Java F-bounded (ограничение типа самим собой, частный случай parametric):

    java

    class User implements Comparable<User> {
    public int compareTo(User other) { ... }
    }
  • PureScript row polymorphism (структурный подтип-полиморфизм для записей):

    purescript

    greet :: { name :: String | r } -> String  
    greet person = "Hi, " <> person.name
  • Haskell higher-kinded polymorphism (parametric полиморфизм над контейнерами):

    haskell

    
    fmap :: Functor f => (a -> b) -> f a -> f b
    fmap (+1) [1,2,3] -- [2,3,4] fmap (+1) (Just 10) -- Just 11
  • Утиная типизация (duck typing) — неявный структурный подтип-полиморфизм, проверка наличия методов/полей в runtime:

    python

def len_plus_one(obj):
    return len(obj) + 1

len_plus_one([1,2,3])  # 4
len_plus_one("abc")    # 4
  • OCaml polymorphic variants (структурный подтип-полиморфизм с открытыми enum'ами):

    ocaml

    let to_string = function
    | `Int i -> string_of_int i
    | `Bool b -> string_of_bool b to_string (Int 5) (* "5" *) to_string (Bool true) ( "true" )
  • Утиная типизация (duck typing) — неявный структурный подтип-полиморфизм, проверка наличия методов/полей в runtime:

    Python

def len_plus_one(obj):
    return len(obj) + 1

len_plus_one([1,2,3])  # 4
len_plus_one("abc")    # 4

Итоговая классификация кратко:

  • Основные виды:

    • Ad-hoc (перегрузка)

    • Ad-hoc (приведение)

    • Parametric (дженерики)

    • Subtype (наследование, интерфейсы)

  • Частные случаи (сводятся к основным, но имеют свои особенности):

    • Type-class (Haskell)

    • F-bounded (Java)

    • Row polymorphism (PureScript)

    • Higher-kinded (Haskell)

    • Existential (Rust)

    • Polymorphic variants (OCaml)

    • Duck typing (Python, JS и др.)

Хоть Haskell и редкий язык, его примеры полезны для понимания концепций. Java и Rust — более распространённые, поэтому их особенности (F-bounded, existential) важны и интересны. Утиная типизация тоже упомянута.

хотя... частные случаи же

ну ок

Основные виды:

  • Ad-hoc (перегрузка)

  • Ad-hoc (приведение типов)

  • Parametric (дженерики)

  • Subtype (наследование, интерфейсы)

Частные случаи (сводятся к основным, но имеют свои особенности):

  • ADT (enum algebraic, явный перечень вариантов + pattern matching) — специальный случай subtype-полиморфизма (варианты enum как подтипы)

  • Duck typing (Python, JS и др.) — специальный случай subtype-полиморфизма (неявный структурный)

  • Type-class (Haskell) — специальный случай ad-hoc полиморфизма

  • F-bounded (Java) — специальный случай parametric полиморфизма

  • Row polymorphism (PureScript) — специальный случай subtype-полиморфизма (структурный)

  • Higher-kinded (Haskell) — специальный случай parametric полиморфизма

  • Existential (Rust) — специальный случай subtype-полиморфизма

  • Polymorphic variants (OCaml) — специальный случай subtype-полиморфизма (структурный)

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

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

Тяжело с этими кожаными

Я или сам дошёл или подсмотрел где следующее определение:

Полиморфизм это возможность обращения к разным реализациям через общий интерфейс

И оно как то засело у меня

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации