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

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

Неделя извращенства на хабре ;)
Не извращенство. После функциональных языков такого в джаве ой как не хватает. А на работе писать можно только на джаве :)
Да, пишешь на С++, хочется на Java. Пишешь на Java, хочется на Scala.
На С++ описанные в посте конструкции уже лет 10 во всю используются, + имитация лямбд + каррирование (binding параметров). Так что скорее пишешь на Java, хочется на С++.
Хочется на Scala — пишешь на Asm'е :-) даешь рекурсию!
Вместо join, наверное «более функциональным» было бы реализовать аналог reduce, а уже join можно сделать на его основе.
Согласен с вами.

Просто я хотел попроще, чтобы людям не знакомым с функциональным программированием было понятнее. Ну, и место в статье ограничено.
А чтобы сделать его еще более функциональным, надо было вместо аналога reduce надо было реализовать аналог foldl, на котором реализовать reduce, на котором реализовать join.
Pattern-matcher было бы очень интересно.
Спасибо
Python? Почему его причислили к функциональным языкам?
Списочные выражения
Списочные выражения к функциональному программированию относятся постольку-поскольку.
Для питонистов это тот уровень функциональщины, который они могут принять:)
Ну я лично пишу в том числе и на питоне, однако назвать его функциональным язык не поворачивается)
Ну все-таки списочные выражения пришли из ML-языков, насколько я понимаю. Да и выглядят они в точности как описания множеств из учебника по алгебре.

Википедия гласит, что в Python есть следующие элементы ФП:
функции высших порядков, развитые средства обработки списков, рекурсия, возможность организации ленивых вычислений
Это немало:)
Списочные выражения были задолго до ML. Взять хотя бы Лисп. Вообще списки — базовая структура данных и в том или ином виде есть в любом высокоуровневом языке программирования.
И выкиньте свой учебник по алгебре, списки никак не тянут на определение множества хотя бы потому, что в них важен порядок элементов, а в множестве нет. Если точно, список — множество пар вида (i, e), где i — индекс (элемент множества с установленным отношением полного порядка), e — элемент множества-носителя,

list ≡ {(i, e)} | i ∈ I, e ∈ E, (a, e1) = (b, e2) & a = b => e1 = e2.
У меня создалось ощущение, что вы не знаете, что такое списочное выражение и чем они отличаются от списков.
просветите
Я бы с удовольствием, но плохо объясняю. Однако вот отрывок из учебника на Хаскелю. Автор хорошо показывает, что такое списочные выражения и откуда у них ноги растут. Обратите внимание, синтаксис очень похож на питоновский.
Спасибо, но я знаком с Хаскелем (языком, не с Карри). Списочные выражения, или, точнее, S-выражения — это способ записи, иногда — способ хранения данных. И списки как структура данных обычно реализуются так, чтобы можно было осуществить разделение «голова-хвост»
Ваше неверное представление, видимо, возникло из-за построителей списков в Хаскеле. Так вот, списки иногда (подчеркиваю, только иногда) являются удачным способом для представления множеств. В частности, механизм построения списка основан на теореме, что множество может быть определено с помощью универсума и предиката принадлежности элемента универсума к множеству. Но любая модифицирующая операция над множествами, представленными списками, требует проверки результата на наличие повторов
И, кстати, в списках-то индекс как раз и не нужен. Нам нужна только его «голова» (ну и хвост). Это позволяет осуществлять ленивые вычисления.
А кто говорит об индексе как о физически хранимом параметре? Тут важно, что в списке важен ПОРЯДОК элементов. У вас легко один и тот же элемент может быть головой списка и при этом содержаться в его хвосте. В множестве не может быть двух одинаковых элементов по определению
> Википедия гласит, что в Python есть следующие элементы ФП:…
Это мало. Эти концепции можно реализовать даже на обычном Си. Однако это не сделает Си функциональным языком программирования. «Функциональность» питона проистекает из-за наличия в нем лямбда-функций, все остальное — просто следствие
Лямбды — это тупо сахар. Ничего в них мистического нет. Куда можно передать функцию, туда можно передать и лямбду, и наоборот.
лямбды — это инструмент построения функций. Без них язык не может быть функциональным в принципе. И это не просто сахар, за ними стоит очень мощная теория
А вы не путаете лямбда-исчисление и понятие lambda в терминах питона? lambda в питоне — это просто анонимная функция. Что с ее помощью можно сделать такого, что нельзя с помощью именованной, покажите мне?

