Программирование haskell язык программирования

Программирование haskell язык программирования

Every expression in Haskell has a type which is determined at compile time. All the types composed together by function application have to match up. If they don’t, the program will be rejected by the compiler. Types become not only a form of guarantee, but a language for expressing the construction of programs.

All Haskell values have a type:

char = 'a' :: Char int = 123 :: Int fun = isDigit :: Char -> Bool 

You have to pass the right type of values to functions, or the compiler will reject the program:

You can decode bytes into text:

bytes = Crypto.Hash.SHA1.hash "hello" :: ByteString text = decodeUtf8 bytes :: Text 

But you cannot decode Text, which is already a vector of Unicode points:

doubleDecode = decodeUtf8 (decodeUtf8 bytes)

Purely functional

Every function in Haskell is a function in the mathematical sense (i.e., «pure»). Even side-effecting IO operations are but a description of what to do, produced by pure code. There are no statements or instructions, only expressions which cannot mutate variables (local or global) nor access state like time or random numbers.

The following function takes an integer and returns an integer. By the type it cannot do any side-effects whatsoever, it cannot mutate any of its arguments.

square :: Int -> Int square x = x * x

The following string concatenation is okay:

Читайте также:  Как начинать изучать язык программирования

The following string concatenation is a type error:

Because getLine has type IO String and not String , like «Name: » is. So by the type system you cannot mix and match purity with impurity.

Type inference

You don’t have to explicitly write out every type in a Haskell program. Types will be inferred by unifying every type bidirectionally. However, you can write out types if you choose, or ask the compiler to write them for you for handy documentation.

This example has a type signature for every binding:

main :: IO () main = do line :: String  getLine print (parseDigit line) where parseDigit :: String -> Maybe Int parseDigit ((c :: Char) : _) = if isDigit c then Just (ord c - ord '0') else Nothing
main = do line  getLine print (parseDigit line) where parseDigit (c : _) = if isDigit c then Just (ord c - ord '0') else Nothing

You can also use inference to avoid wasting time explaining what you want:

do ss  decode "[\"Hello!\",\"World!\"]" is  decode "[1,2,3]" return (zipWith (\s i -> s ++ " " ++ show (i + 5)) ss is) => Just ["Hello! 6","World! 7"]

Types give a parser specification for free, the following input is not accepted:

do ss  decode "[1,2,3]" is  decode "[null,null,null]" return (zipWith (\s i -> s ++ " " ++ show (i + 5)) ss is) => Nothing

Concurrent

Haskell lends itself well to concurrent programming due to its explicit handling of effects. Its flagship compiler, GHC, comes with a high-performance parallel garbage collector and light-weight concurrency library containing a number of useful concurrency primitives and abstractions.

Easily launch threads and communicate with the standard library:

main = do done  newEmptyMVar forkIO (do putStrLn "I'm one thread!" putMVar done "Done!") second  forkIO (do threadDelay 100000 putStrLn "I'm another thread!") killThread second msg  takeMVar done putStrLn msg

Use an asynchronous API for threads:

do a1  async (getURL url1) a2  async (getURL url2) page1  wait a1 page2  wait a2 . 

Atomic threading with software transactional memory:

transfer :: Account -> Account -> Int -> IO () transfer from to amount = atomically (do deposit to amount withdraw from amount)

Atomic transactions must be repeatable, so arbitrary IO is disabled in the type system:

main = atomically (putStrLn "Hello!")

Lazy

Functions don’t evaluate their arguments. This means that programs can compose together very well, with the ability to write control constructs (such as if/else) just by writing normal functions. The purity of Haskell code makes it easy to fuse chains of functions together, allowing for performance benefits.

Define control structures easily:

when p m = if p then m else return () main = do args  getArgs when (null args) (putStrLn "No args specified!") 

If you notice a repeated expression pattern, like

you can give this a name, like

and c t = if c then t else False

and then use it with the same effect as the original expression.

Get code re-use by composing lazy functions. It’s quite natural to express the any function by reusing the map and or functions:

any :: (a -> Bool) -> [a] -> Bool any p = or . map p

Reuse the recursion patterns in map , filter , foldr , etc.

Packages

Open source contribution to Haskell is very active with a wide range of packages available on the public package servers.

There are 6,954 packages freely available. Here is a sample of the most common ones:

