Programowanie w systemie UNIX/Haskell

Haskell zmieni twój sposób myślenia o programowaniu (:-))


      "A language that doesn't affect the way you think about programming, is not worth knowing. "[1]

Wykorzystuje teorię kategorii.[2]


  Eugenia Chang's characterization of category theory:  category theory is the mathematics of mathematics. whatever mathematics does for the world category Theory does for mathematics




Cechy Haskella

edytuj
  • jest to język czysto funkcyjny:[3]
    • nie ma zmiennych w znaczeniu pojemników na wartości jak np. w C, są stałe (niewiadome jak w matematyce)
    • nie ma efektów ubocznych
  • lenistwo: obliczenia są wykonywane w momencie kiedy ich rezultat jest potrzebny, nie wcześniej. W efekcie możesz definiować nieskończone struktury danych, pod warunkiem, że nie używasz ich w całości.
  • nie ma pętli (używa rekurencji)[4]
  • funkcje wyższego rzędu - funkcje mogą być argumentami innych funkcji. Umożliwia to składanie przekształceń.
  • polimorfizm
  • klasy typów
  • monady

Składnia

edytuj
  • Pointfree Style[5]
  • Pointfull style
  • non-layout syntax with curly braces and semicolons [6]
  • do block

Instalacja

edytuj

Potrzebujemy:

  • kompilator/interpreter (GHC)
  • menadżer pakietów (cabal)
  • edytor (np. Gedit ma podświetlanie składni Haskella)

Metody:

  • z użyciem standardowych instalatorów (stabilna, ale zwykle nie najnowsza wersja), np.:
    • Centrum Oprogramowania Ubuntu
    • Menadżer Pakietów Synaptic
  • ręczna najnowszej wersji [7]

Przykłady

edytuj

Funkcje

edytuj

definicje

edytuj
  • w kodzie
  • w osobnym pliku
 let f :: a -> a ; f x = x -- GHCI , one line, signature
 let { fact 0 = 1 ; fact n = n * fact (n-1) } -- use braces with semicolons separating the definitions


-- https://pl.wikibooks.org/w/index.php?title=Programowanie_w_systemie_UNIX/Haskell&action=edit&section=5
> :set +m
> let a 0 = 0
|     a x = 1 + b x
|     b x = 2 + a (x -  1)
> a 1
3



Kwadrat liczby

edytuj

Przykład[8]


-- Haskell
square :: Int -> Int 
square n = n*n
// c
int square(int n) {
return n*n; }


Złożenie (superpozycja) funkcji

edytuj

Złożenie (superpozycja) funkcji ( ang. function composition) za pomocą:

  • operatora . ( ang. dot operator)
  • stylu pointfree
-- the '.' operator is used to compose functions
 
-- result of sort is pipelined to reverse
desort = (reverse . sort)
 
-- the result is a descending sort
countdown = desort [2,8,7,10,1,9,5,3,4,6]

Porównanie:


 


-- Haskell
f . g -- pointfree style
f . g  x -- pointfull style
f (g  x) --
( f . g ) (x) --
// c
f(g(x))

pętle

edytuj
  • Control.Monad.Loops (whileM_)[9]
  • go

Funkcja go[10]

-- https://stackoverflow.com/questions/5926033/haskell-efficient-equivalent-of-for-loop
import System.IO

loop :: Int -> IO ()
loop n = go n
  where
    go n | n <= 0 = return ()
    go n          = go (n-1)

main = loop 1000000


-- https://gist.github.com/niorad/45187d9eb76c585e573e
divideBy x y = go x y 0
  where go a b count
        | a < b = (count, a)
        | otherwise = go (a - b) b (count + 1)

Zmienna count jest pomocniczym licznikiem

Listy skończone [11]

sum [1..10]
product [1..10]

Listy nieskończone (przykład leniwego wartościowania): [12]