Очень мощная теория стоит за тем, что функции являются значениями первого порядка и их можно передавать. AFAIK, это-то и имеет отношение к трудам Алонзо Чёрча.
Я не совсем про лямбда-исчисление Черча. Люмбда-выражения применительно к программированию — это способ построения функций «на лету». И за этим тоже стоит теория, только из области компиляции и трансляции. В питоне это действительно просто анонимная функция. Но эта функция может быть создана в процессе исполнения программы
Я могу сходу назвать еще два способа построения функций в рантайме в питоне, никак не вовлекающих лямбды. И что? Давайте теперь молиться на декораторы и метод __call__?
__call__ и декораторы — это совсем из другой степи, не имеющей отношение к функциональному программированию, и кстати не позволяют породить новые функции
а лямбды-то как порождают новые функции, просветите?
как конкретно интерпретатор Питона это делает — не знаю. Хотя ради Вас пороюсь в исходных кодах. А вот само «порождение» функции — пожалуйста:

def multN(value) : return (lambda x : x**value)
class MultN(object):
    def __init__ (self, power):
        self.power = power

    def __call__ (value):
        return value ** self.power
А так?

def fff(value) :
     if value < 10 :
         return (lambda x : x*2)
     else :
         return (lambda x y : x*y)
Добавляем в класс соответствующие методы, используем apply.

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

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

Такое использование объектов свойственно языкам, в которых есть перегрузка оператора оператора «вызов функции».

Нет, такое использование объектов свойственно только тем языкам, в которых функции не являются гражданами первого класса.
Ээээ. В каком топике? Для какого языка?
Нажмите ctrl+home, чтобы вспомнить. Нажмите ctrl+w, чтобы забыть.
Может я Вам открою глаза на мир… Но Java не является компилируемым языком в том смысле, что не компилируется в машинный код
Теоретически может существовать железо, для которого байт-код джавы будет являться ассемблером. Ну, а пока такого железа под рукой нет, будем его виртуализировать.
Увы, но как-раз теоретически такого железа пока не придумано (мне теория такого железа неизвестна). Именно в перегрузке оператора вызова во время исполнения загвоздка. Можно определить N-е количество объектов с разными версиями этой функции, но нельзя получать их в процессе выполнения, это противоречит архитектуре фон Неймана. Поэтому на данный момент такая функциональность достижима только с использованием лямбда-исчисления. И именно по этой причине нет компилятора Питона. Просто нет таких технологий компиляции
А как же JIT?
JIT (Just In Time) производит исполнение, перевод в машинные инструкции «на лету». Но прямого соответствия между JIT-кодом и машинными командами для заданной архитектуры нет. То есть это все равно процесс интерпретации, хотя и более низкого уровня
Хм, ну тогда и компиляция — это «процесс интерпретации, хотя и более низкого уровня». Разница между компиляцией и интерпретацией зачастую только в том, что результат компиляции сохраняется в ПЗУ в виде файла.
Это принципиальная разница. Пока никому не удалось реализовать в железе рефлексию и интроспекцию
В железе может и нет, а вот в ассемблере — запросто, мне кажется:)
НЛО прилетело и опубликовало эту надпись здесь
JIT-кода в природе нет, тут я маху дал. Это технология. И ради Бога, компилируйте в байт-код что угодно (условно), но это все равно байт код. Я говорю о том, что нет прямого соответствия между байт-кодом Java и машинным кодом процессора.
НЛО прилетело и опубликовало эту надпись здесь
Эта функция будет ТРАНСЛИРОВАТЬСЯ. Нет соответствия означает, что нет (опять же, мне неизвестно) способа транслировать программу на Java в набор машинных инструкций минуя байт-код. Также как такого способа нет для Питона, Руби и многих других современных языков. Процесс выполнения Java-программы — это процесс интерпретации байт-кода Java-машиной
НЛО прилетело и опубликовало эту надпись здесь
Тогда где экзешники Java-программ? Байт-код, как Вы наверное знаете, очень хорошо документирован. Существует GNU-версия компилятора Java, который компилирует в машинный код, однако полной совместимости с языком добиться так и не удалось. Потому что не все так просто. Java-машина помимо сбора статистики (которая, кстати, может помочь оптимизировать процесс интерпретации, но никак не исключить его) делает еще кучу вещей, реализация которых в машинных инструкциях привела бы к необходимости добавлять исполняющую среду в каждую скомпилированную программу. Вот по этому и производится компиляция в байт-код. Потому что дальше этого сделать что-либо весьма проблемно
НЛО прилетело и опубликовало эту надпись здесь
Интересно. Получаем следующую картину. Байт код эквивалентен машинному в том смысле, что может быть в него преобразован. Для большинства языков есть компиляторы в байт-код Java. То есть все языки — компилируемы! Ура товарищи, все проблемы решены!
НЛО прилетело и опубликовало эту надпись здесь
Про scheme история отдельная. Там компиляторы были столь ужасны, поскольку все основные механизмы ставились на костыли. И все же пример показательный. Даже для языка с таким простым синтаксисом, как scheme, не удалось создать достойный компилятор. Java — в десятки раз сложнее даже с синтаксической точки зрения. Поэтому я и говорю, что байт-код — это вынужденная мера. Компилятора Java в машинный код нет и не предвидится не потому что это никому не нужно. Желающих навалом. Только не удается это сделать
НЛО прилетело и опубликовало эту надпись здесь
Всего один вопрос. Что делает JVM? При запуске Java-программы один раз создается набор машинных инструкций — эквивалентная программа — и после этого JVM с чувством выполненного долга отдыхает? Потому что если нет, то речь идет об интерпретации байт-кода.

