LINUX.ORG.RU

Функциональщина. Что выбрать?


0

0

Решил в свободное время заняться изучением модного нынче функционального программирования. Встал естественный вопрос: что выбрать? Этих всяких лиспов, хацкелей, оцамлей и т.п. вагон и маленькая тележка. Чтобы не распыляться выбрал Scheme, т.к. его используют в SICP, но настораживает его не слишком большая распространённость и «академичность». С другой стороны, лямбды и прочие «вкусности» потихоньку приходят и во всякие там питоны и даже плюсы. Не холивара окаянного ради, а сугубо для просвещения и развития спрашиваю: что изучать, чтобы не лежало оно потом мёртвым грузом? У каких языков какие плюсы, минусы и области применения?

★★★★

Последнее исправление: Gvidon (всего исправлений: 1)

Ответ на: комментарий от anonymous

если функция правильно работает, то это уже не важно.

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

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

Miguel ★★★★★
()
Ответ на: комментарий от anonymous

а пруф приводить не буду

Понятно, анонимус пиздит.

речь про статическую типизацию.

поддерживать, изменять.

Именно для этого статическая типизация и предназначена.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Как будто их кто-то заставляет сразу брать и переделывать.

Нашли функцию, где нужен дополнительный аргумент?


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

то НЕТ на свете программиста, который будет часами править

код без возможности скомпилировать и запустить.



Хмм.. Однажды я подобным образом переделывал одну программу на С++, непрерывно в течении 12-и часов без возможности скомпилировать, а потом за 30-минут смог её отладить и запустить, чему был очень горд, хвалил статическую проверку типов, и вообще получил бесценный опыт. Только вот сталкиваться с таким больше не хочу.

archimag ★★★
()
Ответ на: комментарий от tailgunner

>Паттерн матчинг - это динамическая конструкция?

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

anonymous
()
Ответ на: комментарий от anonymous

>> Паттерн матчинг - это динамическая конструкция?

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

Это только в CL можно выполнять программы, которые еще не написаны.

это ведь статическая типизация, а ещё она ото всех ошибок спасает.

да ты петросян...

tailgunner ★★★★★
()
Ответ на: комментарий от archimag

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

Видимо, ты тогда был классе в пятом, не выше.

Miguel ★★★★★
()
Ответ на: комментарий от tailgunner

> Это только в CL можно выполнять программы, которые еще не написаны.

Ну сколько раз ещё нужно повторить, что в CL программ нет? Или это тяжело осилить?

archimag ★★★
()
Ответ на: комментарий от Miguel

> Видимо, ты тогда был классе в пятом, не выше.

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

archimag ★★★
()
Ответ на: комментарий от archimag

Странно, что вы с этим не сталкивались, или может просто больших программ не писали? Ну или ТЗ у вас всегда точны, а архитектор бог.

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

Грубо говоря, если интерфейсы спланированы хрен знает как и надо всё переделывать, статическая типизация позволяет хотя бы отследить, что именно нужно ещё переделать. Измени декларацию типа, и компилятор выдаст тебе в точности те места, где этот тип используется по-старому. Запускать программу ДО исправления этих мест по-любому бессмысленно: мы И ТАК знаем, что программа не будет работать правильно, а изучать корки в дебаггере - это уже last resort.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

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

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

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

anonymous
()
Ответ на: комментарий от Miguel

> Запускать программу ДО исправления этих мест по-любому бессмысленно

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

archimag ★★★
()
Ответ на: комментарий от archimag

> Ну сколько раз ещё нужно повторить, что в CL программ нет?

Это не нужно повторять вообще. По крайней мере до момента, когда ты выдашь определение термина «программа на CL».

Или это тяжело осилить?

Это слишком похоже на ложь или бред.

tailgunner ★★★★★
()
Ответ на: комментарий от Miguel

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

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

tailgunner ★★★★★
()
Ответ на: комментарий от Miguel

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

и большая часть относится к переделыванию самой системы типов в программе?

мы И ТАК знаем, что программа не будет работать правильно

как это зависит от системы типов?

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

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

anonymous
()
Ответ на: комментарий от tailgunner

> По крайней мере до момента, когда ты выдашь определение

термина «программа на CL»


Такого термина нет. В стандарте Common Lisp такого понятия, как «программа», нет. Нельзя исходный код на Common Lisp скомпилировать в программу (ну, ECL может через компилятор С/С++, но это забытая богом реализация). В Common Lisp нет различия между компилятором и программой. При запуске Common Lisp создаётся образ, его можно изменить, например, загрузкой исходного кода, а можно и иначе. Этот образ можно сохранить отдельно, но это не будет программой, это просто другой образ. Можно изменить образ и без загрузки кода, например, просто вводя код в REPL, а потом сохранить его. И делать так много раз подряд. Таким образом можно получить полнофункциональную систему без исходного кода. Система есть, а кода нет. Ага.