[1,3..]
[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,{Interrupted!}
take 20 [1,3..]
[1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39]


Cycle

  ghci> take 10 (cycle [1,2,3])   -- weź 10 pierwszych elementów z listy cyklicznej utworzonej przez powtarzanie elementów listy [1,2,3]
  [1,2,3,1,2,3,1,2,3,1]  
  ghci> take 12 (cycle "LOL ")  
  "LOL LOL LOL "

Monady

edytuj

Poradniki:

Wejście i wyjście

edytuj

Wczytywanie argumentów:

-- http://zvon.org/other/haskell/Outputsystem/getArgs_f.html
-- save it as a file a.hs
-- runhaskell a.hs 1 33 aaaa
import System
main = do 
x <- getArgs
print x

Wczytywanie pliku tekstowego, którego nazwa jest podana jako argument programu:

-- save it as a file f.hs
-- runhaskell f.hs gruff.txt
import System 
main = do 
args <- System.getArgs
let fileName = head args
print fileName
-- read text file
text <- readFile fileName
--  
putStrLn text

Pliki z kodem w Haskellu

edytuj

Pliki z rozszerzeniem hs zawierają kod Haskella. Może to być:[13]

  • funkcja
  • moduł
  • pakiet
  • program


Funkcje

edytuj

Definicję funkcji mojafunkcja zapisz do pliku mojafunkcja.hs W trybie interaktywnym ( ghci) wczytujesz funkcję za pomocą komendy:

:l mojafunkcja 

albo

:r -- reload current script

Moduł

edytuj

Moduł zawiera kilka funkcji. Każdy moduł to jeden plik. Nazwa modułu odpowiada nazwie pliku, z wyjątkiem modułu głównego.

Standardowe moduły:


Sprawdzmy listę funkcji w module :

 :browse nazwa_modułu

Pakiet

edytuj

Pakiet zawsze zawiera: [15]

  • jeden lub kilka modułów

Może zawierać:

  • kod w języku C
  • pliki nagłówkowe
  • dokumentację
  • testy dla zawartych modułów
  • dodatkowe narzędzia

Polecenie:

ghc-pkg list

pokazuje listę zainstalowanych pakietów [16]

Możemy poprosić o opis jednego z zainstalowanych modułów, np time:

ghc-pkg describe time

Cabal jest to program ułatwiający pracę z pakietami.

Pakiety cabala mogą występować w 3 postaciach:

  • kodu źródłowego (skompresowanego w plikach tar-ball, zip)
  • plikach binarnych
  • specjalnych postaciach: RPM, pakiety Debiana, windows [17]

Przykład :

 ghc-pkg Vector        -- sprawdzamy czy mamy moduł vector
 cabal update          -- uaktualniamy cabal
 cabal install Vector  -- instalujemy moduł vector

Program

edytuj

Kompletny program w Haskellu zawiera jeden lub więcej modułów (w tym główny moduł Main) skompilowanych z użyciem pakietów (w tym pakiet Prelude)

Pierwszy program

edytuj

Najprostszy program składa się z:[18][19]

  • jednego głównego pakietu (main)
  • jednego głównego modułu (Main)
  • jednej głównej funkcji (main). [20][21]
-- moduł główny Main
module Main
  (main) -- nazwa eksportowanej funkcji
   where
-- definicja funkcji main
main = putStrLn "Hello world"

Program zapisujemy do pliku, np. w.hs

Jak widać nazwa pliku zawierającego moduł główny nie musi być taka sama jak nazwa modułu.


Program kompilujemy:

ghc -o w w.hs

i uruchomiamy:

./w
 
Trójkąt sierpińskiego

Bardziej skomplikowany przykład, trójkąt Sierpińskiego:

-- http://dev.stephendiehl.com/hask/#diagrams
import Diagrams.Prelude
import Diagrams.Backend.SVG.CmdLine

sierpinski :: Int -> Diagram SVG
sierpinski 1 = eqTriangle 1
sierpinski n =
      s
     ===
  (s ||| s) # centerX
  where
    s = sierpinski (n - 1)

example :: Diagram SVG
example = sierpinski 5 # fc black

main :: IO ()
main = defaultMain example

Uruchamiamy:

  runhaskell diagram1.hs -w 256 -h 256 -o diagram1.svg

Pierwszy projekt

edytuj

Projekt [22]

Pliki z rozszerzeniem hsl zawierają kod w stylu programowania piśmiennego (ang. literate programming) [23]

Jak pracować w Haskellu

edytuj
  • tryb interaktywny (ghci)
  • kompilacja kodu (ghc)
  • z użyciem skryptu runhaskell

runhaskell

edytuj

Z użyciem skryptu BASHA:

runhaskell m.hs

W trybie kompilacji najpierw kompilujemy:

ghc -o m  m.hs

potem uruchamiamy wykonywalny plik:

./m

Sprawdzamy wersję i zainstalowane moduły:

 ghc -v

przykładowy wynik :

Glasgow Haskell Compiler, Version 7.10.3, stage 2 booted by GHC version 7.10.3
Using binary package database: /usr/lib/ghc/package.conf.d/package.cache
wired-in package ghc-prim mapped to ghc-prim-0.4.0.0-6cdc86811872333585fa98756aa7c51e
wired-in package integer-gmp mapped to integer-gmp-1.0.0.0-3c8c40657a9870f5c33be17496806d8d
wired-in package base mapped to base-4.8.2.0-0d6d1084fbc041e1cded9228e80e264d
wired-in package rts mapped to builtin_rts
wired-in package template-haskell mapped to template-haskell-2.10.0.0-3c4cb52230f347282af9b2817f013181
wired-in package ghc mapped to ghc-7.10.3-624693c6aa854116c707bc7b4d0d7cb6
wired-in package dph-seq not found.
wired-in package dph-par not found.
Hsc static flags: 
*** Deleting temp files:
Deleting: 
*** Deleting temp dirs:
Deleting: 
ghc: no input files
Usage: For basic information, try the `--help' option.


Dostępne moduły :

 ghc-pkg list

przykładowy wynik :

/usr/lib/ghc/package.conf.d
   Cabal-1.22.5.0
   array-0.5.1.0
   base-4.8.2.0
   bin-package-db-0.0.0.0
   binary-0.7.5.0
   bytestring-0.10.6.0
   containers-0.5.6.2
   deepseq-1.4.1.1
   directory-1.2.2.0
   filepath-1.4.0.0
   ghc-7.10.3
   ghc-prim-0.4.0.0
   haskeline-0.7.2.1
   hoopl-3.10.0.2
   hpc-0.6.0.2
   integer-gmp-1.0.0.0
   pretty-1.1.2.0
   process-1.2.3.0
   rts-1.0
   template-haskell-2.10.0.0
   terminfo-0.4.0.1
   time-1.5.0.1
   transformers-0.4.2.0
   unix-2.7.1.0
   xhtml-3000.2.1

W trybie interaktywnym:

ghci m.hs

potem uruchamiamy funkcję:

main


Ładowanie (wczytanie) modułów:

:load nazwa

Po wczytaniu modułu trzeba go uaktywnić:

:module nazwa

np.:

:m + Fractal.RUFF.Mandelbrot.Address

uaktywnia moduł Address z pakietu RUFF (m jest skrótem od module [24])


 :quit

aby wyjść

Tryb edycji wieloliniowej (ang. multiline-input mode )

 :set +m

Lista modułów zdefiniowanych przez użytkownika:[25]

:show modules

Lista funkcji w module

:browse ModuleName

Znak zachęty (monit, ang. prompt): Monit wskazuje, nad którym modułem (modułami) trwają prace:

  • znak zachęty zmienił się na *ghci>, aby wskazać, że bieżącym kontekstem dla wyrażeń wpisywanych w znaku zachęty jest moduł główny, który właśnie załadowaliśmy
  • Prelude is the only module you have without any imports

Problemy

edytuj

cabal: unrecognised command: sandbox (try --help)

edytuj

Może wersja cabala jest za stara?[26] Sprawdzamy:

cabal -V

przykładowy wynik:

cabal-install version 1.16.0.2
using version 1.16.0 of the Cabal library 

upgrade:

cabal install cabal-install


oraz:

export PATH="$HOME/.cabal/bin:${PATH}"
 :info name

przykład:

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


 :show paths

zmień ścieżkę:

 :cd path
:t nazwa
 hlint a.hs

Zobacz również

edytuj

Przypisy