> Для scheme нету компиляторов потому же что и для питона — отсутствие декларации типов

Ох, если бы все было так просто…
НЛО прилетело и опубликовало эту надпись здесь
Тогда я неправ. И все-таки рисуемая Вами картина выглядит черезчур идеалистической. Если виртуальная машина (по-сути исполняющий механизм) оказывает какое-либо управляющее воздействие в процессе исполнения программы, то эквивалента в машинных кодах нет. А если такого воздействия нет, то непонятно, почему это называют виртуальной машиной, ведь в этом случае ее функции ограничиваются JIT-компиляцией.
НЛО прилетело и опубликовало эту надпись здесь
Ну это понятно, очевидная оптимизация. Но слабо верится, что функции виртуальной машины ограничиваются вышеперечисленными. Плотнее займусь этим вопросом
Хотя в моем представлении куда более правдоподобной выглядит картина, кода часть программы, та часть, где это возможно, компилируется JIT-ом, а остальная, в которую входят рефлексия и описанные в топике вещи, интерпретируются в процессе исполнения. Очень сложно представить соответствующий таким штукам набор машинных команд
Есть принципиальная разница между интерпретатором и рантаймом, Вам не кажется? Питон тоже можно таскать собой как библиотеку с байт-кодом, это не делает его компилируемым, потому что все равно осуществляется процесс интерпретации. В отличие от Си, в котором иногда происходит обращение к функциям библиотеки времени исполнения
НЛО прилетело и опубликовало эту надпись здесь
В питоне нет лямбда-исчисления. Только безымянные функции, по-старинке названные лямбдами. И все-таки такая нехитрая абстракция позволяет-таки создавать функции в процессе выполнения. Ограниченно, но позволяет
Соответствия нет. Когда в программе на Java присутствует многопоточность, потоки имитируются Java-машиной, а не средствами операционной системы. Когда происходит рефлексия — идет анализ байт-кода, а не машинного. Байт-код обеспечивает кроссплатформенность — бесспорно. Более эффективную компиляцию? скорее исполнение. Но не только. Это еще и вынужденная мера, позволяющая реализовать все средства языка
Тогда где экзешники Java-программ?