archimag ★★★
()
Ответ на: комментарий от tailgunner

Э.., а вы пишете без юнит-тестов? Тяжело вам однако... Их ведь не только для динамических языков исопльзуют? Советую открыть для себя это изобретение прогрессивного человечества, очень помогает, даже не сколько для отладки, сколько для нормального дизайна. Да и как часть документации очень хороши. Юнит-тесты всё равно надо писать, независимо от языка, так зачем нам тогда статическая проверка типов?

archimag ★★★
()
Ответ на: комментарий от anonymous

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

А кто сказал, что все нечистые функции одинаковы?

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

За счёт этого получается удобный параллелизм.

Miguel ★★★★★
()
Ответ на: комментарий от archimag

В Common Lisp я могу запустить код, который будет заведомо некорректный, но ведь не везде, одни части корректные, а другие нет

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

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

Именно так и происходит работа с REPL-ом, да.

Miguel ★★★★★
()
Ответ на: комментарий от anonymous

как это зависит от системы типов?

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

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

Лучше выпустить неработающую программу?

Пойми, наконец, что ошибки типов ВСЁ РАВНО придётся фиксить, хочешь ты этого или нет.

Miguel ★★★★★
()
Ответ на: комментарий от archimag

Э.., а вы пишете без юнит-тестов?

Юнит-тесты - это ручная проверка. Зачем проверять вручную то, что проверяется автоматически?

И, кстати говоря, статическая типизация очень помогает писать юнит-тесты - см. Quickcheck, для примера.

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

Unit tests are overrated. Да, это нужный инструмент - но далеко не главный.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Пойми, наконец, что ошибки типов ВСЁ РАВНО

придётся фиксить, хочешь ты этого или нет.


Угу. Вот поняли что нужны переделки. И пошли всё переделывать. Передали нахрен, запыхались. Запускаем и... вдруг понимаешь что снова не то. Не то поняли, не так сделали. Но вот беда, пока это не запустили, понять это не смогли. Толи дело в CL, можно понять, что идея дурная посреди процесса переделки и направить высвободившиеся ресурсы (нам ведь больше не надо продолжать работу по переделке) на другую идею.

archimag ★★★
()
Ответ на: комментарий от archimag

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

Какую букву в слове REPL ты не понимаешь?

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Юнит-тесты - это ручная проверка.

Зачем проверять вручную то, что проверяется автоматически?


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

archimag ★★★
()
Ответ на: комментарий от archimag

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

1) Речь шла, вообще-то, об ошибках типизации. Зачем проверять их юнит-тестами, если можно переложить работу на компилятор?

2) Вообще-то, система типов - часть идеи. И в (повторюсь) НАСТОЯЩЕЙ статической система типов - довольно значительная часть.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Какую букву в слове REPL ты не понимаешь?

Хм.. Это обязательно вести диалог в подобном ключе? Это местный обычай, без которого обсуждение за обсуждение не считается? Я на ЛОР-е недавно, много ещё не понимаю...

Я исхожу из своего многолетнего опыта работы со статической проверкой типов (не в Haskell, конечно) при разработке больших систем. Из чего исходите вы?

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

archimag ★★★
()
Ответ на: комментарий от archimag

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

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

Оставшаяся часть сообщения к делу не относится.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Речь шла, вообще-то, об ошибках типизации.

Зачем проверять их юнит-тестами, если можно

переложить работу на компилятор?



Я не знаю зачем проверять ошибки типизации. Если код работает правильно - то всё с ним хорошо. Если не правильно - то какая разница почему?

archimag ★★★
()
Ответ на: комментарий от archimag

Я не знаю зачем проверять ошибки типизации. Если код работает правильно - то всё с ним хорошо.

Если в коде есть ошибки типизации, то он гарантированно работает неправильно.

Если не правильно - то какая разница почему?

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

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

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

От этого ещё больше раздуется иерархия типов. значение и чистота - два разных свойства.

anonymous
()
Ответ на: комментарий от anonymous

От этого ещё больше раздуется иерархия типов.

Ну да, от добавления одного (одного!) типа всё мгновенно раздулось, ага.

значение и чистота - два разных свойства.

К счастью, не все столь безапелляционно уверены в неверных вещах.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Модули, как правило, иерархичны. Загрузите тот модуль, над которым

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

На остальные вам начхать.



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

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

archimag ★★★
()
Ответ на: комментарий от archimag

> В стандарте Common Lisp такого понятия, как «программа», нет.

Система есть, а кода нет. Ага.

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

tailgunner ★★★★★
()
Ответ на: комментарий от archimag

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