bytestring Binary data base Prelude, IO, threads
network Networking text Unicode text
parsec Parser library directory File/directory
hspec RSpec-like tests attoparsec Fast parser
monad-logger Logging persistent Database ORM
template-haskell Meta-programming tar Tar archives
snap Web framework time Date, time, etc.
happstack Web framework yesod Web framework
containers Maps, graphs, sets fsnotify Watch filesystem
hint Interpret Haskell unix UNIX bindings
SDL SDL binding OpenGL OpenGL graphics system
criterion Benchmarking pango Text rendering
cairo Cairo graphics statistics Statistical analysis
gtk Gtk+ library glib GLib library
test-framework Testing framework resource-pool Resource pooling
conduit Streaming I/O mwc-random High-quality randoms
QuickCheck Property testing stm Atomic threading
blaze-html Markup generation cereal Binary parsing/printing
xml XML parser/printer http-client HTTP client engine
zlib zlib/gzip/raw yaml YAML parser/printer
pandoc Markup conversion binary Serialization
tls TLS/SSL zip-archive Zip compression
warp Web server text-icu Text encodings
vector Vectors async Async concurrency
pipes Streaming IO scientific Arbitrary-prec. nums
process Launch processes aeson JSON parser/printer
dlist Difflists syb Generic prog.

Sponsors

Fastly‘s Next Generation CDN provides low latency access for all of Haskell.org’s downloads and highest traffic services, including the primary Hackage server, Haskell Platform downloads, and more.

Status.io powers https://status.haskell.org, and lets us easily tell you when we broke something.

Equinix Metal provides compute, storage, and networking resources, powering almost all of Haskell.org in several regions around the world.

DreamHost has teamed up to provide Haskell.org with redundant, scalable object-storage through their Dream Objects service.

Galois provides infrastructure, funds, administrative resources and has historically hosted critical Haskell.org infrastructure, as well as helping the Haskell community at large with their work.

Haskell.org

Psst! Looking for the wiki?

This is the new Haskell home page! The wiki has moved to wiki.haskell.org.

Источник

Haskell — ленивый язык программирования

Haskell — необычный язык с точки зрения тех, кто привык к JavaScript, С++, Python или любому другому императивному языку. Всё дело в том, что Haskell — функциональный язык. Мы уже рассказывали, чем отличаются функциональные языки от остальных, теперь посмотрим на них в деле.

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

Синтаксис в Haskell

Самая простая программа на Haskell выглядит так:

Давайте разберём, как работает единственная строка в программе. Main — это как бы главная функция, которая возвращает какое-то значение. В нашем случае оно возвращает значение вывода на экран строки «Hello World!» . Компилятор знает, что putStrLn — функция вывода на экран, но если бы он этого не знал, нам нужно было бы написать так:

Теперь мы явно указали тип функции main — это ввод и вывод каких-то результатов (IO — input/output, ввод-вывод). Ещё в Haskell есть такие базовые типы данных:

  • Integer — целое число
  • Char — символ
  • Double — число с плавающей точкой или дробное число

А ещё есть специальные конструкции (), [] и ->, с помощью которых можно сделать свои типы данных на основе базовых, например, список или массив.

Математические операции в Haskell работают и выглядят как обычно: +, –, *, /.

Функции и их значения

Почти всё в Haskell делается через функции. Задача программиста — описать функцию таким образом, чтобы компилятор понял:

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

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

// факториал — произведение чисел от единицы до этого числа. // к примеру, факториал трёх равен 1 * 2 * 3 fac :: Integer -> Integer fac 0 = 1 fac n | n > 0 = n * fac (n - 1)

Первая строка после комментариев говорит нам, что мы объявляем новую функцию (fac) ,и работать с типами данных (::) она будет так: на вход ей поступит целое число (Integer) и на выходе функция вернёт тоже целое число (-> Integer) .

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

Последняя строка — самая сложная и интересная. Следите за математической мыслью: факториал любого числа n (fac n) , где n больше нуля (| n > 0) , равен произведению этого числа (n *) на факториал предыдущего числа (fac (n – 1)) .

Если пока непонятно, что тут происходит, — почитайте про рекурсию, там мы подробно разобрали этот пример.

Мы только что записали строгое математическое определение на Haskell и объяснили функции, как ей найти факториал любого числа. Но если мы попробуем вызвать факториал числа 4.5, то Haskell выдаст ошибку, потому что 4.5 не относится к типу Integer.

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

Ленивые вычисления

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

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

Ленивые вычисления помогают сократить нагрузку на ресурсы и делают программы быстрее и эффективнее. Если вы пишете калькулятор, в котором предусматриваете все математические действия, но пользуетесь только сложением, то Haskell даже не будет обращать внимания на остальное. Он будет знать, что у вас есть код, который, если что, может ещё умножать и делить, но делать с ним пока ничего не будет.

Для чего нужен Haskell

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

Компиляторы. Благодаря тому, что Haskell всё делает строго по правилам, это отличный инструмент для написания компиляторов. Задача любого компилятора — взять текст программы, проверить, нет ли в нём ошибок, и выполнить код. Так как Haskell отлично работает с текстом и при этом не ошибается в правилах, то он может преобразовывать команды другого языка в команды для компьютера.

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

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

Источник

Оцените статью