Здесь. Также стоит посмотреть сюда.
Посмотрел. Частичная компиляция + альтернативная реализация Java-машины, не более
Подождите-ка. В C++ есть перегрузка оператора (). Вы же не скажете, что это не компилируется.
Перегрузка — процесс статический. Динамически реализовать его никак нельзя
Интересно. Буду знать. Хотя мне сложно представить такой процессор
Можно нагуглить подробности. Я не очень в процессорах разбираюсь, поэтому не стал искать, но инфа есть.
Увы, но как-раз теоретически такого железа пока не придумано (мне теория такого железа неизвестна)

То, что вам что-то неизвестно, ещё не означает, что этого не существует в природе: en.wikipedia.org/wiki/Java_processor.

Вообще, разработка процессоров специально для байт-кода высокоуровневого языка началась отнюдь не с Java. Ещё задолго до Java-машин существовали Lisp-машины и Smalltalk-машины.
было уже «практически»,
цитата «Компания Sun на Микропроцессорном форуме представила процессор MicroJava 701. Это первый процессор, непосредственно исполняющий байт-код Java.»
источник — www.osp.ru/cw/1997/42/25003/
(«Computerworld Россия», № 42, 1997 )
Да, я ниже уже отписал об этом.
А именно это делает язык функциональным. Если основная единица — объект. То язык объектный. И можно говорить только об элементах других парадигм программирования
Тут ниже высказывалось мнение, что функциональные языки — это те, в которых все функции чистые. Я вот как-то с этим больше согласен.

Но я никогда и не утверждал, что питон — функциональный язык. Но функциональщины там довольно много, и это не только лямбды и списочные выражения.
Чистота функций — следствие, не причина. Списочные выражения — тоже. Парадигму составляют не какие-либо технологии, а система базовых установок, если угодно — идея. Хаскель — функциональный язык, однако писать на нем можно без использования списочных структур и с использованием изменяемых состояний. А вот лямбда-выражения — почти краеугольный камень
Кстати, apply позволяет сделать каррирование, что, в общем-то, порождает новую функцию.
Об этой возможности не знаю, напишите пожалуйста поподробней. Хотя сути в общем-то не меняет. Каррирование просто частный случай. Например в Хаскеле оно определяется очень просто через те же лямбды:

carry f (x, y) = \x y -> f (x, y)
Тут я погорячился малость, простите — все-таки apply в питоне возвращает не функцию, а результат выполнения.
И да, рыться не надо, спасибо:) Мне не интересна в данном случае конкретная реализация.
НЛО прилетело и опубликовало эту надпись здесь
Тогда всё — способности рантайма. Любая возможность языка — просто свойство рантайма
НЛО прилетело и опубликовало эту надпись здесь
Пожалуйста, поясните, почему вы считаете люмбды «тупо синтаксическим сахором», а списочное выражение нет?

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

Вам не кажется, что любой человеческий язык — это синтаксический сахар над криками и жестами?
Действительно важное отличие для меня состоит в совершенно отличной семантике. Так же часто упоминают отсутствие побочных эффектов и ленивые вычисления.
По-моему, единственной важной особенностью как-раз и является отсутствие побочных эффектов, все остальное можно накалякать сверху самому. По этому когда говорят, что человек пишет в функциональном стиле на каком-то языке, имеют ввиду, что он старается минимизировать изменяемые состояния. И по этому map > for.

Питоновская лямбда в свою очередь имеет действительно другую семантику, конечно, это не честно каррированная функция, но и не просто анонимная — в ней напрямую нельзя изменять состояния (lambda: c = 5 не покатит), хотя можно изменить косвенно, по тому, что окружение не «чистое», вроде (lambda x: x.y()).
Поясню. Допустим, мы имеем только чистые функции (без побочных эффектов, чтобы было проще возьмем только от одного аргумента, имена функций и аргументов не существенны, по этому их опустим), для того, чтобы при помощи них произвести какие-то вычисления нужно определить список операций, которые мы можем над ними выполнять. Алонзо Черч показал, что минимально необходимых операций всего 3 — α-преобрзование, β-редукция, η-преобразование.

Имея только чистые функции и 3 операции можно провести любые вычисления (язык полный по Тьюрингу), такой язык называют λ-счислением. Например, построить операцию рекурсии (Y-комбинатор).