Можно более подробно? Я, честно говоря, не вполне понял, что имеется в виду.

Только вот на CL я могу написать первую версию очень криво, потому что она была нужна ещё вчера, а потом потихоньку доводить её до ума. А статическая проверка типов каждую кривость будет тыкать мне в морду...

Да ни фига. Если у тебя с типами фигня, то программа на CL тоже работать не будет. Если не фигня - то никто не мешает тебе писать, не указывая типы вообще. В 95% случаев никаких проблем не возникнет. Type inference, понимаш.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Гм. Ну, вообще-то, для того, чтобы пофиксить,

обычно нужно знать, почему не работает.


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

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

archimag ★★★
()
Ответ на: комментарий от Miguel

> Можно более подробно? Я, честно говоря,

не вполне понял, что имеется в виду.


Модуль, в нём функция, функция принимает другую функцию и выполняет свою работу. В реальной системе эта функцию, которая передается в модуль в качестве аргумента берется из другого модуля. Чего непонятного?

archimag ★★★
()
Ответ на: комментарий от Miguel

> Да ни фига. Если у тебя с типами фигня, то программа на CL

тоже работать не будет.


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

archimag ★★★
()
Ответ на: комментарий от archimag

> Э.., а вы пишете без юнит-тестов?

Откуда ты это взял?

Их ведь не только для динамических языков исопльзуют?

Не только. Но их изобрели смолтокеры, что как бы намекает.

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

Отличный совет. Запоздал на несколько лет, но всё равно - отличный совет.

tailgunner ★★★★★
()
Ответ на: комментарий от Miguel

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

Пойми, наконец, что ошибки типов ВСЁ РАВНО придётся фиксить, хочешь ты этого или нет.

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

большая надёжность от системы типов - это тоже весьма сомнительно.

anonymous
()
Ответ на: комментарий от archimag

исследование стэка вызов обычно почти даёт ответ почему

Понятно, серьёзных программ вы не писали.

Исследование стека даёт ответ на вопрос «где проявилась ошибка». Оно почти никогда не даёт ответа «где она возникла».

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

Гм. Вы про ошибки типизации или про ошибки вообще?

Miguel ★★★★★
()
Ответ на: комментарий от archimag

Модуль, в нём функция, функция принимает другую функцию и выполняет свою работу. В реальной системе эта функцию, которая передается в модуль в качестве аргумента берется из другого модуля. Чего непонятного?

Ну передайте туда свою собственную заглушку, проблем-то...

Miguel ★★★★★
()
Ответ на: комментарий от archimag

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

В то время как настоящая проблема - совсем в другом месте, которого в стеке уже давно нет.

Miguel ★★★★★
()

Решился учить функциональщину - учи хаскель. Сразу. Если не глуп - всё будет ок.
Только определись зачем оно тебе.

tia
()
Ответ на: комментарий от anonymous

постепенно приводить всё к новой иерархии объектов( и аналогам).

Вот почему когда говоришь «система типов» всякие анонимусы слышат «иерархия объектов»?

Учитывая незначительность доли ошибок типизации от остальных

Ещё раз: в том же Хаскеле, например, многие ошибки становятся (или могут быть сделаны) ошибками типизации. Так что доля ошибок типизации тем выше, чем сильнее система типов.

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

Ну дык переделывай систему типов, кто мешает-то?

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Исследование стека даёт ответ на вопрос «где проявилась ошибка».

Оно почти никогда не даёт ответа «где она возникла».


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

Гм. Вы про ошибки типизации или про ошибки вообще?


Вообще про ошибки.

archimag ★★★
()
Ответ на: комментарий от Miguel

> В то время как настоящая проблема - совсем в другом месте,

которого в стеке уже давно нет.


Э.. Куда делась? Как это нет в стэке?

archimag ★★★
()
Ответ на: комментарий от archimag

«где появилась ошибка» находится в верхней части стэка, «где они возникал» где-то ниже, Common Lisp показывает всю последовательность вызовов.

Все показывают. А только ошибка возникла не тогда, когда вместо строки прочитали число, а тогда, когда вместо строки ЗАПИСАЛИ число. Только система это проглотила и вышла из этого стекфрейма.

Miguel ★★★★★
()
Ответ на: комментарий от archimag

Вообще про ошибки.

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

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> а тогда, когда вместо строки ЗАПИСАЛИ число.

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

archimag ★★★
()
Ответ на: комментарий от archimag

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

В стеке не видно. А пример, разумеется, примитивный.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> В любой большой программе на любом языке ошибки -

неизбежная и достаточно значительная часть.


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

Кстати, насчёт больших программ, и много больших программ вы написали на Haskell? А то я никак не могу от наших «адептов Haskell» какого-нибудь значимого кода, который они написали, добиться

archimag ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.