Beginners projects examples

Examples of simple Haskell projects with no lib dependency. You can launch these projects directly from ghci (so you won’t need to set up cabal and build files).

Basic apps

Code for these apps is a slight modification of:
https://www.haskellforall.com/2015/10/basic-haskell-examples.html

ToDo App

This is todo-list that will look smth like this (in terminal):

Current TODO list:
0: NewEntry
1: Something
>> Enter command:

Example usage:

  • Add new entry with command like + DoSmth.
  • To remove entry #0, type - 0.
  • To exit type q
 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
promptTodosList :: [String] -> IO ()
promptTodosList todos = do
	putStrLn ""
	putStrLn "Current TODO list:"
	mapM_ printTodoEntry (zip [0..] todos)
	putStrLn ">> Enter command:"
	command <- getLine
	interpretUserInput command todos

interpretUserInput :: String -> [String] -> IO ()
interpretUserInput ('+':' ':todo) todos = promptTodosList (todo:todos)
interpretUserInput ('-':' ':num ) todos =
	case deleteEntry (read num) todos of
		Nothing -> do
			putStrLn "No TODO entry matches the given number"
			promptTodosList todos
		Just todos' -> promptTodosList todos'
interpretUserInput  "q"           todos = return ()
interpretUserInput  command       todos = do
	putStrLn ("Invalid command: `" ++ command ++ "`")
	promptTodosList todos

printTodoEntry :: (Int, String) -> IO ()
printTodoEntry (n, todo) = putStrLn (show n ++ ": " ++ todo)

