Кортежі — це гетерогенні структури даних, фіксованої довжини, та з елементами здебільшого різних типів.

Створюються вони за допомоги бінарного оператора (,).

> (,) 'c' True ('c',True)

При цьому в нас виходить двох елементний кортеж, часто його ще називають парою. Але кортежі можуть бути й довші, або коротші.

Найкоротший кортеж - це порожній кортеж.

> () ()

Якщо нам потрібно більше елементів, то значить треба додати більше ком. Але кортежу довжиною в один елемент не існує, оскільки синтаксично його не можливо створити через те що елемент в дужках, наприклад (3), це просто групування.

> (,,) 4 'a' False (4,'a',False) > (,,,) 4 'a' False "Haskell" (4,'a',False,"Haskell")

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

> (4, 'a', False, "Haskell") (4,'a',False,"Haskell")

Тепер перейдемо до їхніх типів.

Нагадую: простори імен типів та всього іншого відокремлені.

Тип кортежів це ().

> :t () () :: ()

Якщо це не порожній кортеж, то й тип не порожній.

> :t ('c', True) ('c', True) :: (Char, Bool)

Це все добре, але треба якось же із ними працювати й отримувати окремі елементи. У стандартній бібліотеці для цього є кілька готових функцій, наприклад візьмемо fst котра повертає перший елемент, та snd котра повертає другий елемент.

> p = (1,2) > fst p 1 > snd p 2

Ці функції працюють тільки з парами, і реалізовуються через зіставлення.

fst (x,_) = x snd (_,y) = y

#ukrhaskell #haskell #tuple #кортежі #типи #програмування

Гетерогенна система — Вікіпедія

Під час вивчання Haskell, та й, мабуть, під час роботи з ним, часто використовується інтерпретатор. На жаль, за замовчуванням він не дуже зручний, але частково ми можемо виправити це простими налаштуваннями.

Всі налаштування робляться через його команду :set. До прикладу, часто може знадобитися переглядати типи функцій, чи їхніх значень після виконання. Робиться це командою :type передаючи певну конструкцію, але можна увімкнути автоматичний друк типів при виконанні кожної інструкції командою :set +t. Після цього виконання буде виглядати так:

ghci> 4 4 it :: Num a => a ghci> 'N' 'N' it :: Char ghci> 2 + 2 * 4 10 it :: Num a => a

Тепер нам не потрібно виконувати окремі команди, щоб побачити значення та тип результату.

Також можна змінити підказку (prompt) щоб не дивитись кожнісінький раз на те що ми й так знаємо, а саме що ми у ghci.

ghci> :set prompt "> " > "It's a String" "It's a String" it :: String >

Це вже краще, але далеко до ідеалу, тому додамо ще якусь корисну інформацію, окрім прибирання зайвої, наприклад номер рядка й завантажений модуль.

> :set prompt "[%l] %s> " [7] Prelude>

Так вже цікавіше.

Є ще одна штучка котра може нам іноді знадобитись при тестах.

[1] Prelude> f = do <interactive>:1:5: error: [GHC-82311] Empty 'do' block Suggested fix: Perhaps you intended to use NondecreasingIndentation [2] Prelude> :set +m [3] Prelude> f = do ghci| putStrLn "Привіт всім хаскелятам та хаскелищам!" ghci| [6] Prelude> f Привіт всім хаскелятам та хаскелищам! [7] Prelude>

:set +m дозволяє писати багато рядкові конструкції.

Це все добре, але після перезапуску, всі налаштування загубляться у просторі всесвіту й ви втратите свої персональні налаштунки, а допомагає уникнути цього .ghci файл у домашній теці, або у поточній. Просто запишіть у нього всі необхідні команди й ви спаслись.

#ukrhaskell #haskell #ghci #налаштування #prompt #set

Social.Net.Ua

error та undefined це не звичайні функції. Крім того, що вони поліморфні, та ще й переривають виконання.

Перша приймає рядок з повідомленням помилки й виводить його на екран, а друга просто перериває обчислення.

Їхній поліморфізм полягає в типі повернення, й потрібен для того, щоб можна було їх викликати будь-де.

λ> error "Упс, щось не так." *** Exception: Упс, щось не так. CallStack (from HasCallStack): error, called at <interactive>:4:1 in interactive:Ghci1 λ> undefined *** Exception: Prelude.undefined CallStack (from HasCallStack): undefined, called at <interactive>:5:1 in interactive:Ghci1

#ukrhaskell #haskell #error #undefined #поліморфізм #програмування

Найменшенький (@[email protected])

Параметризований поліморфізм у

Окрім виразу let in є ще схожа конструкція where. Вона виконує ту ж функцію, але трохи по інакшому.

g n = x+2*x where x = n^3+2

На перший погляд, змінилась тільки послідовність оголошення та використання функцій, і це майже правильно. Ключова відмінність у, тому що let in це вираз, а where конструкція. Тому друге можна використовувати там де не можна перше, наприклад при використанні кількох визначень з охоронними виразами чи зіставленнями.

f x | x > 0 = y * 2 | x < 0 = y / 2 where y = cos x^2

На жаль приклад дуже не дуже, але є що є.

Тут оголошені функції після whare можна використовувати в усіх визначеннях f, й навіть в охоронних виразах. let in такого не дозволяє.

#ukrhaskell #haskell #where #letin #вираз #конструкція #підфункції #охоронні_вирази #програмування

Найменшенький (@[email protected])

Вираз let in у

Часто у вичисленнях потрібно використовувати одне й теж значення у різних місцях і коли це не якийсь літерал, а вираз, то простіше дати йому назву і використати її. Саме для цього служить вираз let in.

f x = let y = x+2*x in y+1/y

Це спрощує, і написання, і розуміння коду. У цьому випадку одразу видно що використовується одне значення і не потрібно порівнювати візуально. Функція повертає значення яке стоїть після in.

Ми можемо оголошувати одразу кілька функцій у секції let.

f x = let a = x*2 b = x^2 in a + b/2

У let можна створювати не тільки константи, а й функції з аргументами.

f x = let g y = y*2 in g x + 2

Читати такий код не дуже зручно тому його можна, або розбити на кілька рядків, або додати дужки.

f x = let {g y = y*2} in g x + 2

#ukrhaskell #програмування #haskell #letin #let #in #вираз #підфункції

Social.Net.Ua

У синтаксисі Haskell як і у Python відступи грають важливу роль. Коли у нас є довге визначення і ми хочемо розбити його на менші й зрозуміліші частини потрібно якось уточнити що це не нове оголошення, а продовження попереднього.

Для прикладу візьмемо вираз if then else, бо інші ми ще не вивчали.

f x = if x > 0 then True else False

У цьому виразі немає великих значень чи формул, але ми розглянемо його для простого розуміння. Розбити його на окремі рядки ми можемо так.

f x = if x > 0 then True else False

Або так.

f x = if x > 0 then True else False

Кількість пробілів не має значення. Головне що їх було, або таж кількість, або більше. Якщо відступ відсутній, то це означає що починається нове оголошення.

!! Один символ табуляції завжди вважається як 8 пробілів.

#ukrhaskell #програмування #haskell #відсупи #табуляція #синтаксис #syntax #spaces #tab

Social.Net.Ua

Під час розробки часто потрібно перезапускати програму перевіряючи її поведінку. Але робити це за допомоги інтерпретатора ghci запускаючи функцію не завжди зручно, та й збирати компілятором ghc і запускати бінарник також. Тому для зручності було зроблено runghc. Це команда яка компілює код і зразу його виконує не зберігаючи у файл.

$ runghc main.hs

#ukrhaskell #haskell #ghc #ghci #runghc #запуск #програмування

Social.Net.Ua

@balaraz розкажіть, як ви зараз думаєте, навіщо оцей хаскель потрібен, які у нього переваги над імперативними мовами? (btw тег #ukrhaskell прикольний, буду користуватись)

Через те що у Haskell аргументи функції не вкладаються у дужки при спробі передати результат однієї функції в іншу потрібно використати дужки для групування.

foo bar x -- Функції передаються два аргументи bar і x foo (bar x) -- Функції bar передається аргумент x і її результат підставиться як один аргумент функції foo

У цьому простому прикладі все виглядає просто і зрозуміло, але якщо у нас був би складніший вираз з іншими дужками все виглядало б не так зрозуміло.

Для полегшення додали спеціальний оператор зниження пріоритету. Складається він з одного символу $. Використати його ми можемо тут підставивши між функціями.

foo $ bar x

Результат буде той же що й з дужками.

Цей оператор має праву асоціативність і найнижчий пріоритет. Перевірити це ми можемо в інтерпретаторі.

> :i ($) ($) :: (a -> b) -> a -> b -- Defined in ‘GHC.Base’ infixr 0 $

Тако ж у цьому повідомленні ми бачимо тип цього оператора. Він приймає іншу функцію й аргумент цієї функції, а повертає результат цієї функції. Визначення цього оператора таке.

($) f x = f x

#ukrhaskell #програмування #haskell #оператор #$ #пріоритет #функції

Social.Net.Ua

За допомоги зіставлення ми можемо розділити виконання на кілька гілок залежно від умови, але ця умова може тільки перевіряти на рівність. Коли нам потрібно використати складнішу умову на допомогу приходять охоронні вирази.

Оголошуються ці вирази після аргументів відділяючись вертикальною лінією.

f x | x > 0 = x

Цю функцію можна викликати тільки на додатних числах.

Також ми можемо обʼєднати зіставлення й охоронні вирази.

f 0 = 0 f x | x > 0 = 2 / x

Якщо нам потрібно виконувати різні дії залежно від різних умов ми можемо оголошувати кілька різних функцій.

f x | x > 0 = x * 2 f x | x < 0 = x * (-2)

Якщо спробуємо викликати цю функцію на значенні 0, то отримаємо помилку.

*** Exception: Demo.hs:(18,1)-(19,22): Non-exhaustive patterns in function f

У попередньому дописі я писав що можна оголошувати тільки одну функцію з константою, а не зіставленням, але як ви вже могли замітити, то з охоронними виразами це обмеження зникає.

У такому оголошенні є мінус, а саме те що ми повторюємо назву функції та її аргументи, тому ми можемо спростити це оголошення ось так.

f 0 = 0 f x | x > 0 = x * 2 | x < 0 = x * (-2)

Це абсолютно рівні оголошення, тільки тут ми ще додали зіставлення для нуля.

#ukrhaskell #програмування #haskell #охоронні_вирази #зіставлення #умови #функції

Social.Net.Ua