Get the code: learnhaskell.hs
Haskell skapades för att vara ett praktiskt, rent, funktionellt programmeringssprÄk. Det Àr kÀnt för sin anvÀnding av monader och dess hÀrledande typsystem men anledningen till att jag stÀndigt Äterbesöker sprÄket Àr pÄ grund av dess elegans. Haskell gör programmering till ett rent nöje.
-- Radkommenterar börjar med tvÄ bindestreck.
{- Flerradskommentarer innesluts av vÀnster/höger mÄsvinge bindestreck
block pÄ detta vis.
-}
----------------------------------------------------
-- 1. Fördefinierade datatyper och operatorer
----------------------------------------------------
-- Du har siffror
3 -- 3
-- Matte fungerar som förvÀntat
1 + 1 -- 2
8 - 1 -- 7
10 * 2 -- 20
35 / 5 -- 7.0
-- Division Àr normalt inte heltalsdivision
35 / 4 -- 8.75
-- Heltalsdivision, hÀr infix div
35 `div` 4 -- 8
-- Boolar (Sant och Falskt) Àr fördefinierade
True
False
-- Samt dess operationer
not True -- False
not False -- True
1 == 1 -- True
1 /= 1 -- False
1 < 10 -- True
-- I ovanstÄende exempel Àr `not` en funktion vilken bara tar ett argument.
-- Haskell behöver inte paranteser för sina funktionsanrop... alla argument
-- ges mellanslagsseparerade direkt efter funktionen. Det övergripande mönstret
-- Àr:
-- func arg1 arg2 arg3...
-- Se sektionen om funktioner för information om hur du skriver dina egna.
-- StrÀngar och bokstÀver
"Detta Àr en strÀng"
'a' -- bokstav
'Du kan inte anvÀnda enkelfnutt för strÀngar.' -- fel!
-- StrÀngar kan konkateneras
"Hej " ++ "vÀrlden!" -- "Hej vÀrlden!"
-- En strÀng Àr en lista av bokstÀver
['H', 'e', 'j', 's', 'a', 'n'] -- "Hejsan"
"Detta Àr en strÀng" !! 0 -- 'D'
----------------------------------------------------
-- 2. Listor och Tupler
----------------------------------------------------
-- Varje element i en lista mÄste ha samma typ.
-- Dessa listor Àr ekvivalenta:
[1, 2, 3, 4, 5]
[1..5]
-- Intervall Àr mÄngsidiga.
['A'..'F'] -- "ABCDEF"
-- Man kan stega intervall.
[0,2..10] -- [0, 2, 4, 6, 8, 10]
[5..1] -- [] (Haskell förutsÀtter normalt inkrement)
[5,4..1] -- [5, 4, 3, 2, 1]
-- Indexering in i en lista
[1..10] !! 3 -- 4 (nollindexerat)
-- Man kan ha oÀndliga listor i Haskell!
[1..] -- listan över alla naturliga tal
-- OÀndliga listor fungerar enbart för att Haskell har "lat evaluering".
-- Det betyder att Haskell bara evaluerar de uttryck den mÄste. Du kan alltsÄ
-- frÄga efter det 1000:e elementet i en oÀndlig lista och Haskell kommer dÄ ge
-- dig det:
[1..] !! 999 -- 1000
-- Nu har Haskell evaluerat element 1 till 1000 i denna lista... men resten
-- av medlemmarna i denna oÀndliga lista existerar inte Ànnu! Haskell kommer
-- faktiskt inte utvÀrdera element den inte mÄste.
-- Sammanslagning av tvÄ listor
[1..5] ++ [6..10]
-- LĂ€gg till 0 vid listhuvudet
0:[1..5] -- [0, 1, 2, 3, 4, 5]
-- fler listoperationer som huvud, svans, initiella samt sista
head [1..5] -- 1
tail [1..5] -- [2, 3, 4, 5]
init [1..5] -- [1, 2, 3, 4]
last [1..5] -- 5
-- listomfattningar
[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10]
-- med bivilkor
[x*2 | x <- [1..5], x*2 > 4] -- [6, 8, 10]
-- Varje element i en tupel kan ha olika typ men en tupel kan bara ha en
-- fixerad, eller statisk, lÀngd.
-- En tupel:
("haskell", 1)
-- För att komma Ät element i ett par, alltsÄ en 2-tupel, finns
-- de fördefinierade funktionerna:
fst ("haskell", 1) -- "haskell"
snd ("haskell", 1) -- 1
----------------------------------------------------
-- 3. Funktioner
----------------------------------------------------
-- En enkel funktion med tvÄ parametrar
add a b = a + b
-- Notera Àven att om du anvÀnder ghci (Haskellinterpretatorn) kommer du behöva
-- anvÀnda `let` namnbindning för att synliggöra din funktionsdeklaration,
-- alltsÄ
let add a b = a + b
-- För att anvÀnda funktionen
add 1 2 -- 3
-- Man kan Àven göra funktionsanropet infix, alltsÄ mellan parametersÀttningen,
-- med hjÀlp av bakÄtfnuttar:
1 `add` 2 -- 3
-- Du kan Àven definiera funktioner vars funktionsnamn avsaknar bokstÀver!
-- Med hjÀlp av parenteser kan du dÀrmed definiera operatorer (normalt infix)!
-- Följande Àr en operator för heltalsdivision, vilken förlitar sig pÄ div:
(//) a b = a `div` b
35 // 4 -- 8
-- Funktionsvakter: ett enkelt sÀtt att grena ut dina funktioner
fib x
| x < 2 = 1
| otherwise = fib (x - 1) + fib (x - 2)
-- Mönstermatchning fungerar pÄ liknande vis. HÀr ger vi tre olika
-- parametermatchningar för vÄrat fib-resulat. Haskell kommer automatiskt följa
-- första bÀsta trÀff, uppifrÄn ned, vars vÀnstra sida om likhetstecknet matchar
-- anroparens parametervÀrde.
fib 1 = 1
fib 2 = 2
fib x = fib (x - 1) + fib (x - 2)
-- Mönstermatchning pÄ tupler:
foo (x, y) = (x + 1, y + 2)
-- Mönstermatchning pÄ listor. HÀr Àr `x` det första elementet i listan och `xs`
-- Àr resten av listan. Nu kan vi skriva vÄran egen map-funktion
minMap func [] = []
minMap func (x:xs) = func x:(minMap func xs)
-- Anonyma funktioner, eller lambdauttryck, skapas med hjÀlp av omvÀnt
-- snedstreck, följt av parametrarna
minMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7]
-- AnvÀndning av fold (Àven kallad `inject`, `reduce`, osv.) tillsammans med en
-- anonym funktion. `fold1` Àr en vÀnstervikande funktion och anvÀnder första
-- vÀrdet i listan som det initiella vÀrdet för ackumulatorn.
foldl1 (\acc x -> acc + x) [1..5] -- 15
----------------------------------------------------
-- 4. Mer funktioner
----------------------------------------------------
-- Partiell applikation:
-- Om du inte anropar funktionen med alla sina argument
-- blir den partiellt applicerad. Det betyder att du erhÄller en funktion dÀr en
-- delmÀngd av parametrarna blivit vÀrdesatta men nÄgra Àr fortfarande fria.
add a b = a + b
foo = add 10 -- foo Àr nu en funktion som tar ett nummer och lÀgger till 10 till
-- det
foo 5 -- 15
-- Ett annat sÀtt att skriva samma sak
foo = (10+)
foo 5 -- 15
-- Funktionskomposition:
-- Operatorn `.` kedjar ihop funktioner
-- Till exempel, nedan Àr `foo` en funktion som tar ett vÀrde, den adderar 10
-- till det, multiplicerar det resultatet med 4 och sen ersÀtts med det vÀrdet.
foo = (4*) . (10+)
-- 4*(10+5) = 60
foo 5 -- 60
-- Precedensordning:
-- Haskell har en operator `$`. Denna operator applicerar en funktion till en
-- given parameter med dess precedens. I kontrast mot vanlig
-- funktionsapplikation, vilket har den högsta utvÀrderingsprioriteten 10 och
-- associerar till vÀnster, har denna prioritetsordning 0 och Àr
-- högerassociativ. Denna lÄga prioritet medför att parameteruttrycket till
-- höger om operatorn fÄr det reducerat innan det appliceras till sin vÀnster.
-- före
even (fib 7) -- falskt
-- ekvivalent
even $ fib 7 -- falskt
-- med funktionskomposition
even . fib $ 7 -- falskt
----------------------------------------------------
-- 5. Typsignaturer
----------------------------------------------------
-- Haskell har ett vÀldigt starkt typsystem, alla giltiga uttryck har en typ.
-- NÄgra grundlÀggande typer:
5 :: Integer
"hello" :: String
True :: Bool
-- Funktioner har ocksÄ typer,
-- `not` tar en bool och returnerar en bool:
-- not :: Bool -> Bool
-- HÀr Àr ett exempel pÄ en funktionssignatur vilken beskriver en funktion som
-- reducerar tvÄ heltal till ett:
-- add :: Integer -> Integer -> Integer
-- Trots att Haskell hÀrleder typen pÄ icke typsatta uttryck Àr det bra form att
-- explicit ange dessa för ens deklarerade funktioner:
double :: Integer -> Integer
double x = x * 2
----------------------------------------------------
-- 6. Kontrollflöde och Ifsatser
----------------------------------------------------
-- if-sats
haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome"
-- if-statser kan spridas över rader men indentering har betydelse
haskell = if 1 == 1
then "awesome"
else "awful"
-- case uttryck: följande Àr ett exempel pÄ kommandoradsparsning
case args of
"help" -> printHelp
"start" -> startProgram
_ -> putStrLn "bad args"
-- Haskell har inte loopar istÀllet anvÀnds recursion.
-- map applicerar en funktion över varje element i en lista
map (*2) [1..5] -- [2, 4, 6, 8, 10]
-- man kan deklarera en for funktion genom att anvÀnda map
for array func = map func array
-- och dÀrefter anvÀnda den tillsammans med en anonym funktion för att
-- efterlikna en loop
for [0..5] $ \i -> show i
-- men vi kunde Àven ha skrivit pÄ följande vis:
for [0..5] show
-- Du kan anvÀnda foldl eller foldr för att reducera en lista
-- foldl <fn> <initial value> <list>
foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43
-- Vilket Àr samma sak som
(2 * (2 * (2 * 4 + 1) + 2) + 3)
-- foldl viker frÄn vÀnster, foldr frÄn höger
foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
-- Vilket alltsÄ Àr samma sak som
(2 * 1 + (2 * 2 + (2 * 3 + 4)))
----------------------------------------------------
-- 7. Datatyper
----------------------------------------------------
-- SÄhÀr definierar du din egen datatyp i Haskell
data Color = Red | Blue | Green
-- NÀr du gjort det kan du anvÀnda den i funktionssignaturer och uttryck
say :: Color -> String
say Red = "Du Àr Rö!"
say Blue = "Du Àr BlÄ!"
say Green = "Du Àr Grön!"
-- Dina datatyper kan Àven ta parametrar
data Maybe a = Nothing | Just a
-- Följande uttryck Àr alla specialiseringar av typen Maybe
Just "hello" -- har typen `Maybe String`
Just 1 -- har typen `Maybe Int`
Nothing -- har typen `Maybe a` för alla `a`
----------------------------------------------------
-- 8. Haskell IO
----------------------------------------------------
-- Ăven om IO inte kan förstĂ„s fullt ut utan att först förklara monader Ă€r det
-- inte svÄrt att lÀra sig tillrÀckligt för att komma igÄng
-- NÀr ett Haskellprogram körs Àr det topnivÄns main som körs. Main mÄste
-- returnerna ett vÀrde av typen `IO a`, för nÄgon typ `a`. Till exempel:
main :: IO ()
main = putStrLn $ "Hej, himmelen! " ++ (say Blue)
-- putStrLn har typen type String -> IO ()
-- Det Àr enkelt att göra IO om du kan implementera ditt program som en funktion
-- frÄn String till String. Funktionen
-- interact :: (String -> String) -> IO ()
-- tar denna funktion och matar den med strÀngdata frÄn stdin och skriver ut
-- resultatet som en strÀng pÄ stdout
countLines :: String -> String
countLines = show . length . lines
main' = interact countLines
-- Du kan tÀnka pÄ vÀrden av typen `IO ()` som att representera
-- hÀndelsesekvenser du vill att din dator skall utföra, likt imperativa sprÄk.
-- För att kedja ihop hÀndelsesekvenser anvÀnder man ett syntaktiskt socker
-- kallat do-notation. Som exempel:
sÀgHej :: IO ()
sÀgHej = do
putStrLn "Vad heter du?"
namn <- getLine -- denna raden lÀser en rad frÄn stdin och vi binder den till
-- funktionsnamnet `namn`
putStrLn $ "Hejsan, " ++ namn
-- Ăvning: Skriv din egen version av interageringsfunktionen `interact` som bara
-- lÀser en rad frÄn stdin, vanliga `interact` lÀser till EOF.
-- Koden i sÀgHej kommer dock aldrig exekveras. Den enda handlingen som blir det
-- Àr som bekant utvÀrderingen av `main`.
-- För att köra `sÀgHej` kommentera ut definition av `main` ovan och
-- avkommentera nedanstÄende version:
-- main = sayHello
-- LÄt oss bÀttre förstÄ hur funktionen `getLine` vi just anvÀnde fungerar. Dess
-- typsignatur Àr:
-- getLine :: IO String
-- Du kan tÀnka pÄ typen `IO a` som att representeras av ett datorprogram vilken
-- kommer generera ett vÀrde av typen `a` nÀr det exekveras (utöver allt annat
-- det kan tÀnkas göra). Vi kan dÀrtill binda detta vÀrde till ett namn för
-- ÄteranvÀndning genom att anvÀnda `<-`. Vi kan Àven skapa vÄran egen handling
-- av typen `IO String`:
handling :: IO String
handling = do
putStrLn "Detta Àr en rad, tihi"
input1 <- getLine
input2 <- getLine
-- Typen av hela `do` blocket Àr vad som stÄr pÄ sista raden. HÀr Àr Àven
-- `return` inte ett nyckelord i sprÄket utan en funktion med en typsignatur
return (input1 ++ "\n" ++ input2) -- return :: String -> IO String
-- Vi kan anvÀnda `return` pÄ samma sÀtt som vi anvÀnde `getLine`:
main'' = do
putStrLn "Jag kommer eka tvÄ rader!"
result <- handling
putStrLn result
putStrLn "Tack och hej leverpastej!"
-- Typen `IO` Àr ett exempel pÄ en monad. SÀttet Haskell utnyttjar monader pÄ Àr
-- anledningen till hur sprÄket kan bibehÄlla sin renhet. En funktion vilken
-- interagerar med omvÀrlden (alltsÄ gör IO) blir markerad med `IO` i sin
-- typsignatur. Detta lÄter oss enkelt upptÀcka vilka funktioner som Àr "rena"
-- (inte interagerar med omvÀrlden eller Àr tillstÄndsoberoende) and vilka
-- funktioner som inte Àr det.
-- Detta Àr ett mÀktigt sÀrdrag eftersom det Àr enkelt att köra rena funktioner
-- sammanlöpande; Samtidig programmering Àr enkel att göra i Haskell.
----------------------------------------------------
-- 9. Haskell REPL (kodtolk)
----------------------------------------------------
-- Efter installation av GHC kan vi starta tolken genom att skriva `ghci`.
-- Nu kan du mata in Haskellkod direkt i den. Nya vÀrden mÄste introduceras med
-- `let` bindning:
let foo = 5
-- Du kan Àven se typen av namnbindningen med `:t`
> :t foo
foo :: Integer
-- Operatorer, som `+`, `:` och `$` Àr funktioner. Deras typ kan inspekteras
-- genom att skriva operatorn mellan parenteser:
> :t (:)
(:) :: a -> [a] -> [a]
-- Du kan fÄ ytterliggare information om nÄgot namn genom att anvÀnda `:i`
> :i (+)
class Num a where
(+) :: a -> a -> a
...
-- Defined in âGHC.Numâ
infixl 6 +
-- Du kan Àven köra alla handlingar av typen `IO ()` direkt i tolken
> sÀgHej
Vad Àr ditt namn?
Kompis!
Hello, Kompis!
Det finns mycket mer att upptÀcka med Haskell, inklusive typklasser och monader. Vilka Àr de stora idéerna som gör Haskell till det roliga programmeringssprÄket det Àr. Jag lÀmar dig med ett sista exempel; En implementation av quicksort:
qsort [] = []
qsort (p:xs) = qsort mindre ++ [p] ++ qsort större
where mindre = filter (< p) xs
större = filter (>= p) xs
Det finns tvÄ populÀra sÀtt att installera Haskell pÄ: Den traditionella Cabal sÀttet, eller det nyare Stack sÀttet.
Du kan finna vÀnligare och/eller djupare introduktioner till Haskell pÄ engelska frÄn: Learn you a Haskell, Happy Learn Haskell Tutorial eller Real World Haskell.
Got a suggestion? A correction, perhaps? Open an Issue on the GitHub Repo, or make a pull request yourself!
Originally contributed by Adit Bhargava, and updated by 1 contributor.