С тех пор, как я начал рассказывать компьютерам, что делать (также известный как программирование), я выучил несколько языков. Некоторые из этих языков мне не понравились (C ++ с Boost), некоторые я «в середине» с (Perl), а некоторые мне очень понравились (Ruby, Python, Haskell, возможно, JS).
На первый взгляд, Руби и Хаскелл кажутся невероятно далеко друг от друга, почти не имея сходства.
Ruby является императивным языком (хотя и с некоторыми встроенными в него функциональными элементами), но в нем нет каррирования (одной из основных концепций Haskell) и не предусмотрено никаких положений для оптимизации хвостовых вызовов, что делает рекурсию неосторожным выбором во многих случаях. Кроме того, это очень практичный язык — скорость ставится за эффективность программиста. Он был популяризирован Ruby on Rails, который, будучи веб-фреймворком, вероятно, настолько практичен, насколько это возможно.
Haskell, с другой стороны, является чисто функциональным языком (если вы не знаете, что это значит, продолжайте читать). Функции абсолютно необходимы для написания языка и являются первоклассными объектами (подробнее об этом позже). Язык не сразу практичен — вам нужно немало почитать, прежде чем вы сможете, скажем, создать небольшой HTTP-сервер (на самом деле это довольно просто в Ruby с Celluloid) .
Если немного углубиться в изучение обоих языков (я полагаю, что вы, ребята, грамотны в Ruby; если нет, то это намного проще, чем в Haskell :)), мы начинаем видеть очень интересные сходства.
Функциональная парадигма
Традиционное или императивное программирование работает как приготовление пищи. Вы приказываете компьютеру сделать это, затем делаете это, и затем другое. Функциональное программирование работает совсем по-другому.
Руби — это императивный язык. Это может быть не сразу очевидно (если вы не задумывались об этом), но написание кода на Ruby читается как набор указаний для достижения какой-то цели.
При попытке написать эти указания происходит некоторое изменение и удержание «состояния». Это может означать, например, изменение значения переменной. Вот шокер — у функционального программирования нет такого понятия, как состояние. Таким образом, без изменения переменных!
Теперь вспомним, когда вы впервые изучили программирование, и вы увидели утверждение типа «x = x + 1».
Моя первая мысль, когда я увидел это, была: «Подожди, что? Разве вы не можете просто отменить х с обеих сторон, но тогда вы получите невозможное уравнение … »
При этом вся математика, которую ты выучил, стала бесполезной.
Но с Haskell (чисто функциональным языком) он возвращает вам математику! Поскольку переменные не могут изменяться, операторы присваивания, подобные описанным выше, не могут произойти!
Императивные языки довольно свободны с побочными эффектами. Побочные эффекты — это то, что происходит за пределами «рабочей среды» метода. Например, это означает изменить состояние экрана в методе, напечатав строку.
Но побочные эффекты могут быть действительно ужасными. При вызове метода на императивном языке вы не гарантируете, что он будет делать то, что вы намеревались сделать. Метод может возвращать что-то совершенно другое при вызове с теми же аргументами в более позднее время, потому что на него может повлиять некоторое внешнее состояние.
В чисто функциональных языках (опять же, Haskell) побочные эффекты почти не могут возникнуть. Это также дает нам то, что называется ссылочной прозрачностью . Ссылочная прозрачность означает, что функция, которую мы вызываем, даст один и тот же результат для набора аргументов, независимо от того, что происходит во внешнем мире. Но если побочные эффекты не могут произойти, как мы должны печатать что-то на консоли? Ну, это делается с помощью какой-то магии, называемой монадой, которая в основном пытается поместить побочные эффекты в абсолютно чистую, свободную от состояний и побочных эффектов среду.
Отличным примером чисто функционального языка является математика. В математике нет такого понятия, как побочный эффект, потому что побочные эффекты не влияют! Это все определения и использование этих определений.
Как это соотносится с Ruby? Это где языки очень разные. Haskell невероятно чист, где все безумно чисто, и вам не разрешают делать беспорядок. В то время как с Ruby, если что-то не сделано должным образом, код может стать запутанным беспорядком изменений состояния. Рубин также облегчает выполнение побочных эффектов, когда это необходимо, тогда как с Haskell это королевская боль.
Система типов
Ruby, как большинство из нас знает, имеет динамическую систему типов, что означает, что определение типа выполняется во время выполнения. Таким образом, если есть какая-то проблема с типами переменных, которые не совпадают, мы только обнаруживаем это во время выполнения (то есть это приведет к аварийному завершению или выбрасыванию исключения).
Это кажется довольно плохим, потому что у нас могут постоянно зависать программы. Однако, если вы можете отслеживать ваши типы в вашей программе, это значительно ускоряет разработку.
Если у вас есть какой-либо опыт работы с Java ( дрожь ), которая имеет статическую систему типов, вы знаете проблемы, которые может вызвать такая система типов, как Java.
У Haskell невероятно строгая статическая система типов. Это, поначалу, заставляет большинство Rubyists съеживаться, и это определенно кажется более раздражающим. Но, позвольте мне сказать вам, это спасатель жизни.
У системы типов Haskell есть то, что называется выводом типа . Таким образом, практически во всех случаях вам не нужно «записывать» типы каких-либо функций. Из-за этого система типов Haskell чувствует себя невероятно краткой и очень быстрой, обеспечивая при этом удобство и безопасность строгой статической системы типов.
Ruby и Haskell на первый взгляд довольно сильно отличаются друг от друга. Один очень динамичный и плавный, другой жесткий и невероятно сильный. Но конечный результат, который получают обе системы типов, — это очень гибкий и быстрый опыт, позволяющий вам в значительной степени избегать размышлений о типах.
Однако, как личное предпочтение, мне больше нравится система типов Haskell. Я почти никогда не замечаю систему типов. Это также дает дополнительную выгоду от удивительной проверки типов во время компиляции, в той степени, в которой, если она компилируется, она, вероятно, не потерпит крах.
Важным фактором является модель данных. Ruby, как и почти все другие популярные языки (кроме JS), имеет модель ООП. Haskell использует нечто, называемое алгебраическими типами данных, объяснение которых занимает довольно много времени (особенно людям, у которых уже есть концепция ООП). По сути, эти типы данных не имеют большой иерархической структуры, и, при правильном использовании, может быть невероятно мощным.
Завершение — пока
Надеюсь, эта статья дала вам общее представление о том, что такое функциональное программирование, и начала сравнение между Haskell и Ruby.
Одно можно сказать наверняка. Если вы Rubyist, это не помешает поднять немного Haskell. Это может даже изменить способ написания кода (это для меня).
В следующей статье мы рассмотрим больше сходств и различий Haskell с Ruby, таких как встроенные методы (и функции), а также то, как функционируют сообщества обоих языков.
Руководство LYAH — отличный способ начать!