Но так как каждый раз записывать Y-комбинатор или числа в Church numerals муторно и бессмысленно, то напридумали разного синтаксического сахара, например функции от нескольких аргументов (define в лисп) и тп
Таких функций напридумывали много (map, fold, reduce и тд) и язык реализцющих их каким-то образом, но не реализующий лямда-счисления, называют языком с функциональными возможностями, которым является Python. Lisp в свое время и был реализацией лямбда-счисления, по этому он просто функциональный язык.
Спасибо. Я стал лучше понимать, что придумал Черч.
Все верно, с одним замечанием. Y-комбинатор относится к комбинаторной логике, а не к лямбда-исчислению. Хотя они и изоморфны
И все-таки — одно дело, когда сверху сам калякаешь, а другое, когда язык это поощряет. Любая реализация тех же списочных выражений в виде некоторой функции будет выглядеть более громоздко.

Видимо, я был неправ, когда говорил, что лямбды в питоне не несут никакой новой семантики. Во-первых, я не знал про «lambda: c = 5 не покатит». Во-вторых, я пишу на JS, а там нет различия меду анонимными функциями и именованными (за тем исключением, что именованные подвергаются «поднятию», но я не могу придумать красивый не синтетический пример, когда это меняет их использование).
Или, другими словами — где та грань, где синтаксический сахар переходит в новую семантику? Как вы считаете?
эта грань в мозгах, а не в языке. Кто то на Java как фортране пишет, кто то на С в объектном стиле.
А писать на питоне можно очень по разному:)
Функциональное программирование — это в первую очередь иммутабельность данных, а уж потом все манипуляции с функциями, как со значениями.
В функциональном программировании не функция является объектом, а все элементы языка могут быть интерпретированы как функции. Поэтому можно говорить только об элементах функционального программирования в Java, Python и прочив мультипарадигмальных языках
Функциональное программирование — это идея в головах людей(или если хотите, подход к решению), а не конкретный язык или набор фишек, впрочем стоит отметить, что определенный набор-таки позволяет сделать это проще и естественее.

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

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

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

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

Как Function<F, T> реализует first class function?
А если не реализует — функциональное ли это программирование?

Скажем в javascript делаем так:

function outer () {
var ctx="внутри";
var inner = function() {
return ctx;
};
return inner;
}

var ctx="снаружи";
var res = outer();
console.debug(res());



Как это реализуется на Java?
НЛО прилетело и опубликовало эту надпись здесь
это делегаты, не замыкания
есть в совсем не функциональных паскале и си, например

замыкание, иже closure или first class function, должно еще уметь таскать за собой контекст, что и делает функциональщину такой приятной и полезной на асинхронных алгоритмах

и Function<F, T> из примера, строго говоря, контекст тащит, хоть и некими ограничениями
но я не пойму почему в статье об этом ни слова
НЛО прилетело и опубликовало эту надпись здесь
Ссылка на процедуру в Си вовсе не есть элемент функциональщины. По этому вопросу, собственно, и все дебаты. Как было правильно замечено ранее, функциональное программирование не определяется набором возможностей языка, это образ мышления. Если функцию можно рассматривать в математическом смысле, т.е. как отображение одного множества на другое, то это — функциональный язык. Поэтому Си — не функциональный, а императивный язык программирования
НЛО прилетело и опубликовало эту надпись здесь
Где в Си наследование, инкапсуляция и полиморфизм, чтобы его можно было считать объектным? Макросы там определяют процесс компиляции и только, к алгоритмике не имеют никакого отношения. И когда Вы сможете на Си сделать то же каррирование — тогда можно будет поговорить о функциональности. А так, простите, процедурный язык и только
НЛО прилетело и опубликовало эту надпись здесь
Я так понимаю, у нас с Вами разные исходные убеждения. Для меня неприемлемо говорить о поддержке какой-либо идеи даже элементно, если не отражены основные концепции. То, что в Си можно присваивать переменной адрес функции, не является элементом функционального программирования, потому что сама концепция присваивания функции, именно функции, чужда этому языку. Речь может идти о присваивании адреса, что можно реализовать и на ассемблере. И я не думаю, что найдется человек, всерьез утверждающий, что ассемблер поддерживает функциональную парадигму программирования
НЛО прилетело и опубликовало эту надпись здесь
ta6aku, спасибо за конструктивную критику.

