Команда Typeable понимает ценность безопасности. Мы любим Haskell, но стоит ли его выбирать, если ваша цель – создание защищенного программного обеспечения? Хотелось бы сказать «да», но как и для большинства эмпирических вопросов о разработке ПО, здесь просто нет объективного доказательства, подтверждающего, что Haskell – или ещё какой-нибудь язык программирования – обеспечивает большую безопасность, чем любой другой. Нельзя сказать, что выбор языка в Typeable не имеет значения для безопасности, но какое именно значение он имеет, еще нужно подумать.
Опираясь на пятилетний опыт преподавания вводного курса по защите ПО, я могу подтвердить, что универсальной теории, на которой может строиться защита ПО, не существует. Вопросы безопасности чаще всего преподаются путем перечисления различных уязвимостей, способов их минимизации и моделей безопасности в надежде на то, что на этой основе студенты смогут получить общее понимание вопроса. Из немногих существующих теоретических работ только некоторые содержат попытки провести связь между языком программирования и аспектами безопасности.
В этой статье я собираюсь коротко описать свою любимую точку зрения на то, как выбор языка программирования можно привязать к безопасности. Этот подход подразумевает распределение различных уязвимостей по шкале от «уязвимостей предметной области» до «побочных уязвимостей».
Чисто техническая Уязвимость, относящаяся
уязвимость исключительно к предметной области
v v
?-----------?-----------?----------?
^ ^ ^
Инструментарий Инструментарий Нужно
должен исправить может помочь подумать
На оси выше показан источник различных уязвимостей программного обеспечения. На крайнем правом конце мы видим ошибки, связанные исключительно с доменной областью, то есть ошибки, совершенно не зависящие от используемого инструментария. Примером такой ошибки являются «контрольные вопросы», которые в начале 2000-х использовались многими веб-сервисами для восстановления паролей. Зачастую это были вопросы типа «Девичья фамилия вашей матери?». Позднее, примерно в 2009-2010 годах, возникло такое явление, как социальные сети, и неожиданно «девичья фамилия матери» перешла в категорию общедоступной информации. Неважно, какую технологию вы используете для реализации такой схемы с «контрольными вопросами». Эта схема все равно не работает.
На крайнем левом конце шкалы у нас представлены ошибки, имеющие чисто техническую причину. Они абсолютно не зависят от области задачи. Хорошим примером такой проблемы является печально известное переполнение буфера. Совершенно неважно, что хранится в буфере – если буфер переполнился, злоумышленник получает возможность беспрепятственно вмешаться в работу поддерживающих структур вашей программы. В данной ситуации переполнения буфера можно избежать, по крайней мере, теоретически, используя набор инструментальных средств, в котором отсутствует неконтролируемая запись в буфер.
Между этими крайними точками шкалы находится уязвимость, не вызванная ни чисто технической причиной, ни проблемами домена. Стандартный пример такой уязвимости обычно можно найти в сервисах, обеспечивающих выгрузку файлов.
В таких сервисах обычно есть соблазн записать файл пользователя непосредственно в файловую систему сервера. Однако под каким именем файла? Использовать непосредственно имя файла пользователя – верный путь к катастрофе, так как оно может выглядеть как ../../../etc/nginx/nginx.conf
, ../../../etc/passwd/
или любые другие файлы, к которым сервер имеет доступ, но не должен их изменять.
Эта опасность сочетает в себе технический и доменный аспекты уязвимости. Маловероятно, что какой-нибудь набор инструментальных средств сможет предложить готовое решение для этой проблемы, однако легко увидеть, что одни инструменты могли бы обеспечить лучший контроль такого проблемного поведения, чем другие.
Использование шкалы
Польза этой шкалы заключается в оценке вашего инструментария, в том числе языка программирования и платформ. Сколько чисто технических проблем ваш инструментарий решает самостоятельно? Какую часть шкалы он закрывает, предлагая дополнительные возможности для устранения ошибок, которые приводят к уязвимостям?
В идеале современный инструментарий должен практически полностью устранять чисто технические уязвимости. Например, большинство современных языков, таких как Haskell, C# и Java, по большей части обеспечивают защиту содержимого памяти и в целом предотвращают переполнение буфера, попытки дважды освободить одну и ту же ячейку, а также другие технические проблемы. Однако от правильного инструментария можно получить еще больше пользы. Например, легко представить себе систему, в которой имеется техническая возможность разделить абсолютный и относительный пути к файлу, что упрощает контроль атак с обходом каталога (path traversal), таких как загрузка пользователем файла поверх какого-нибудь важного конфигурационного файла системы.
Haskell – нижняя часть шкалы
Как и большинство современных языков, Haskell хорошо работает с техническими уязвимостями низкого уровня. Во-первых, Haskell обеспечивает безопасный доступ к памяти, что делает огромный пласт возможных уязвимостей, особенно связанных с массивами данных и переполнением буфера, недосягаемым для потенциальных злоумышленников. Во-вторых, Haskell имеет статическую типизацию, что также защищает от целого семейства ошибок, например, от известных манипуляций с типами в PHP:
// From imaginary CSRF token protection:
if ($tokenHash == $hashFromInternet->{'tokenHash'}) {
echo "200 OK - Request accepted", PHP_EOL;
}
else {
echo "403 DENIED - Bad CSRF token", PHP_EOL;
};
Видите, в чем здесь проблема? В большинстве динамических языков, таких как PHP, «тип» записи JSON определяется в процессе выполнения и зачастую основывается на структуре входных данных. Кроме того, в объектно-ориентированном программировании «тип» используется для выбора поведения с помощью динамической диспетчеризации, что фактически позволяет злоумышленнику выбирать, какой код будет исполнен. Наконец, в языке PHP равенство, выраженное через ==
, зависит от типа вводимых данных, и в приведенном выше примере атакующий может полностью обойти защиту.
Аналогичная проблема возникла с Java (и другим языками, см. https://frohoff.github.io/appseccali-marshalling-pickles/). Java предложил исключительно удобный способ сериализации любого объекта на диск и восстановления этого объекта в исходной форме. Единственной досадной проблемой стало отсутствие способа сказать, какой объект вы ждете! Это позволяет злоумышленникам пересылать вам объекты, которые – после десериализации в вашей программе – превращаются во вредоносный код, сеющий разрушения и крадущий данные.
Это не значит, что вы не можете создать безопасный код на PHP или не можете получить такие же ошибки в Haskell, однако по своей природе Haskell не склонен к таким уязвимостям. Если переложить приведенный выше пример кода на Haskell, он будет выглядеть примерно так:
data Request = Request {csrfToken :: Token, ... other fields}
doSomething :: Session -> Request -> Handler ()
doSomething session request
| csrfToken session == csrfToken request = ... do something
| otherwise = throwM BadCsrfTokenError
В этом случае возможность манипуляций с типами устраняется с помощью стандартного приема – путем присвоения интерфейсным типам конкретного, известного еще до исполнения программы типа.
Haskell – середина шкалы
Если рассматривать среднюю часть шкалы между «техническими» и «доменными» уязвимостями, Haskell демонстрирует возможности, которые, на мой взгляд, определяют преимущества от его выбора.
Прежде всего, в Haskell имеется возможность моделировать данные более точно по сравнению с такими языками, как как C, Javascript или даже Java. В основном это обусловлено удобством его синтаксиса и наличием типов-сумм. Точное моделирование данных имеет значение для безопасности, поскольку код домена в основном представляет собой модель некоторого реального явления. Чем меньше ее точность, тем больше возможностей имеют злоумышленники.
Наличие инструментов точного моделирования дает программистам возможность обходить ловушки в домене. Например, рассмотрим простую возможность легко выразить одной строчкой идею о том, что, скажем, номер страхового свидетельства неизвестен, либо скрыт, либо содержит указанное пользователем значение:
data SSN = Unknown | Redacted | SSN Text
А теперь сравним моделирование той же идеи с использованием строковых величин ""
, "<REDACTED>"
и "191091C211A"
. Что произойдет, если пользователь введет "<REDACTED>" в поле ввода SSN? Может ли это в дальнейшем привести к проблеме? В Haskell об этом можно не беспокоиться.
Аналогичные приемы могут помочь программистам повысить безопасность повсеместно. Продолжая предыдущий пример безопасного хранения пользовательских файлов на сервере – если ваша функция сохранения пользовательских загрузок начинается с
storeFileUpload :: Path Abs File -> ByteString -> IO ()
storeFileUpload path = ...
вероятность того, что вы создадите ситуацию, при которой пользователи могут сделать запись поверх ваших системных файлов, значительно снижается. Этот код скомпилируется, если только отсутствует практическая возможность, при которой путь к файлу содержит атаку с обходом каталога. Точно так же если после того, как пользователь потерпел неудачу при входе в систему, пользовательские данные просто недоступны для программы, или если вы просто не можете внедрить данные непроверенного пользователя в страницы HTML, вероятность ошибки снижается.
Я не утверждаю, что нельзя использовать другие языки для написания безопасного кода, и даже не говорю, что Haskell автоматически делает ваш код более безопасным. Я только считаю, что в Haskell имеются очень удобные инструменты, которые можно использовать для усиления защиты.
Haskell и ошибки домена
Чисто доменные ошибки выше описывались как ошибки, не зависящие от используемых инструментов. Это не совсем верно. Инструменты не выбираются случайным образом. Сообщества, объединяющие единомышленников, зачастую образуются вокруг различных инструментов. При этом такие сообщества могут иметь несхожие взгляды на безопасность.
В связи с этим в пользу Haskell говорит тот факт, что Haskell невозможно хорошо изучить случайным образом. На сегодняшний день Haskell остается достаточно редкой технологией, которая преподается даже не во всех университетах и входит не во все учебные планы. То есть если человек хорошо владеет Haskell, имеются основания предполагать, что он также имеет навык работы с формальными системами или интерес к компьютерным наукам. И хотя это не гарантирует, что программисты, работающие с Haskell, кое-что знают о безопасности, можно предположить, что при необходимости они смогут быстро разобраться в этом вопросе.
Однако это все догадки. Сообщество Haskell до сих пор достаточно мало, чтобы не быть объектом атак, а специалисты по Haskell в общем случае еще не так сильно озабочены проблемами безопасности, как разработчики на Javascript или Python.
Заключение
Конечно, Haskell имеет свои недостатки, и я не утверждаю, что другие языки не могут похвастаться такими же достоинствами. А в некоторых случаях, например, в случае атак по времени и прочих атак по сторонним каналам, другие инструменты могут обеспечить даже лучшую защиту. Кроме того, некоторые языковые сообщества в большей степени ориентированы на безопасность, чем Haskell. Но лично я считаю, что по сравнению с существующими универсальными языками программирования, доступными для выбора, Haskell предлагает очень хороший набор функций для разработки безопасного программного обеспечения.
koldyr
Прекрасно.
А работа есть? Кругом джава, Котлин, плюсы, питон, го ...
eyudkin
То, что на хаскелле нет работы, а на пехапе, питоне и прочем её очень много, как раз многое говорит о человечестве ;)
merhalak
Ровным счётом ничего не говорит.
GospodinKolhoznik
Говорит то, что на самом деле всех устраивает пользоваться забагованным по и гнаться за качеством (в смысле уменьшения количества багов) особо никому не надо.
С другой стороны если глючит веб интерфейс то и черт бы с ним. А такие системы, где баги приводят к критическим последствиям мало кто пишет. Ну разве что в банковской сфере, борт компьютеры транспортных средств и ПО для электростанций.
vkni
Кроме того, ещё говорит о том, что время программистов стоит слишком мало. Одно шуточное высказывание гласит, что у нас вся индустрия - "это борьба с ошибками, чем раньше их найдёшь, тем меньше потери".
Соответственно, если индустрия может сжигать время программистов в написании тестов там, где можно обойтись компилятором, это означает, что программисты продают своё время слишком дёшево. Тесты нужны, но они затратнее расстановки аннотаций типов => должны быть на следующем уровне воронки проверок.
0xd34df00d
Только программистов, которые хотя бы слышали там что-то про какого-то Карри-Говарда, сильно меньше, чем готовых радостно на скрам-митинге отчитываться про покрытие тестами.
vkni
Это всё идёт, кмк, от экономики (отчуждённый творческий труд, недооплаченность остальных профессий и т.д., и т.п.). Но тут мы — крайние оппоненты, да и может быть это лично мои задвиги.
0xd34df00d
Я не уверен, что отчуждение труда и вот это вот всё тут как-то играет роль (равно как и отсутствие полной свободы договора — это говоря о моих задвигах). Иначе, наверное, чуть больше людей занималось бы этим всем по вечерам и выходным, где никто ничего не отчуждает (да и не особо ограничивает).
vkni
Так времени нет — дети, семьи и т.д. Ну и вообще, если работа выжимает, то сил на выходных не остаётся.
sshikov
В принципе на проект хватило бы одного-двух. Но да, и столько сложно найти.
tzlom
Тут скорее проблема что на одного понимающего Хаскел будет 100 вообще не въезжающего в него. А продукт делать надо, у бизнеса есть деньги и он готов попробовать сделать ребенка за месяц.
nullc0de
ну да, ну да, такой замечательный язык, а всеволишь около 30е место в рейтинге языков программирования и на hh даже вакансий почти нет, и большая часть ваканский не про сам хаскалль, и приписка что хорошо будет если знаете хаскелль и видимо им даже не пользуются и приписали к вакансиям для вида… Главная проблема языка, что контекст его применения очень ограничено и это функциональный язык. Его можно сравнить с R, который рассчитан только на анализ данных, и что-то серьезное писать уже не удобно. Когда говорят про хаскелль, у меня лично ассоциация с плохим программистом, C++, Java, C, JS, C# не осилил, где-то краем уха услышал про хаскелль и его безопасность, про плохой сборщик мусора и сразу начал гнуть пальцы, какой замечательный хаскелль и он программист… В принципе в разговоре с хаскеллистами нет смысла даже начинать разговор про работу сборщика мусора, про алгоритмы, и т.д. Они зациклены на своих стереотипах, и мифах… В принипе язык не плохой, но вокруг языка сформировалась именно такая атмосфера. А еще хаскеллисты настолько профессионалы, что вместо аргументов начинают сразу минусовать, видимо их сильно обижает, что не оценили их профессионализма…
koldyr
В принципе при разговоре с импиративщиками нет смысла даже начинать разговор за категории, функторы, естественные преобразования, линзы, монады и т.д. они зациклены на своих стереотипах и шаблонах проектирования.
Просто порог входа повыше и не учат почти нигде. Абстрактная алгебра в массе мало кому даётся на уровне понимания.
nullc0de
Хорошо, если говорите про алгебру отвечу вам вашим же языком, у вас же там все хаскеллисты доктора математических наук. В основе эффективных алгоритмов лежит всегда какая-то эффективная математическая модель. Покажите мне реализацию на хаскелль какого нибудь уникального и эффективного алгоритма.
GospodinKolhoznik
За эффективностьб алгоритмов надо обращаться к старым добрым Си или даже к Асм. Хаскель это про другое. Про то, что компилятор берет на себя максимум выявления всех возможных потенциальных ошибок.
nullc0de
Для этого в других языках есть статический анализатор…
GospodinKolhoznik
И это прекрасно. Будущее наступило, и все языки стали совершенными (даже Бэйсик), благодаря наличию синтаксического анализатора.
Кстати, синтаксический анализатор умеет отследить, что программист по ошибке сложил целое число, которое обозначает количество долларов на счету с другим целым числом, которое показывает сколько на счету евро? Компилятор Хаскеля умеет отслеживать такого рода ошибки (и это далеко не предел). Но это вовсе не значит, что только Хаскель хороший, а все остальные языки отстой. Просто у него действительно есть как сильные, так и слабые стороны и глупо это отрицать.
nullc0de
Да, если им создать разный тип данных. Но чтобы такого не было, есть юнит тесты. Посмотрите исходники баз данных и как там тестируются транзакционные модели)))
FenestramDeveloper
Только-только начал изучать хаскель, а уже умудрился из-за спешки наделать ошибок: может быть сложение долларов с евро он может отследить, а сложение вместо вычитания пропустил (оба значения были в метрах, результат тоже). На такой случай тоже есть какой-то приём или всё-таки каким бы безопасным ни был язык, программист всегда сможет затупить так, чтобы написать нерабочий код и не заметить?
0xd34df00d
void*
. Сравните — плюсовая сортировка в четыре раза быстрее сишной! Я уж не говорю о средствах для метапрограммирования.Inspector-Due
По поводу второго пункта: будет ли эффективным парсер того же самого zip (да вообще любых файлов, где находится маленькая файловая система; соответственно, чтобы прочитать один файл в это мини-файловой системе не надо читать весь файл (в котором находится эта fs), надо использовать
fseek
и прочитать содержимое)? И не скатится ли всё к императивному коду?Проблема ещё в библиотеках. К примеру, по дефолту нельзя в
binary
выбрать, какой будет порядок байтов. По умолчаниюBinary
тайпкласс реализован для примитивов так, что порядок байт --- big endian. И как с этим быть?0xd34df00d
Парсер zip я не писал ни разу, но в самом общем случае
BSL.take n . BSL.drop s
дляimport qualified Data.ByteString.Lazy as BSL
эквивалентноfseek(s)
+fread(n)
. Когда мне нужно что-то взять из более простых форматов откуда-то из середины файла, я ровно это и пишу.Даже если скатится, покуда это изолированная императивность а-ля
ST
, то ничего страшного.Бесстыдно пропиарюсь.
Inspector-Due
Я пробовал делать так, но это не работало, если не было
-O2
. В любом случае до этого я использовалhGet
иhSeek
. И всё же это облегчает работу, потому что теперь не придётся тащить вездеIO
. Заметил, что если два раза попытаться сделать так, как вы сделали, то начинает читаться файл (может, не весь)Можно с вами связаться? У меня есть пару вопросов.
0xd34df00d
Да, такая ленивость хорошо работает только с однопроходными алгоритмами (и это общий принцип — с ленивыми списками и тому подобным аналогичные проблемы).
Библиотеки вроде pipes или conduit это чинят, но я ими никогда не пользовался (у меня почти всегда либо вход большой, но алгоритм однопроходный, либо алгоритм многопроходный, но вход маленький).
Можно тут в комментах, можно в личке, можно там в ишшуезах (если это по теме библиотеки). Можно в джаббере, но кто им сейчас пользуется.
А это не вы там про парсинг FAT спрашивали, кстати? А то я письмо видел, но руки не дошли вчитаться в код.
Inspector-Due
Я немного почитал код библиотеки zip, там используется
conduit
, а получение первичной информации — информация о самом архиве и файлах, которые в нём есть — вообще происходит так (те самыеhSeek
+hGet
). Так что, как я понимаю,binary-generic-combinators
не особо поможет. Конечно, для того, чтобы спарсить заголовок, может и подойдёт, но всё равно придётся использовать библиотеку наподобиеconduit
.Я в личку вам написал свой адрес.
Да, я :\
Удалил сообщение, потому что понял, что не по теме вообще.
csl
Касательно джаббер, я — пользователь conference.jabber.ru, например…
warlock13
Самый правильный способ — использовать sourceFileRange/sourceHandleRange. А LazyIO оказалось неработоспособной концепцией.
vkni
Вообще говоря, SML - это стандартный учебный язык. "Введение в стандартный ML" - это жалкие 100 страниц, в которых ядро языка занимает всего лишь 40.
Линзы, категории в программировании и т.д. появились относительно недавно (20 лет для учебного процесса - это ничего).
vshabanov
Вы какую-то уж совсем ерунду пишете.
Хаскелл — язык общего назначения. Ограничен разве что наличием библиотек для предметной области (GUI для Windows на нём делать, наверное, не очень удобно), ну и embedded или hard realtime на нём не получится (и то, возможны варианты: я как-то писал небольшую прошивку на Си, а команды ей отправлял из Хаскелла).
Функциональный язык — не проблема, а, наоборот, преимущество.
Интересная ассоциация. Раньше было наоборот: программисты на императивных языках не могли осилить Хаскелл (и так же его ругали, как и вы). Возможно, пришло время, что Хаскелл стал настолько популярен, что кто-то стал учить его как первый язык и не понимать, как работают императивные языки, но что-то я очень сильно в этом сомневаюсь (как и в том, что после хаскелла будет трудно понять императивные языки, скорее будет лучше видно, насколько они неудобны и многословны для большинства задач).
Настройка сборщика — достаточно распространенное занятие, т.к. это дешевый способ немного ускорить работу программы (настройки по-умолчанию весьма консервативны, хотя их вроде планировали подкрутить в новых версиях GHC), а также ограничить общий объем памяти (утечки из-за невычисленных thunk-ов всё же случаются), дабы в swap не ушло.
Многие алгоритмы в функциональных языках отличаются от императивных. Так что, может быть, ваши алгоритмы просто не подходят?
Хотя императивно в Хаскелле тоже можно (в IO или ST), но это не всегда эффективно, зачастую, как раз из-за сборщика мусора, для которого отслеживание измененных ссылок — головная боль (и в GHC оно, за ненадобностью, не так оптимизировано, как в императивных языках). Хотя есть и unboxed vectors (без нагрузки на сборщик). Можно совсем как в Си писать.
Так что не стесняйтесь — говорите про сборщик и алгоритмы. Мы про это любим.
Не стоит всех хаскеллистов стричь под одну гребенку. Многие из них пришли к Хаскеллу как раз после того, как осилили C++, Java, C, JS, C#, и вполне понимают, о чём они говорят.
За этот бредовый наезд (который мог бы показаться жирным троллингом, да боюсь, что вы серьёзно так думаете) вас и заминусовали.
И подтяните русский язык. Если человек не умеет грамотно писать на своём родном языке, то и на языке программирования грамотно писать не получится.
nullc0de
ЛОЛ. Вы какую-то ерунду пишите. Вы всегда так себе противоречите в тексте, сами же сначала пишете, что его ограниченная функциональность никак не мешает, а дальше сами же противоречите себе и приводите контраргументы своим словам…
А кто писал, что это проблема? Уже придумываете на ходу чего не было написано? Нет там никакой проблемы. Перечитайте внимательное, речь была про ограниченный контекст применения языка. Вы сами же подтвердили мои слова.
Статистика популярности и вакансий говорит об обратном. Язык появился в 1990 и за это время только отстает в развитии от других, за это время можно было кучу функционала и библиотек написать. Но ничего этого нет и поэтому его контекст применения очень ограничен, поэтому и низкая популярность. Если бы язык действительно решал эффективно какие-то задачи, то был бы в то 10 языков программирования, но он топчется в около 30 месте.
Так покажите пример крутого алгоритма на хаскелль. Никто не опроверг моих слов и не показал ни одного примера.
Кончились аргументы, докопались до орфографии? Только на это хаскеллисты способны? Сестра где примеры? Где пруфы?
FenestramDeveloper
«Так покажите пример крутого алгоритма на хаскелль.» — дело в том, что в хаскель пишут не алгоритмы, а что-то типа определений. Приведу пример «относительно крутого определния» из википедии (специализируюсь на си, хаскель пока еле-еле читаю):
foldr — это свёртка списка. Работает так (от b->e или наоборот неуверен):
foldr (fun) a (b,c,d,e) раскрывается в fun(b, fun(c, fun(d, fun (e, a))))
в данном случае fun — это лямбда выражение с двумя аргументами, где p — очередное значение из primeNums, а r — значение с предыдущей иттерации.
Начальное значение — это True, а само выражение лямбды делает следующее:
(\p r-> p*p>n || (rem n p /= 0 && r))
если проверяемое число слишком больше, то автоматом возвращает true, иначе: если на предыдущей иттерации был false, то сразу возвращаем false (ленивые вычисления), и только в противном случае делим и ся результатом.
Понятно, что очень большая нагрузка на оптимизатор: он должен понять, что primeNums возрастающая последовательно, что p*p возрастает по мере возрастания аргумента итд… но если всё это сработает, то получится невероятно оптимизированный алгоритм: для проверки простоты числа N будут проводиться деления только на простые сомножители менее sqrt(N).
Если в чём-то ошибся, надеюсь, более знающие поправят.
0xd34df00d
И какие прорывные фичи упустили?
Куча функционала и библиотек соответственно.
Основная проблема хаскеля в том, что лет 15-20 назад вокруг него по не очень понятным мне причинам создался ореол, что без знания теории категорий и PhD в математике там никуда. Вот и следствия.
Чтобы понять ваш бейзлайн, можете привести пример крутого алгоритма на любом другом языке?
vshabanov
Почитайте определения, чем отличается ЯП общего назначения, от специализированного языка. ЯП общего назначения подходит для большого круга задач, но это вовсе не значит, что для всех задач он подходит одинаково хорошо. Вы некорректно сравнивали Хаскелл с R, языком куда более специализированным, на что я вам и указал.
Вы писали: "Главная проблема языка, что контекст его применения очень ограничено и это функциональный язык"
Она как раз подтверждает то, что программисты на популярных языках не осилили Хаскелл, а не наоборот.
Наоборот, передовые фичи из Хаскелла переходят в другие языки.
Hackage весьма большой.
На Хаскелл очень эффективно писать сложную обработку данных (компиляторы, например), а это небольшой процент задач. Так же на Хаскелле более эффективно писать и изменять большие программы, но чтобы это понять, надо иметь опыт написания таких программ. Да и мелкие программы (если это не совсем однострочники, где подойдет bash/awk/sed/perl) тоже удобнее писать просто за счет компактного синтаксиса, pattern matching и проверки типов. Но чтобы это понять, надо знать Хаскелл.
Основная проблема в том, что Хаскелл отличается от обычных императивных языков и требует определённого сдвига парадигмы. Учить императивные python, Java, C#, Go, Swift или JS после Си гораздо проще, чем Хаскелл.
Еще одна проблема — legacy. Если проект написан на Java/C++/JS, то внедрить Хаскелл (да и любой другой язык) будет проблематично. По-этому, чаще всего Хаскелл используют во вспомогательных утилитах, или в новых проектах.
Так же, есть проталкиваемые большими корпорациями языки типа Java, Go, Swift, рассчитанные на среднего программиста (обученного императивным языкам) и большие коллективы. Или Си, который приходилось использовать, т.к. на нем написана ОС и больше ничего толком нет. Или JavaScript, кроме которого в браузере ничего не работало.
И не будем забывать про производительность — хорошо оптимизировать программы на функциональных языках научились не так давно, а снижение производительности в несколько раз по сравнению с Си на старых машинах часто было недопустимо.
Так что нет ничего удивительного в том, что Хаскелл не в первой десятке.
Функциональные языки — это, вообще, кузница уникальных алгоритмов.
Избитый пример — балансировка красно-черного дерева, которая на Хаскелл занимает в 10 раз меньше строк, чем на Java.
Еще посмотрите Functional pearls, там много всего:
https://wiki.haskell.org/Research_papers/Functional_pearls
Или почитайте книгу Окасаки "Чисто функциональные структуры данных". Её даже на русский перевели.
Учитесь вести цивилизованную дискуссию без огульных обвинений и агрессивных выпадов. Тут вроде не анонимная имиджборда.
chemistmail
Есть, буквально недавно предложение пришло, резюме скрыто последних полтора года.
На вопрос «откуда», нашли в битбакете, а я уж и сам забыл когда им пользовался последний раз.
Yuuri
Мало, но есть. Интересующихся приглашаем в телеграм-чатик #haskell_jobs, куда периодически что-то сыпется.
CrazyOpossum
К сожалению, я не нашёл мощной библиотеки работы с путями — пришлось самому делать
Естественно, в прототипе не было ничего такого, просто импорт библиотеки filepath, которая пытается разбивать строки по слэшам и догадываться, что путь, а что имя файла — ну так и для php должны быть либы с эквивалентым функционалом. Биндинги к windows api/posix тоже приходилось писать на hsc2hs — никакой защиты указателей там нет, потому что C — а что делать? Я к тому, что Хаскелл, конечно, подталкивает к безопасности, но не нужно переоценивать этот эффект.
В первой половине статьи как раз упоминается, что ошибки домена не зависят от технологии. А Хаскелл (Линукс/I2P/Мой-суперкрутой-шифр) — безопасен, потому что Неуловимый Джо — это вообще не аргумент.
nullc0de
Все аргументы за хаскелль сводятся к одному, что если собаке пришить 5ю ногу, и когда она будет бежать споткнется, то будет еще одна нога которая поможет. Самое главное люди никогда не слышали про MISRA, авиа и военные стандарты написания кода, и создание отказоустойчивого оборудования, что есть анализаторы проверяющие код на ошибки и соответствие стандартам. Как говорил выше смысла с ними нет даже спорить, о моделях проектирования они не знают… Зато проблемы вида сложить долларовый и евро счета высасывают из пальца, а потом обижаются…
CrazyOpossum
Не все.
Yaris
Ну вот я слышал про MISRA, и даже частично приходилось пользоваться. И Haskell немножко трогал. И исходя из своего опыта я бы предпочёл Haskell, если бы был выбор. Потому что всякие анализаторы и стандарты — это круто, но из С при этом получается такой обрубок, что пользоваться невозможно. Ada (к вопросу о военных стандартах) на вид неплоха, но уж очень многословно всё.
GospodinKolhoznik
Про евро и доллар я из пальца высосал. Но я не обижался.
Минусанул вас кто-то другой, и скорее всего по инерции. Вы же пришли в пост про хаскель и зачем то стали унижать хаскелистов, что мол они даже язык Си не смогли осилить, и поэтому на хаскель переключились. Что мало того, что грубо, так ещё и абсурдно. Скорее всего поэтому и заминусовали.
Да и вообще, откуда у вас такая неприязнь к этому языку? Ну есть такой язык, какой то очень небольшой процент людей на нём пишет. Очень часто just for fun, ибо вакансий чрезвычайно мало. И кому этот язык и эти люди мешают?
nullc0de
Неприязнь пришла от самих хаскеллистов. Выше писал, что сами же они сформировали такую атмосферу вокруг себя. Что они не признают своих ошибок, и языки другие до конца не изучили, это сразу бросается в глаза и сами же предвзято относятся к другим языкам, и самое главное сравнивают свой хаскелль с языками с которыми в принципе нельзя сравнить. Хаскеллисты мне напоминают инфантильного ребенка. Есть такие дети, у которых родители все за детей делают, им ничего не надо думать и делать, им шнурки завяжут, сопельки и попу подотрут, дадут денег на карманные расходы, а потом вырастают совсем беспомощными и на практике уж ничего не могут делать сами, а потом начинают вокруг обвинят других в надуманных ими же проблемах… Что в прочем они очередной раз показывают своими минусами… Если быть реалистами, рынок труда и рейтинг языков программирования всех расставил по заслуженным местам…
FenestramDeveloper
Не так уж и из пальца.
В 1999 году… NASA… В ходе расследования… выяснилось, что часть проектировщиков в качестве единицы измерения силы использовала ньютон, а часть — «фунт-силу». Это повлекло за собой ошибку в управлении аппаратами и их гибель.
Я думал, вы так сделали отсылку к тому инциденту.
GospodinKolhoznik
Про евро и доллары я на память процитировал слова Richard Eisenberg — одного из ключевых разработчиков dependent types в современном хаскеле. Он и более сложные примеры приводил, такие как тип списка, который гарантирует на этапе компиляции, что список всегда будет отсортирован.
Но я сам ещё не знаю, как реализовать такую вещь с отсортированным списком и если бы речь зашла о том, что «а ну ка покажи на примере как это делается», то я бы слился. Поэтому я привёл более простой пример (который знаю как сделать).
0xd34df00d
Мне больше нравится разделение представления списка и предикатов на нём (это как-то более хорошо композируется, да и компилятору проще вырезать ненужное), и предикат упорядоченности не то чтобы очень сложный: тут значение типа
OrderedList f xs
означает, что списокxs
отсортирован согласно компараторуf
. Там просто рассматриваются три случая:x? : x? : xs
сортирован, если x? меньше x? согласно компараторуf
, и хвост списка (то есть,x? : xs
) сортирован.Интересно, что здесь ничего о свойствах
f
не говорится. Но, например, если известно, чтоf
— транзитивно, то можно показать, что из этого определения следует, что x_i ? x_j (согласноf
) для i ? j.Вот понятие перестановки уже не столь интуитивно (а оно нужно, чтобы сформулировать требования к функции сортировки, корректность которой я там доказывал — её результат должен быть не только сортированным, но и являться перестановкой исходного списка).
0xd34df00d
И сразу кровь, боль, страдание.
А, впрочем, зачем выносить, ээ, компоненты путей (это же они, да?) на уровень типов?
Но технология позволяет выявлять внутреннюю неконсистентность реализации.
Я уже сильно больше десятка раз находил ерунду в своей формализации одной системы типов только потому, что пытался доказать её свойства на агде.
CrazyOpossum
Пути не произвольные, там дальше и функции на компонентах есть. Ну и кроме того, очень хотелось синглтоны пощупать, и больное место нашлось.
За агду не скажу, но на хаскелле решал задачу синглтрнами (другую, не с путями) — 5 классов состояний, некоторые комбинации невозможны, некоторые достижимы только из определённых. В целом, мне понравилось — выкинул кучу тестов, потому что тестировать стало нечего, но это всё ещё далеко от математической истины. Наверное, на этих ваших агдах/идрисах/коках можно добиться полного доказательства корректности, но мы же знаем, что дальше хаскелля жизни нет — ни экосистемы, ни сообщества.
amarao
Очень интересно, что предотвращает больше ошибок — заумные семигрупоиды в Хаскеле или borrow checker в Rust.
qw1
borrow checker появился прежде всего потому, чтобы иметь безопасный язык без GC.
Есть GC — и вся возня с лайфтаймами по большей части не важна.
0xd34df00d
На самом деле важна, потому что память — не единственный ресурс. И вот тогда
семигруппоидымонадические регионы и прочий параметрический полиморфизм второго ранга вполне себе помогают.vkni
Вопрос, кстати — начиная с какого количества тактов есть смысл в Хаскеле создавать очередную «зелёную нитку»? Есть мысль (не моя), что можно считать от кеша — то есть, обрабатываемые данные должны быть больше, чем cache line. В общем, хотелось бы наработать какую-то интуицию на эту тему — уже ведь даже в процессорах AMD по 128 ядер обещают, а писать на больших машинах с общей памятью эффективно народ умеет лишь на Make. ;-)
0xd34df00d
Универсальный ответ — бенчмаркать надо.
Моя стандартная интуиция для случая параллельной обработки (ну там
parMap
всякие) — чтобы ниток было раз в 100-1000 больше, чем у меня ядер. Их тогда и не сильно много, и возможная неравномерность нагрузки в разных чанках выравнивается.Либо можно просто брать repa или accelerate и не думать.
Это да. Сильно больше :]
Смотря что мы обсуждаем — параллельную детерминистичную обработку (ну, в смысле, один
map
сделать на куче ядер сразу) или конкурентную работу (ну там,forkIO
,async
, вот это всё, с принципиально разной логикой в разных потоках выполнения). Первое на самом деле просто, второе, если в примитивы какого-нибудь STM запихивается — тоже.vkni
Как меня учили — прежде чем что-то измерять, надо примерно знать, что получишь. ;-) Так что большое спасибо за интуицию.
Ну я конкретно обсуждаю определённый класс машин — это типа как последние СПАРКи с полутерабайтом ОЗУ и сотней-другой потоков выполнения. Я не видел систем, кроме make, которые могут легко использовать эти машины так, что действительно появляется их приемущество перед табуном 4-ядерных Linux машин.
То есть, грубо говоря, машина есть, а полностью использовать её достоинства: почти видеокартное кол-во ядер, большая общая память, никто не может — те же задачи можно гонять на большом кол-ве маленьких машинок.
Просто эти монстры уже скоро будут доступны широкой публики => надо как-то уже уметь легко параллелить «обычные» задачи на дикое кол-во потоков. То есть, например, параллельный парсер CSV — у нас же подгрузка данных с NVMe тоже параллельная.
tzlom
Это на курсах по P-fishing что-ли? Измерено значит измерено, дальше вопрос можем ли понять что именно было измерено и с какой погрешностью.
amarao
Вот чем больше я знакомлюсь с Rust, тем меньше я считаю, что главная задача borrow checker — это управление мусором.
На самом деле концепция ownership позволяет контролировать coupling между посистемами. Если мы даём reference, то мы обязаны заботиться о нём. Это не обязательно память. Это может быть открытый fd, принятый http запрос и т.д.
Если мы отдаём ownership, то мы перестаём думать про этот объект. Например, диспетчер http-запросов будет раскидывать запросы в воркеров с передачей владения, если он это сделает через reference'ы, то ему придётся за ними следить. Это будет очень сложно, архитектурно.
Система borrow checker не улучшает ситуацию, она просто выводит архитектурные ошибки на уровень компиляции. Например, передать reference в соседний тред почти никогда не возможно (и так делать не надо). Синтаксис Rust запрещает это, остальные языки не могут ничего сказать про эту проблему.
vkni
К сожалению, когда защита слишком неудобная, её обходят. Я видел, что вместо передачи объекта во внутреннюю функцию (которая просто перепаковывает его и пересылает по сети), его клонируют, и передают клон.
Видимо это и есть корень нареканий на borrow checker — он слишком поздно показывает проблемы. Ведь на этапе компиляции часто слишком поздно исправлять архитектурные ошибки. Особенно, если архитектура уже где-то используется.
Спасибо!
qw1
vkni
Как всегда, две стороны:
1. Да! И об этом ему надо рассказать, открыть глаза! :-)
2. К сожалению, часто одним приходится платить за ошибки других — архитектура может быть задана заранее. :-(
amarao
В тот момент, когда bc вам мешает, и вы значение клонируете, то есть вероятность, что это разумное решение для обеспечения разумности поведения. Т.е. (за вычетом проблем с disjoint borrows у структур) bc просто запрещает плохие действия. Иногда не очевидно, где это укусит, но бывает так, что потом становится понятно — что действительно, bc был прав.