deleteEntry :: Int -> [a] -> Maybe [a]
deleteEntry 0 (_:as) = Just as
deleteEntry n (a:as) = do
	as' <- deleteEntry (n - 1) as
	return (a:as')
deleteEntry _  []    = Nothing

main = do
	putStrLn "========================================="
	putStrLn "Commands:"
	putStrLn "+ <String> - Add a TODO entry"
	putStrLn "- <Int>    - Delete the numbered entry"
	putStrLn "q          - Quit"
	putStrLn "========================================="
	promptTodosList []

Calendar

This app prints pretty-formated calendar for 2015. For example, November looks like:

November 2015
--------------------
Su Mo Tu We Th Fr Sa
_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

 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
data DayOfWeek
    = Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday
    deriving (Eq, Enum, Bounded)

data Month
    = January | February | March     | April   | May      | June
    | July    | August   | September | October | November | December
    deriving (Enum, Bounded, Show)

nextDayOfWeek :: (Eq a, Enum a, Bounded a) => a -> a
nextDayOfWeek x | x == maxBound = minBound
                | otherwise     = succ x

pad :: Int -> String
-- makes '1' to be ' 1', and keeps '12' to be '12'
pad dayN = case show dayN of
    [char] -> [' ', char]
    chars  -> chars

month :: Month -> DayOfWeek -> Int -> String
month month dayNameOf01 dayNumberOfLast =
    show month ++ " 2015\n" ++ header ++ (spaces Sunday)
    where
    header = "--------------------\nSu Mo Tu We Th Fr Sa\n"
    --
    spaces:: DayOfWeek -> String
    spaces currDay | dayNameOf01 == currDay = (days dayNameOf01 1)
                   | otherwise = "   " ++ spaces (nextDayOfWeek currDay)
    --
    days:: DayOfWeek -> Int -> String
    days Sunday    n | n > dayNumberOfLast = "\n"
    days _         n | n > dayNumberOfLast = "\n\n"
    days Saturday  n = pad n ++ "\n" ++ days Sunday (succ n)
    days day       n = pad n ++ " "  ++ days (nextDayOfWeek day) (succ n)

year :: String
year = (month January   Thursday  31)
    ++ (month February  Sunday    28)
    ++ (month March     Sunday    31)
    ++ (month April     Wednesday 30)
    ++ (month May       Friday    31)
    ++ (month June      Monday    30)
    ++ (month July      Wednesday 31)
    ++ (month August    Saturday  31)
    ++ (month September Tuesday   30)
    ++ (month October   Thursday  31)
    ++ (month November  Sunday    30)
    ++ (month December  Tuesday   31)

main :: IO ()
main = putStr year

Genes decoder

This app will translate input like GCUAGG to AlaArg.

Warning! App does not bother itself to properly check user input, so providing bad RNA will result in error.

 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
data RNA
    = A | U | C | G
    deriving (Read)

data AminoAcid
    = Ala | Cys | Asp | Glu | Phe | Gly | His | Ile | Lys | Leu
    | Met | Asn | Pro | Gln | Arg | Ser | Thr | Val | Trp | Tyr
    | Stop
    deriving (Show)

decode :: RNA -> RNA -> RNA -> AminoAcid 
decode U U U = Phe
decode U U C = Phe
decode U U A = Leu
decode U U G = Leu
decode U C _ = Ser
decode U A U = Tyr
decode U A C = Tyr
decode U A A = Stop
decode U A G = Stop
decode U G U = Cys
decode U G C = Cys
decode U G A = Stop
decode U G G = Trp
decode C U _ = Leu
decode C C _ = Pro
decode C A U = His
decode C A C = His
decode C A A = Gln
decode C A G = Gln
decode C G _ = Arg
decode A U U = Ile
decode A U C = Ile
decode A U A = Ile
decode A U G = Met
decode A C _ = Thr
decode A A U = Asn
decode A A C = Asn
decode A A A = Lys
decode A A G = Lys
decode A G U = Ser
decode A G C = Ser
decode A G A = Arg
decode A G G = Arg
decode G U _ = Val
decode G C _ = Ala
decode G A U = Asp
decode G A C = Asp
decode G A A = Glu
decode G A G = Glu
decode G G _ = Gly

decodeAll :: [RNA] -> [AminoAcid]
decodeAll (a:b:c:ds) = decode a b c : decodeAll ds
decodeAll  _         = []

main :: IO ()
main = do
    str <- getContents
    let rna :: [RNA]
        rna = map (\c -> read [c]) str
    let aminoAcids :: [AminoAcid]
        aminoAcids = decodeAll rna
    putStrLn (concatMap show aminoAcids)

Story generator

This app pre-generates 972 variants of story and then asks user to select one of them:

ghci> Enter a number from 0 to 971
user: 30

ghci>
There once was a princess who lived in a shoe.
They found a giant while while strolling along and immediately regretted it.
Then a magical pony named Pinkie Pie found them and saved the day.
The end.

 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
stories :: [String]
stories = do
    let str0 = "There once was "
    str1 <- ["a princess ", "a cat ", "a little boy "]
    let str2 = "who lived in "
    str3 <- ["a shoe.", "a castle.", "an enchanted forest."]
    --
    let str4 = "\nThey found a "
    str5 <- ["giant ", "frog ", "treasure chest " ]
    let str6 = "while "
    str7 <- ["when they got lost ", "while strolling along "]
    let str8 = "and immediately regretted it.\nThen a "
    --
    str9 <- ["lumberjack ", "wolf ", "magical pony "]
    let str10 = "named "
    str11 <- ["Pinkie Pie ", "Little John ", "Boris "]
    let str12 = "found them and "
    str13 <- ["saved the day.", "granted them three wishes."]
    let str14 = "\nThe end."
    --
    return (  str0
           ++ str1
           ++ str2
           ++ str3
           ++ str4
           ++ str5
           ++ str6
           ++ str7
           ++ str8
           ++ str9
           ++ str10
           ++ str11
           ++ str12
           ++ str13
           ++ str14
           )

main :: IO ()
main = do
    let len :: Int
        len = length stories
    -- or single-line: let len = length stories
    putStrLn ("Enter a number from 0 to " ++ show (len - 1))
    n <- readLn
    putStrLn ""
    putStrLn (stories !! n)

More

Later, homies