В моей статье действительно ничего не было сказано о замыканиях, хотя это один из ключевых элементов функционального программирования. Уверяю вас, что замыкания в Java можно использовать также как и в JavaScript. Наверное в следующий раз стоит начать статью именно с этого вопроса.
А где тут функциональное программирование?
НЛО прилетело и опубликовало эту надпись здесь
eliah_lakhin, Если возможно, расскажите, в чём преимущества «функциональной» реализации по сравнению с «традиционной». Просто из примера, это не очевидно: кода стало больше + синтаксис generic'ов усложняет чтение.
Одно из преимуществ, например, заключается в том, что метод main, изначально написанный в императивном стиле(«традиционном»), занимал 6 строк, а после переписывания в функциональном стиле стал занимать всего одну строку.
main? Насколько я понял, он состоит только из вызова joinNumbers и для мало что изменилось при переходе к функциональной реализации.

А вот кода стало больше (INT_TO_STRING + map +join > императивный joinNumbers)

И появились конструкции вроде
public static <F, T> List map(Collection from, Function<? super F,? extends T> transformer) {
> А вот кода стало больше (INT_TO_STRING + map +join > императивный joinNumbers)

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

А можно вообще ничего не писать, просто взять уже готовый Google Collections или Apache Common Collections.
Странно, что никто не откомментировал про Clojure. Функциональное программирование без всяких извращений.
А что про него комментировать? Котлеты отдельно, мухи отдельно. Являясь большим фанатом clojure, я пишу участки, требовательные к производительности, на Java. И в Андроид пока clj не засунешь (я пробовал — 7 секунд на запуск пустого приложения). Если сравнить jvm-байткод с ассемблером, то Java сейчас — это С. Есть множество более экспрессивных и concise языков (Scala/JRuby/Groovy/Clojure), но что должно работать быстро, все равно пишется на Джаве.
А вот-такие штуки для Джавы вроде Google Collections — это как С++ как С. Код выглядит как дерьмо, но зато можно сделать больше, не теряя производительности.
Прошу прощения, конечно же «это как С++ для С».
Давно было интересно, как можно писать функционально на Java. Однако эта статья не ответила на основные мои вопросы, которые возникают сразу после объявления интерфейса Function:

1) Как реализуется композиция: λxyz.x(yz)?
2) Типы (которые всё-таки вводятся, т.к. интерфейс-то параметрический), как я понимаю, образуют категорию, допускающую экспоненцирование и умножение. Как реализовать контроль типов? Более точный вопрос: как выглядит функция каррирования, ведь для этого нужно как минимум определить произведение?
С экспоненцированием проблем таких нет, т.к. Function, будучи интерфейсом, может рассматриваться как тип аргумента/результата (что, собственно, и подчеркивается в статье)
1) В терминах Google Colletions композиция может быть реализована с помощью Functions.compose. Она принимает две функции и создает инстанс новой функции, которая при вызове осуществляет применение(Function.apply) переданных функций.

2) Каррирование можно реализовать, например, одним из двух способов:

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

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

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

В общем, оба способа на самом деле не универсальны, имеют свои плюсы и минусы, однако в целом позволяют решить проблему типобезопасным образом.
Вы видели Scala?
Там все проще делается, без попыток заставить императвную Java быть функциональной.
Конечно видел. Я на нем пишу каждый день, и не только на нем. :)
Тогда непонятно желание протащить это на Java. Разве что для развлечения :).
Кстати очень напомает linq в C#.
Статья ориентирована на Java программистов, которые хотели бы познакомиться с ФП. Мне кажется, что примеры на Java конструкций, имеющих аналог в функциональных языках, облегчит задачу перехода к функциональному программированию.

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

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

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

> кода столько же

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

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

хотели бы познакомиться с функциональным программированием, но не имеет возможности/желания изучать Haskell/Scala/Lisp/Python, — эта статья специально для вас.


то есть, если у вас дома нету интернета и нет желания учить фп — вот пожалуйста вам в наказание, горите в аду!!!
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории