summaryrefslogtreecommitdiffhomepage
path: root/pl-pl/haskell-pl.html.markdown
blob: 236b89589f26275a5f4903b8c811352778224b96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
---
category: language
filename: haskell-pl.hs
language: Haskell
contributors:
    - ["Adit Bhargava", "http://adit.io"]
translators:
    - ["Remigiusz Suwalski", "https://github.com/remigiusz-suwalski"]
lang: pl-pl

---

Haskell został zaprojektowany jako praktyczny, czysto funkcyjny język 
programowania. Jest znany przede wszystkim ze względu na jego monady oraz system
typów, ale ja lubię do niego wracać przez jego elegancję. Sprawił on, że 
programowanie jest prawdziwą przyjemnością.

```haskell
-- Komentarze jednolinijkowe zaczynają się od dwóch myślników
{- Komentarze wielolinijkowe należy 
zamykać w bloki klamrami.
-}

----------------------------------------------------
-- 1. Podstawowe typy danych oraz operatory
----------------------------------------------------

-- Mamy liczby
3 -- 3

-- Podstawowe działania działają tak, jak powinny
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20
35 / 5 -- 7.0

-- dzielenie domyślnie zwraca ,,dokładny'' wynik
35 / 4 -- 8.75

-- dzielenie całkowitoliczbowe
35 `div` 4 -- 8

-- wartości logiczne także są podstawowym typem danych:
True
False

-- operacje logiczne: negacja oraz porównania
not True -- False
not False -- True
1 == 1 -- True
1 /= 1 -- False
1 < 10 -- True

-- W powyższych przykładach, `not` jest funkcją przyjmującą jeden argument.
-- Haskell nie potrzebuje nawiasów, by wywołać funkcję: argumenty są po prostu
-- wypisywane jeden za drugim. Ogólnie wygląda to tak:
-- funkcja arg1 arg2 arg3...
-- Sekcja poświęcona funkcjom zawiera informacje, jak stworzyć własne.

-- Łańcuchy znaków (stringi) i pojedyncze znaki:
"To jest lancuch."
'a' -- znak
'Nie mozna laczyc apostrofow z lancuchami.' -- błąd!

-- Łańcuchy można sklejać
"Hello " ++ "world!" -- "Hello world!"

-- Łańcuch jest listą własnych znaków
['H', 'e', 'l', 'l', 'o'] -- "Hello"
"To jest lancuch" !! 0 -- 'T'

----------------------------------------------------
-- 2. Listy oraz krotki
----------------------------------------------------

-- Wszystkie elementy listy muszą być tego samego typu.
-- Poniższe dwie listy są identyczne:
[1, 2, 3, 4, 5]
[1..5]

-- Zakresy są uniwersalne.
['A'..'F'] -- "ABCDEF"

-- Przy tworzeniu zakresów można określić krok.
[0,2..10] -- [0, 2, 4, 6, 8, 10]
[5..1] -- To nie zadziała, gdyż w Haskellu zakresy tworzone są domyślnie rosnąco
[5,4..1] -- [5, 4, 3, 2, 1]

-- indeksowanie listy od zera
[1..10] !! 3 -- 4

-- Można nawet tworzyć listy nieskończone!
[1..] -- lista wszystkich liczb naturalnych

-- Nieskończone listy mają prawo działać, ponieważ Haskell cechuje się leniwym 
-- wartościowaniem. To oznacza, że obliczane są jedynie te elementy listy, 
-- których istotnie potrzebujemy. Możemy poprosić o tysiączny element i 
-- dostaniemy go:

[1..] !! 999 -- 1000

-- Haskell wyznaczył pierwsze tysiąc elementów listy, ale cała jej reszta 
-- jeszcze nie istnieje! Nie zostanie obliczona ich wartość, póki nie zajdzie
-- taka potrzeba.

-- łączenie dwóch list
[1..5] ++ [6..10]

-- dodawanie pojedynczego elementu na początek listy
0:[1..5] -- [0, 1, 2, 3, 4, 5]

-- więcej operacji na listach
head [1..5] -- 1
tail [1..5] -- [2, 3, 4, 5]
init [1..5] -- [1, 2, 3, 4]
last [1..5] -- 5

-- list comprehensions
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]

-- z dodatkowym warunkiem
[x*2 | x <- [1..5], x*2 > 4] -- [6, 8, 10]

-- każdy element krotki może być innego typu, jednak sama krotka musi być stałej 
-- długości. Przykładowo:
("haskell", 1)

-- dostęp do elementów pary (krotki długości 2):
fst ("haskell", 1) -- "haskell"
snd ("haskell", 1) -- 1

----------------------------------------------------
-- 3. Funkcje
----------------------------------------------------
-- Prosta funkcja przyjmująca dwa argumenty
add a b = a + b

-- Pamiętaj, że podczas stosowania ghci, interpretera Haskella, wszelkie 
-- definicje muszą zostać poprzedzone słowem `let`, na przykład:
-- let add a b = a + b

-- Używanie funkcji:
add 1 2 -- 3

-- Nazwę funkcji można podać między dwoma argumentami, ale wtedy musi zostać
-- otoczona grawisami:
1 `add` 2 -- 3

-- Nazwa funkcji nie musi zawierać żadnych liter, przykładem czego jest 
-- operator dzielenia:
(//) a b = a `div` b
35 // 4 -- 8

-- Strażnicy: prosty sposób na rozbijanie funkcji na przypadki
fib x
  | x < 2 = 1
  | otherwise = fib (x - 1) + fib (x - 2)

-- Dopasowanie wzorca jest podobne. Haskell sam automatycznie wybierze, która
-- z poniższych definicji fib powinna zostać użyta:
fib 1 = 1
fib 2 = 2
fib x = fib (x - 1) + fib (x - 2)

-- Dopasowanie z krotkami:
foo (x, y) = (x + 1, y + 2)

-- Dopasowanie z listami. Tutaj `x` jest pierwszym elementem listy, 
-- natomiast `xs` to jej reszta (ogon). Poniższa funkcja nakłada funkcję
-- na każdy z elementów listy:
myMap func [] = []
myMap func (x:xs) = func x:(myMap func xs)

-- Funkcje anonimowe tworzone są przy użyciu w-tył-ciachu, po którym następują
-- wszystkie argumenty:
myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]

-- używanie zwijania z anonimowymi funkcjami: foldl1 zwija z lewej strony,
-- przyjmując jako wartość początkową zbieracza pierwszy element listy.
foldl1 (\acc x -> acc + x) [1..5] -- 15

----------------------------------------------------
-- 4. Więcej funkcji
----------------------------------------------------

-- częściowe nakładanie: jeśli funkcja nie otrzyma wszystkich swoich argumentów, 
-- zostaje cześciowo nałożona - zwraca funkcję, która przyjmuje pozostałe,
-- brakujące argumenty.

add a b = a + b
foo = add 10 -- foo jest teraz funkcją, która przyjmuje liczbę, zwiększa ją o 10
foo 5 -- 15

-- Inny sposób na zapisanie tego samego:
foo = (10+)
foo 5 -- 15

-- składanie funkcji:
-- operator `.` składa wiele funkcji w jedną.
-- Dla przykładu, foo jest funkcją, która powiększa swój argument o 10, mnoży 
-- tak uzyskaną liczbę przez 4 i zwraca wynik:
foo = (4*) . (10+)

-- 4*(10 + 5) = 60
foo 5 -- 60

-- ustalanie kolejności
-- Haskell posiada inny operator, `$`, który nakłada funkcję do podanego
-- parametru. W przeciwieństwie do zwykłego lewostronnie łącznego nakładania 
-- funkcji, którego priorytet jest najwyższy (10), operator `$` posiada
-- priorytet 0 i jest prawostronnie łączny. Tak niski priorytet oznacza, że
-- wyrażenie po prawej traktowane jest jako parametr funkcji po lewej

-- wcześniej
even (fib 7) -- fałsz

-- równoważnie
even $ fib 7 -- fałsz

-- składanie funkcji
even . fib $ 7 -- fałsz


----------------------------------------------------
-- 5. Sygnatury typów
----------------------------------------------------

-- Haskell posiada wyjątkowo silny system typów, w którym każde poprawne 
-- wyrażenie ma swój typ.

-- Kilka podstawowych typów:
5 :: Integer
"hello" :: String
True :: Bool

-- Funkcje też są określonego typu.
-- `not` przyjmuje wartość logiczną i taką też zwraca:
-- not :: Bool -> Bool

-- Przykład funkcji przyjmującej dwa argumenty
-- add :: Integer -> Integer -> Integer

-- Dobrą praktyką podczas definiowania wartości jest napisanie nad nią
-- także jej typu:
double :: Integer -> Integer
double x = x * 2

----------------------------------------------------
-- 6. Wyrażenia warunkowe
----------------------------------------------------

-- wyrażenie warunkowe
haskell = if 1 == 1 then "wspaniale" else "paskudnie" -- haskell = "wspaniale"

-- wyrażenie warunkowe można rozbić na wiele linii, 
-- ale trzeba uważać na wcięcia w kodzie
haskell = if 1 == 1
            then "wspaniale"
            else "paskudnie"

-- rozpatrywanie przypadków: oto jak można parsować argumenty z linii poleceń:
case args of
  "help" -> printHelp
  "start" -> startProgram
  _ -> putStrLn "bad args"

-- Haskell zastępuje pętle (których nie ma) rekurencyjnymi wywołaniami funkcji.
-- map aplikuje funkcję do każdego elementu listy:

map (*2) [1..5] -- [2, 4, 6, 8, 10]

-- możesz zdefiniować funkcję for przy użyciu map:
for array func = map func array

-- a następnie użyć jej:
for [0..5] $ \i -> show i

-- mogliśmy użyć krótszego zapisu bez zmiany działania funkcji for:
for [0..5] show

-- Do redukcji listy służy polecenie foldl (foldr):
-- foldl <fn> <initial value> <list>
foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43

-- Jest to równoważne z:
(2 * (2 * (2 * 4 + 1) + 2) + 3)

-- foldl składa od od lewej strony, foldr od prawej
foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16

-- To zaś równoważne jest:
(2 * 1 + (2 * 2 + (2 * 3 + 4)))

----------------------------------------------------
-- 7. Typy danych
----------------------------------------------------

-- Oto jak tworzy się nowe typy danych w Haskellu:

data Color = Red | Blue | Green

-- Teraz można używać ich we własnych funkcjach:

say :: Color -> String
say Red = "You are Red!"
say Blue = "You are Blue!"
say Green =  "You are Green!"

-- Twoje typy danych mogą posiadać nawet parametry:

data Maybe a = Nothing | Just a

-- Wszystkie poniższe są typu Maybe
Just "hello"    -- typu `Maybe String`
Just 1          -- typu `Maybe Int`
Nothing         -- typu `Maybe a` for any `a`

----------------------------------------------------
-- 8. Haskell IO
----------------------------------------------------

-- Chociaż obsługa wejścia i wyjścia nie może zostać wyjaśniona przez poznaniem
-- monad, spróbujemy zrobić to częściowo

-- Wykonanie programu napisanego w Haskellu wywołuje funkcję `main` 
-- Musi zwrócić wartość typu `IO a` dla pewnego `a`. Przykład:

main :: IO ()
main = putStrLn $ "Hello, sky! " ++ (say Blue)
-- putStrLn has type String -> IO ()

-- Najłatwiej obsłużyć wejście i wyjście, kiedy program zostanie 
-- zaimplementowany jako funkcja String -> String. Funkcja
--    interact :: (String -> String) -> IO ()
-- pobiera pewien tekst, wykonuje na nim operacje, po czym wypisuje wynik.

countLines :: String -> String
countLines = show . length . lines

main' = interact countLines

-- Możesz myśleć o wartości typu `IO ()` jako reprezentującej ciąg czynności,
-- które komputer ma wykonać, zupełnie niczym program komputerowy w imperatywnym
-- języku programowania. Akcje można łączyć przy użyciu notacji `do`:

sayHello :: IO ()
sayHello = do
   putStrLn "What is your name?"
   name <- getLine -- this gets a line and gives it the name "name"
   putStrLn $ "Hello, " ++ name

-- Ćwiczenie: napisz własną wersję `interact`, 
-- która czyta tylko jedną linię wejścia.

-- Kod w `sayHello` nigdy się nie wykona. Jedyną akcją, która zostanie 
-- uruchomiona, jest wartość `main`.
-- Aby uruchomić `sayHello`, należy zastąpić poprzednią definicję `main` przez
--   main = sayHello

-- Spróbujmy lepiej zrozumieć, jak działa funkcja `getLine`, której właśnie
-- użyliśmy. Jej typem jest
--    getLine :: IO String
-- Możesz myśleć o wartości typu `IO a` jako reprezentującej program, który
-- wygeneruje wartość typu `a`, poza wszystkim innym, co jeszcze zrobi.
-- Możemy także tworzyć własne akcje typu `IO String`:

action :: IO String
action = do
   putStrLn "This is a line. Duh"
   input1 <- getLine
   input2 <- getLine
   -- The type of the `do` statement is that of its last line.
   -- `return` is not a keyword, but merely a function
   return (input1 ++ "\n" ++ input2) -- return :: String -> IO String

-- Możemy użyć tego tak jak używaliśmy `getLine`:

main'' = do
    putStrLn "I will echo two lines!"
    result <- action
    putStrLn result
    putStrLn "This was all, folks!"

-- Typ `IO` jest przykładem monady. Sposób w jakim Haskell używa monad do 
-- obsługi wejścia i wyjścia pozwala mu być czysto funkcyjnym językiem.
-- Każda funkcja, która wchodzi w interakcje ze światem zewnętrznym, oznaczana
-- jest jako `IO` w jej sygnaturze typu, co umożliwia odróżnianie funkcji
-- czystych od zależnych od świata lub modyfikujących stan.

-- To naprawdę użyteczna własność, dzięki której jesteśmy w stanie uruchamiać
-- czyste funkcje jednocześnie.

----------------------------------------------------
-- 9. Interaktywne środowisko programowania
----------------------------------------------------

-- Aby uruchomić repl (read-eval-print loop, interaktywne środowisko), należy
-- wpisać `ghci`. Można już programować. Do definiowania nowych wartości służy
-- słowo kluczowe `let`:

let foo = 5

-- Do sprawdzania typów dowolnej wartości (wyrażenia) wykorzystuje się `:t`:

> :t foo
foo :: Integer

-- Działania takie jak `+`, `:` czy `$`, są funkcjami.
-- Przed sprawdzeniem ich typu należy otoczyć je nawiasami:

> :t (:)
(:) :: a -> [a] -> [a]

-- Dodatkowych informacji dostarcza `:i`:

> :i (+)
class Num a where
  (+) :: a -> a -> a
  ...
    -- Defined in ‘GHC.Num’
infixl 6 +

-- Można nawet wykonywać akcje typu `IO ()`!

> sayHello
What is your name?
Friend!
Hello, Friend!

```

Pominęliśmy wiele aspektów Haskella, wliczając w to monady. To właśnie one 
sprawiają, że programowanie w Haskellu sprawia tyle frajdy. Na zakończenie
pokażę Tobie implementację algorytmu quicksort w Haskellu:

```haskell
qsort [] = []
qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater
    where lesser  = filter (< p) xs
          greater = filter (>= p) xs
```

Haskell może zostać zainstalowany na co najmniej dwa sposoby:
 - tradycyjnie [przy użyciu Cabala](http://www.haskell.org/platform/),
 - nowocześnie [z pomocą Stack](https://www.stackage.org/install).

Godnymi poleceniami wprowadzeniami są wspaniałe
[Learn you a Haskell](http://learnyouahaskell.com/) albo
[Real World Haskell](http://book.realworldhaskell.org/).