`
47 |
48 | ## 3. Code blocks
49 |
50 | Very similar to ``, but use the `
` tag. Call this function `code_`.
51 |
52 |
53 | ## Solutions
54 |
55 |
56 | Unordered lists
57 |
58 | ```hs
59 | ul_ :: [Structure] -> Structure
60 | ul_ =
61 | Structure . el "ul" . concat . map (el "li" . getStructureString)
62 | ```
63 |
64 |
65 |
66 |
67 |
68 | Ordered lists
69 |
70 | ```hs
71 | ol_ :: [Structure] -> Structure
72 | ol_ =
73 | Structure . el "ol" . concat . map (el "li" . getStructureString)
74 | ```
75 |
76 | Note: the two functions above could be unified.
77 |
78 |
79 |
80 |
81 |
82 | Code blocks
83 |
84 | ```hs
85 | code_ :: String -> Structure
86 | code_ = Structure . el "pre" . escape
87 | ```
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | - [About this book](./01-about.md)
4 | - [Hello world](./02-hello.md)
5 | - [Building an HTML printer library](./03-html_printer.md)
6 | - [Flexible HTML content (functions)](./03-html/01-html_content.md)
7 | - [Adding type signatures](./03-html/02-type_signatures.md)
8 | - [Embedded Domain Specific Languages](./03-html/03-edsls.md)
9 | - [Safer HTML construction with types](./03-html/04-safer_construction.md)
10 | - [Preventing incorrect use with modules](./03-html/05-modules.md)
11 | - [Escaping characters](./03-html/06-escaping_characters.md)
12 | - [Exposing internal functionality (Internal modules)](./03-html/07-internal_modules.md)
13 | - [Exercises](./03-html/08-exercises.md)
14 | - [Summary](./03-html/09-summary.md)
15 | - [Custom markup language](./04-markup.md)
16 | - [Representing the markup language as a Haskell data type](./04-markup/01-data_type.md)
17 | - [Parsing markup part 01 (Recursion)](./04-markup/02-parsing_01.md)
18 | - [Displaying the parsing results (type classes)](./04-markup/03-displaying_results.md)
19 | - [Parsing markup part 02 (Pattern matching)](./04-markup/04-parsing_02.md)
20 | - [Gluing things together](./05-glue.md)
21 | - [Converting Markup to HTML](./05-glue/01-markup_to_html.md)
22 | - [Working with IO](./05-glue/02-io.md)
23 | - [Defining a project description](./05-glue/03-project.md)
24 | - [Fancy options parsing](./05-glue/04-optparse.md)
25 | - [Handling errors and multiple files](./06-errors_and_files.md)
26 | - [Handling errors with Either](./06-errors_and_files/01-either.md)
27 | - [Either with IO?](./06-errors_and_files/02-except.md)
28 | - [Exceptions](./06-errors_and_files/03-exceptions.md)
29 | - [Let's code already!](./06-errors_and_files/04-implementation.md)
30 | - [Summary](./06-errors_and_files/05-summary.md)
31 | - [Passing an environment](./07-environment.md)
32 | - [Writing tests](./08-testing.md)
33 | - [Generating documentation](./09-documentation.md)
34 |
35 | - [Recap](./10-recap.md)
36 |
37 | ---
38 |
39 | [Where to go next](./11-next.md)
40 |
41 | [Frequently asked questions](./12-faq.md)
42 |
--------------------------------------------------------------------------------
/src/10-recap.md:
--------------------------------------------------------------------------------
1 | # Recap
2 |
3 | In this book, we've implemented a very simple static blog generator while learning Haskell as we go.
4 |
5 | - We've learned about basic Haskell building blocks, such as definitions, functions,
6 | types, modules, recursion, pattern matching, type classes, IO, and exceptions.
7 | - We've learned about [EDSLs](./03-html/03-edsls.html) and used the *combinator pattern* to implement
8 | a composable html generation library.
9 | - We've learned how to leverage types, modules, and smart constructors
10 | to [make invalid states unrepresentable](./03-html/04-safer_construction.html).
11 | - We've learned how to represent complex data using [ADTs](./04-markup/01-data_type.html).
12 | - We've learned how to use [pattern matching](./04-markup/04-parsing_02.html#pattern-matching) to transform ADTs,
13 | and how to use [recursion](./04-markup/02-parsing_01.html#recursion-and-accumulating-information) to solve problems.
14 | - We've used the *functional core, imperative shell* approach to build a program that handles IO and applies
15 | our domain logic to user inputs.
16 | - We've learned about abstractions such as [monoids](./05-glue/01-markup_to_html.html#monoids),
17 | [functors](./05-glue/04-optparse.html#functor) and [monads](./06-errors_and_files/01-either.html#monadic-interface),
18 | and how they can help us reuse code and convey information about shared interfaces.
19 | - We've learned how to create fancy [command-line interfaces](./05-glue/04-optparse.html), [write tests](./08-testing.html),
20 | and [generate documentation](./09-documentation.html).
21 |
22 | While Haskell is a very big and complex language, and there's always more to be learned,
23 | I think we've reached an important milestone where
24 | you can start building your own Haskell projects and be productive with Haskell!
25 |
26 | This is a good time to celebrate and pat yourself on the back for getting this far! Great job, you!
27 |
28 | If you'd like to learn even more about Haskell and continue your Haskell journey
29 | beyond this book, check out the appendix sections [Where to go next](./11-next.md) and the [FAQ](./12-faq.md).
30 |
31 | ## Thank you!
32 |
33 | Thank you for reading this book. I hope you enjoyed it and found Haskell interesting.
34 |
35 | I would very much like to hear your feedback. If you'd like, you could leave your
36 | feedback on this book's
37 | [discussion board](https://github.com/soupi/learn-haskell-blog-generator/discussions),
38 | or you could reach me via email.
39 | You can find my contact information [on my website](https://gilmi.me).
40 |
41 | If you liked this book, do let me know - your kind words mean a lot.
42 |
43 | > Finally, if you *really* liked this book and would like to support future passion projects
44 | > like it, you can [support me directly via Ko-fi](https://ko-fi.com/gilmi).
45 |
46 | Thank you, and good luck with your next Haskell project!
47 |
--------------------------------------------------------------------------------
/src/04-markup.md:
--------------------------------------------------------------------------------
1 | # Custom markup language
2 |
3 | In this chapter, we will define our own simple markup language
4 | and parse documents written in this language into Haskell data structures.
5 |
6 | Our markup language will contain the following features:
7 |
8 | - Headings: prefix by a number of `*` characters
9 | - Paragraphs: a group of lines without empty lines in between
10 | - Unordered lists: a group of lines, each prefixed with `- `
11 | - Ordered lists: a group of lines, each prefixed with `# `
12 | - Code blocks: a group of lines, each prefixed with `> `
13 |
14 | Here's a sample document:
15 |
16 | ```org
17 | * Compiling programs with ghc
18 |
19 | Running ghc invokes the Glasgow Haskell Compiler (GHC),
20 | and can be used to compile Haskell modules and programs into native
21 | executables and libraries.
22 |
23 | Create a new Haskell source file named hello.hs, and write
24 | the following code in it:
25 |
26 | > main = putStrLn "Hello, Haskell!"
27 |
28 | Now, we can compile the program by invoking ghc with the file name:
29 |
30 | > ➜ ghc hello.hs
31 | > [1 of 1] Compiling Main ( hello.hs, hello.o )
32 | > Linking hello ...
33 |
34 | GHC created the following files:
35 |
36 | - hello.hi - Haskell interface file
37 | - hello.o - Object file, the output of the compiler before linking
38 | - hello (or hello.exe on Microsoft Windows) - A native runnable executable.
39 |
40 | GHC will produce an executable when the source file satisfies both conditions:
41 |
42 | # Defines the main function in the source file
43 | # Defines the module name to be Main or does not have a module declaration
44 |
45 | Otherwise, it will only produce the .o and .hi files.
46 | ```
47 |
48 | which we will eventually convert into this (modulo formatting) HTML:
49 |
50 | ```html
51 | Compiling programs with ghc
52 |
53 | Running ghc invokes the Glasgow Haskell Compiler (GHC),
54 | and can be used to compile Haskell modules and programs into native
55 | executables and libraries.
56 |
57 |
58 | Create a new Haskell source file named hello.hs, and write
59 | the following code in it:
60 |
61 |
62 | main = putStrLn "Hello, Haskell!"
63 |
64 |
65 | Now, we can compile the program by invoking ghc with the file name:
66 |
67 |
68 | ➜ ghc hello.hs
69 | [1 of 1] Compiling Main ( hello.hs, hello.o )
70 | Linking hello ...
71 |
72 |
73 | GHC created the following files:
74 |
75 |
76 |
77 | - hello.hi - Haskell interface file
78 | - hello.o - Object file, the output of the compiler before linking
79 | - hello (or hello.exe on Microsoft Windows) - A native runnable executable.
80 |
81 |
82 | GHC will produce an executable when the source file satisfies both conditions:
83 |
84 |
85 |
86 | - Defines the main function in the source file
87 | - Defines the module name to be Main, or does not have a module declaration
88 |
89 |
90 | Otherwise, it will only produce the .o and .hi files.
91 |
92 | ```
93 |
--------------------------------------------------------------------------------
/src/03-html/03-edsls.md:
--------------------------------------------------------------------------------
1 | # Embedded Domain-Specific Languages
2 |
3 | Right off the bat, we run into a common pattern in Haskell: creating
4 | Embedded Domain-Specific Languages (EDSLs for short).
5 |
6 | Domain-specific languages (DSLs) are specialized programming languages that are
7 | tailored to specific domains, in contrast to general-purpose languages,
8 | which try to work well in many domains.
9 |
10 | A few examples of DSLs are:
11 |
12 | - make - for defining build systems
13 | - DOT - for defining graphs
14 | - Sed - for defining text transformations
15 | - CSS - for defining styling
16 | - HTML - for defining web pages
17 |
18 | An *embedded* domain-specific language is a little language that is
19 | embedded inside another programming language, making a program written in
20 | the EDSL a valid program in the language it was written in.
21 |
22 | The little HTML library we've been writing can be considered an EDSL.
23 | It is used specifically for building web pages (by returning HTML strings),
24 | and is valid Haskell code!
25 |
26 | In Haskell, we frequently create and use EDSLs to express domain-specific
27 | logic. We have EDSLs for concurrency, command-line options parsing, JSON and HTML,
28 | creating build systems, writing tests, and many more.
29 |
30 | Specialized languages are useful because they can solve specific problems in
31 | a concise (and often safe) way, and by embedding, we get to use the full power of
32 | the host language for our domain logic, including syntax highlighting and
33 | various tools available for the language.
34 |
35 | The drawback of embedding domain-specific languages is that we have to adhere to
36 | the rules of the programming language we embed in, such as syntactic and semantic rules.
37 |
38 | Some languages alleviate this drawback by providing meta-programming capabilities
39 | in the form of macros or other features to extend the language.
40 | And while Haskell does provide such capabilities as well, it is also expressive and concise
41 | enough that many EDSLs do not need them.
42 |
43 | Instead, many Haskell EDSLs use a pattern called _the combinator pattern_:
44 | They define *primitives* and *combinators* -
45 | primitives are basic building blocks of the language,
46 | and combinators are functions that combine primitives into more complex structures.
47 |
48 | In our HTML EDSL, our primitives are functions such as `html_` and `title_`
49 | that can be used to create a single HTML node, and we pass other
50 | constructed nodes as input to these functions, and combine them into a more complex
51 | structure with the append function `<>`.
52 |
53 | There are still a few tricks we can use to make our HTML EDSL better:
54 |
55 | 1. We can use Haskell's type system to make sure we only construct *valid*
56 | HTML, so for example, we don't create a `` node
57 | without a `` node or have user content that
58 | can include unescaped special characters,
59 | and throw a type error when the user tries to do something invalid.
60 |
61 | 2. Our HTML EDSL can move to its own module so it can be reused in multiple modules.
62 |
63 | In the next few sections, we'll take a look at how to define our own types and
64 | how to work with modules to make it harder to make errors, and a little bit
65 | about linked lists in Haskell.
66 |
--------------------------------------------------------------------------------
/src/03-html/09-summary.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | In this chapter, we built a very minimal HTML EDSL.
4 | We will later use this library to convert our custom markup formatted text to HTML.
5 |
6 | We've also learned about:
7 |
8 | - Defining and using functions
9 | - Types and type signatures
10 | - Embedded domain-specific languages
11 | - Chaining functions using the `.` operator
12 | - Preventing incorrect use with `newtype`s
13 | - Defining modules and the `Internal` module pattern
14 | - Encapsulation using `newtype`s and modules
15 |
16 | Here's our complete program up to this point:
17 |
18 | ```hs
19 | -- hello.hs
20 |
21 | import Html
22 |
23 | main :: IO ()
24 | main = putStrLn (render myhtml)
25 |
26 | myhtml :: Html
27 | myhtml =
28 | html_
29 | "My title"
30 | ( append_
31 | (h1_ "Heading")
32 | ( append_
33 | (p_ "Paragraph #1")
34 | (p_ "Paragraph #2")
35 | )
36 | )
37 | ```
38 |
39 | ```hs
40 | -- Html.hs
41 |
42 | module Html
43 | ( Html
44 | , Title
45 | , Structure
46 | , html_
47 | , h1_
48 | , p_
49 | , ul_
50 | , ol_
51 | , code_
52 | , append_
53 | , render
54 | )
55 | where
56 |
57 | import Html.Internal
58 | ```
59 |
60 | ```hs
61 | -- Html/Internal.hs
62 |
63 | module Html.Internal where
64 |
65 | -- * Types
66 |
67 | newtype Html
68 | = Html String
69 |
70 | newtype Structure
71 | = Structure String
72 |
73 | type Title
74 | = String
75 |
76 | -- * EDSL
77 |
78 | html_ :: Title -> Structure -> Html
79 | html_ title content =
80 | Html
81 | ( el "html"
82 | ( el "head" (el "title" (escape title))
83 | <> el "body" (getStructureString content)
84 | )
85 | )
86 |
87 | p_ :: String -> Structure
88 | p_ = Structure . el "p" . escape
89 |
90 | h1_ :: String -> Structure
91 | h1_ = Structure . el "h1" . escape
92 |
93 | ul_ :: [Structure] -> Structure
94 | ul_ =
95 | Structure . el "ul" . concat . map (el "li" . getStructureString)
96 |
97 | ol_ :: [Structure] -> Structure
98 | ol_ =
99 | Structure . el "ol" . concat . map (el "li" . getStructureString)
100 |
101 | code_ :: String -> Structure
102 | code_ = Structure . el "pre" . escape
103 |
104 | append_ :: Structure -> Structure -> Structure
105 | append_ c1 c2 =
106 | Structure (getStructureString c1 <> getStructureString c2)
107 |
108 | -- * Render
109 |
110 | render :: Html -> String
111 | render html =
112 | case html of
113 | Html str -> str
114 |
115 | -- * Utilities
116 |
117 | el :: String -> String -> String
118 | el tag content =
119 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
120 |
121 | getStructureString :: Structure -> String
122 | getStructureString content =
123 | case content of
124 | Structure str -> str
125 |
126 | escape :: String -> String
127 | escape =
128 | let
129 | escapeChar c =
130 | case c of
131 | '<' -> "<"
132 | '>' -> ">"
133 | '&' -> "&"
134 | '"' -> """
135 | '\'' -> "'"
136 | _ -> [c]
137 | in
138 | concat . map escapeChar
139 | ```
140 |
141 | > You can also [browse the code as a tree](https://github.com/soupi/learn-haskell-blog-generator/tree/2a4691de627bcb280e92f3d02a88d5404179dc86).
142 |
--------------------------------------------------------------------------------
/src/11-next.md:
--------------------------------------------------------------------------------
1 | # Where to go next
2 |
3 | Haskell is an incredibly rich and deep programming language.
4 | New cutting-edge techniques, concepts, and features are still being discovered
5 | and sometimes integrated into GHC. This sometimes makes it seemingly impossible
6 | to catch up to.
7 |
8 | This phenomena is sometimes dubbed
9 | [The Haskell pyramid](https://patrickmn.com/software/the-haskell-pyramid/).
10 | I hope that by reading this book and following the exercises,
11 | you readers have reached the bar of productivity, and you can now go and start
12 | working on your own projects with Haskell. I highly encourage you to do so.
13 | In my opinion, writing useful Haskell projects is the best method to solidify
14 | what you currently know and identify what you still need to learn.
15 |
16 | ## Extending this project
17 |
18 | If you'd like to extend this project, here are a few ideas for you:
19 |
20 | 1. **Serve over HTTP** - You can use a web library such as
21 | [wai](https://www.youtube.com/watch?v=mz5_HmLGRXc) or
22 | [twain](https://gilmi.me/blog/post/2022/04/24/learn-twain-bulletin-app)
23 | to serve this blog over HTTP instead of generating it statically
24 | 2. **Rewrite it with libraries** - you could rewrite it and use a real-world
25 | [HTML package](https://hackage.haskell.org/package/lucid)
26 | and [markdown parser](https://hackage.haskell.org/package/cmark-gfm)
27 | 3. **Add features**
28 | 1. You could add a metadata block at the top of each article
29 | which would include the title, publish date, and tags of a blog post,
30 | and use them when generating HTML, index page, and even tags pages
31 | 2. You could add HTML pages templating using
32 | [mustache](https://hackage.haskell.org/package/mustache) or similar,
33 | and use that to generate a nice and customizable structure to the page
34 | 3. You could add support for links and images in our markup language parser
35 | 4. You could add support for configuration files which would include things like
36 | the blog title, description, or other meta information for things like
37 | [twitter cards](https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards)
38 |
39 | Or anything else you can think of, consider this project your playground and
40 | do whatever you like with it!
41 |
42 | ## Other resources
43 |
44 | At some point, you are likely to run into new concepts, techniques,
45 | or even just a feeling of "I feel like this could be done better".
46 | I'd like to point you in the right direction so you can find additional information
47 | and learn new Haskell things when you need to or want to.
48 |
49 | I've compiled a list of resources for learning Haskell called
50 | [Haskell study plan](https://github.com/soupi/haskell-study-plan),
51 | which includes links to very useful articles, community hubs, and news aggregators,
52 | project suggestions, and even cool open-source Haskell projects.
53 | You will also find alternative explanations for things we've covered
54 | and even links to other Haskell tutorials, guides, and books in case you need
55 | a different view on things.
56 |
57 | Also, the [GHC User Guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/index.html)
58 | is a fantastic resource with loads of articles and information about the language and GHC tooling around it.
59 | It is often the best place to learn about the Haskell language.
60 |
61 | However, don't feel pressured to learn everything Haskell
62 | has to offer right away. Mastering Haskell is a journey that can take a lot of time.
63 | Most of us are definitely not there yet, but we can still be very productive with Haskell,
64 | build real-world projects, and even discover new techniques and concepts ourselves.
65 |
66 | Remember that in a lazy language, we evaluate things only when needed.
67 | Maybe we can do that too, with Haskell concepts!
68 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # learn-haskell-blog-generator
2 |
3 | This is the source repository for
4 | "Learn Haskell by building a blog generator",
5 | a project-oriented book about Haskell.
6 |
7 | To view the book, visit the [website](https://learn-haskell.blog).
8 |
9 | To report issues and ask questions,
10 | visit the [issue tracker](https://github.com/soupi/learn-haskell-blog-generator/issues).
11 |
12 | ## Code reference
13 |
14 |
15 |
16 |
17 | | Chapter |
18 | Code |
19 | Commit |
20 |
21 |
22 |
23 |
24 | | 3.9 - HTML EDSL |
25 | Tree |
26 | |
27 |
28 |
29 | | 4.4 - Markup parsing |
30 | Tree |
31 | Diff |
32 |
33 |
34 | | 5.1 - Markup to HTML conversion |
35 | Tree |
36 | Diff |
37 |
38 |
39 | | 5.2 - IO |
40 | Tree |
41 | Diff |
42 |
43 |
44 | | 5.3 - Package format |
45 | Tree |
46 | Diff |
47 |
48 |
49 | | 5.4 - CLI options parsing |
50 | Tree |
51 | Diff |
52 |
53 |
54 | | 6 - Richer HTML content |
55 | Tree |
56 | Diff |
57 |
58 |
59 | | 6.5 - Handling multiple files |
60 | Tree |
61 | Diff |
62 |
63 |
64 | | 7 - Environment |
65 | Tree |
66 | Diff |
67 |
68 |
69 | | 8 - Testing |
70 | Tree |
71 | Diff |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/02-hello.md:
--------------------------------------------------------------------------------
1 | # Hello, world!
2 |
3 | In this chapter, we will create a simple HTML "hello world" program and use the Haskell toolchain
4 | to compile and run it.
5 |
6 | > If you haven't installed a Haskell toolchain yet, visit
7 | > [haskell.org/downloads](https://haskell.org/downloads) for instructions on how to download
8 | > and install a Haskell toolchain.
9 |
10 | ## A Haskell source file
11 |
12 | A Haskell source file is composed of definitions.
13 |
14 | The most common type of definition has the following form:
15 |
16 | ```hs
17 | =
18 | ```
19 |
20 | Note that:
21 |
22 | 1. Names must start with a lowercase letter
23 | 2. We cannot use the same name more than once in a file
24 |
25 | A source file containing a definition of the name `main` can be treated as an executable,
26 | and the expression `main` is bound to is the entry point to the program.
27 |
28 | Let's create a new Haskell source file called `hello.hs` and write the following line there:
29 |
30 | ```hs
31 | main = putStrLn "Hello, world!"
32 | ```
33 |
34 | We've defined a new name, `main`, and bound it to the expression `putStrLn "Hello, world!"`.
35 |
36 | the body of `main` means calling the function `putStrLn` with the string `"Hello, world!"`
37 | as input. `putStrLn` takes a single string as input and prints that string to the standard output.
38 |
39 | __Note__: we don't need parenthesis to pass arguments to functions in Haskell.
40 |
41 | Running this program will result in the following text printed on the screen:
42 |
43 | ```
44 | Hello, world!
45 | ```
46 |
47 | Note that we cannot just write `putStrLn "Hello, world!"`
48 | without the `main =` part, because it is not a definition. This is something that is allowed
49 | in languages such as Python and OCaml, but not in Haskell or, for example, C.
50 |
51 | ## Compiling programs
52 |
53 | To run this little program, we can compile it using the command line program `ghc`:
54 |
55 | ```sh
56 | > ghc hello.hs
57 | [1 of 1] Compiling Main ( hello.hs, hello.o )
58 | Linking hello ...
59 | ```
60 |
61 | Invoking `ghc` with `hello.hs` will create the following artifact files:
62 |
63 | 1. `hello.o` - Object file
64 | 2. `hello.hi` - Haskell interface file
65 | 3. `hello` - A native executable file
66 |
67 | And after the compilation, we can run the `hello` executable:
68 |
69 | ```sh
70 | > ./hello
71 | Hello, world!
72 | ```
73 |
74 | ## Interpreting programs
75 |
76 | Alternatively, we can skip the compilation and creation of artifact files phase and run the source file directly
77 | using the command line program `runghc`:
78 |
79 | ```sh
80 | > runghc hello.hs
81 | Hello, world!
82 | ```
83 |
84 | We can also redirect the output of the program to a file and then open it in Firefox.
85 |
86 | ```sh
87 | > runghc hello.hs > hello.html
88 | > firefox hello.html
89 | ```
90 |
91 | This command should open Firefox and display a web page with `Hello, world!` written in it.
92 |
93 | I recommend using `runghc` with this tutorial. While compiling produces significantly faster programs,
94 | interpreting programs provides us with faster feedback while we are developing and making frequent changes.
95 |
96 | > If you want to learn more about the core Haskell tools, you can read
97 | > [this article](https://gilmi.me/blog/post/2021/08/14/hs-core-tools),
98 | > but what's described above is enough for our usage at the moment.
99 |
100 | ## More bindings
101 |
102 | We can define the HTML string passed to `putStrLn` in a new name instead of passing
103 | it directly to `putStrLn`. Change the content of the `hello.hs` file we defined above to:
104 |
105 | ```hs
106 | main = putStrLn myhtml
107 |
108 | myhtml = "Hello, world!"
109 | ```
110 |
111 | __Note__: the order in which we declare the bindings does not matter.
112 |
--------------------------------------------------------------------------------
/src/01-about.md:
--------------------------------------------------------------------------------
1 | # About this book
2 |
3 | > Looking for reviews and mentions? Click here.
4 |
5 |
10 |
11 |
16 |
17 | In this book, we will implement a simple static blog generator in Haskell,
18 | converting documents written in our own custom markup language to HTML.
19 |
20 | We will:
21 |
22 | 1. Implement a tiny HTML printer library
23 | 2. Define and parse our own custom markup language
24 | 3. Read files and glue things together
25 | 4. Add command line arguments parsing
26 | 5. Write tests and documentation
27 |
28 | In each chapter of the book, we will focus on a particular task we wish to achieve,
29 | and throughout the chapter, learn just enough Haskell to complete the task.
30 |
31 | ## Other ways to read this book
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
Do you prefer watching videos?
40 | Impure Pics made a video series based on this book!
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Do you prefer reading PDFs?
49 | An experimental PDF version is now available.
50 |
51 |
52 |
53 |
54 | ## Why should you read this book?
55 |
56 | There are many Haskell tutorials, guides, and books out there. Why read this one?
57 |
58 | ### Pros
59 |
60 | There are probably more, but here are a few possible pros:
61 |
62 | - It is **relatively short** - most Haskell books are hundreds of pages long.
63 | This book (when exported to PDF) is roughly 150 pages long.
64 | - It is **project oriented**. Many Haskell books teach Haskell by teaching the underlying
65 | concepts and features in a neat progression. In this book, we **build a Haskell program**
66 | and learn Haskell on the way. This will be a pro to some and a con to others.
67 | There are other tutorials like this. The most notable ones are
68 | [Beginning Haskell](https://www.apress.com/gp/book/9781430262510#otherversion=9781430262503)
69 | and [Haskell via Sokoban](https://haskell-via-sokoban.nomeata.de/).
70 | - It touches on **important topics** such as design patterns, testing, and documentation.
71 | - It has **many exercises** as well as **solutions** to those exercises.
72 | - It's **online**, which means corrections are easy to make.
73 | - It's **free**.
74 |
75 | ### Cons
76 |
77 | There are probably more, but here are a few possible cons:
78 |
79 | - It **may lack depth** - many, much longer Haskell tutorials are long because they go
80 | deeper into the nuts and bolts of each feature, and I tried to keep this book relatively short.
81 | - It **may not cover as many features or techniques** as other tutorials -
82 | we try to cover features as they pop up in our implementation, but we will
83 | probably miss features that aren't as important for our tasks,
84 | while other resources may try to cover many different use cases.
85 | - It **does not have a technical editor**, though it has seen quite a bit of editing.
86 |
87 | ### Other learning resources
88 |
89 | The [haskell.org/documentation](https://www.haskell.org/documentation/) page lists
90 | many tutorials, books, guides, and courses. You can find a few alternatives that I can
91 | recommend [in this list](https://github.com/soupi/haskell-study-plan#about-this-guide).
92 |
93 |
102 |
103 | ## Discussions
104 |
105 | Do you want to discuss the book? Maybe ask a question?
106 | Try the [discussion board](https://github.com/soupi/learn-haskell-blog-generator/discussions)!
107 |
108 |
--------------------------------------------------------------------------------
/src/03-html/05-modules.md:
--------------------------------------------------------------------------------
1 | # Preventing incorrect use with modules
2 |
3 | In this section, we will move the HTML generation library to its own module.
4 |
5 | ## Modules
6 |
7 | Each Haskell source file is a module. The module name should have the
8 | same name as the source file and start with a capital
9 | letter. Sub-directories should also be part of the name, and we use `.`
10 | to denote a sub-directory. We'll see that in the next section.
11 |
12 | The only exception to the rule are entry points to the program -
13 | modules with the name 'Main' that define `main` in them. Their source
14 | file names could have any name they want.
15 |
16 | A module declaration looks like this:
17 |
18 | ```hs
19 | module
20 | (
21 | )
22 | where
23 | ```
24 |
25 | The export list can be omitted if you want to export everything
26 | defined in the module, but we don't. We will list exactly the
27 | functions and types we want to export. This will give us control
28 | over how people can use our tiny library.
29 |
30 | We will create a new source file named `Html.hs` and add the following
31 | module declaration code at the top of the file:
32 |
33 | ```hs
34 | module Html
35 | ( Html
36 | , Title
37 | , Structure
38 | , html_
39 | , p_
40 | , h1_
41 | , append_
42 | , render
43 | )
44 | where
45 | ```
46 |
47 | Note that we do not export:
48 |
49 | 1. The constructors for our new types, only the types themselves.
50 | If we wanted to export the constructors as well, we would've written
51 | `Html(Html)` or `Html(..)`. This way the user cannot create their own
52 | `Structure` by writing `Structure "Hello"`.
53 |
54 | 2. Internal functions used by the library, such as `el` and `getStructureString`.
55 |
56 | And we will also move the HTML related functions from our `hello.hs` file
57 | to this new `Html.hs` file:
58 |
59 | ```hs
60 | newtype Html
61 | = Html String
62 |
63 | newtype Structure
64 | = Structure String
65 |
66 | type Title
67 | = String
68 |
69 | html_ :: Title -> Structure -> Html
70 | html_ title content =
71 | Html
72 | ( el "html"
73 | ( el "head" (el "title" title)
74 | <> el "body" (getStructureString content)
75 | )
76 | )
77 |
78 | p_ :: String -> Structure
79 | p_ = Structure . el "p"
80 |
81 | h1_ :: String -> Structure
82 | h1_ = Structure . el "h1"
83 |
84 | el :: String -> String -> String
85 | el tag content =
86 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
87 |
88 | append_ :: Structure -> Structure -> Structure
89 | append_ c1 c2 =
90 | Structure (getStructureString c1 <> getStructureString c2)
91 |
92 | getStructureString :: Structure -> String
93 | getStructureString content =
94 | case content of
95 | Structure str -> str
96 |
97 | render :: Html -> String
98 | render html =
99 | case html of
100 | Html str -> str
101 | ```
102 |
103 | Now, anyone importing our module (using the `import` statement
104 | below module declarations but above any other
105 | declaration), will only be able to import what we export.
106 |
107 | Add the following code at the top of the `hello.hs` file:
108 |
109 | ```hs
110 | import Html
111 | ```
112 |
113 | The `hello.hs` file should now look like this:
114 |
115 | ```hs
116 | -- hello.hs
117 |
118 | import Html
119 |
120 | main :: IO ()
121 | main = putStrLn (render myhtml)
122 |
123 | myhtml :: Html
124 | myhtml =
125 | html_
126 | "My title"
127 | ( append_
128 | (h1_ "Heading")
129 | ( append_
130 | (p_ "Paragraph #1")
131 | (p_ "Paragraph #2")
132 | )
133 | )
134 | ```
135 |
136 | And the `Html.hs` file should look like this:
137 |
138 | ```hs
139 | -- Html.hs
140 |
141 | module Html
142 | ( Html
143 | , Title
144 | , Structure
145 | , html_
146 | , p_
147 | , h1_
148 | , append_
149 | , render
150 | )
151 | where
152 |
153 | newtype Html
154 | = Html String
155 |
156 | newtype Structure
157 | = Structure String
158 |
159 | type Title
160 | = String
161 |
162 | html_ :: Title -> Structure -> Html
163 | html_ title content =
164 | Html
165 | ( el "html"
166 | ( el "head" (el "title" title)
167 | <> el "body" (getStructureString content)
168 | )
169 | )
170 |
171 | p_ :: String -> Structure
172 | p_ = Structure . el "p"
173 |
174 | h1_ :: String -> Structure
175 | h1_ = Structure . el "h1"
176 |
177 | el :: String -> String -> String
178 | el tag content =
179 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
180 |
181 | append_ :: Structure -> Structure -> Structure
182 | append_ c1 c2 =
183 | Structure (getStructureString c1 <> getStructureString c2)
184 |
185 | getStructureString :: Structure -> String
186 | getStructureString content =
187 | case content of
188 | Structure str -> str
189 |
190 | render :: Html -> String
191 | render html =
192 | case html of
193 | Html str -> str
194 | ```
195 |
196 | > As an aside, you might have noticed that I've decided to suffix the functions used to
197 | > construct HTML values with an underscore (`_`). This is mostly an aesthetic decision which,
198 | > in my opinion, makes the EDSL easier to recognize,
199 | > but it is also useful to avoid name clashes with
200 | > functions defined in the Haskell standard library, such as `head`.
201 | > I took this idea from a Haskell HTML library named `lucid`!
202 |
--------------------------------------------------------------------------------
/src/12-faq.md:
--------------------------------------------------------------------------------
1 | # Frequently asked questions
2 |
3 | > Got a question? You can ask in the [discussion board](https://github.com/soupi/learn-haskell-blog-generator/discussions) or the [issue tracker](https://github.com/soupi/learn-haskell-blog-generator/issues)!
4 |
5 | ## General questions
6 |
7 | ### Why should I learn Haskell
8 |
9 | I've written a couple of articles on the topic:
10 |
11 | - [Consider Haskell](https://gilmi.me/blog/post/2020/04/28/consider-haskell) (Alternative title, 'What can I do with Haskell?')
12 | - [7 things I learned from Haskell](https://gilmi.me/blog/post/2022/12/13/learned-from-haskell)
13 |
14 | ### How to install editor tools
15 |
16 | As far as I know, the most recommended setup today for Haskell development is using
17 | VSCode or [VSCodium](https://vscodium.com/) together with the
18 | marketplace [Haskell extension](https://marketplace.visualstudio.com/items?itemName=haskell.haskell).
19 |
20 | The Haskell extension uses [haskell-language-server](https://github.com/haskell/haskell-language-server)
21 | which can be installed via [GHCup](https://www.haskell.org/ghcup/) or even via the Haskell extension itself.
22 |
23 | If you already have a preferred editor,
24 | [see if HLS supports it](https://haskell-language-server.readthedocs.io/en/latest/configuration.html#configuring-your-editor),
25 | or alternatively use [GHCid](https://github.com/ndmitchell/ghcid#readme)
26 | which provides rapid feedback independently from an editor.
27 |
28 | ### How to learn new things
29 |
30 | The Haskell community keeps marching forward, developing new libraries, tools, and techniques
31 | as well as creating new material for older concepts.
32 | The [Haskell planetarium](https://haskell.pl-a.net) aggregates feeds from several communities into
33 | one page, as well as a [Haskell Weekly newsletter](https://haskellweekly.news/).
34 | You might also find quite a bit of Haskell presence on the
35 | [Fediverse](https://fosstodon.org/tags/haskell)!
36 |
37 | ## Debugging
38 |
39 | ### How to debug Haskell code
40 |
41 | Most imperative languages provide a step debugger. While the
42 | [GHCi debugger](https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html#the-ghci-debugger),
43 | exists, it is not particularly easy to use, especially because of Haskell's lazy evaluation, where things
44 | might not be evaluated in the order we might intuitively expect. Because of that,
45 | Haskellers tend to use
46 | [trace debugging](https://hackage.haskell.org/package/base-4.16.4.0/docs/Debug-Trace.html#g:1) and
47 | equational reasoning. With trace debugging, we try to *verify our assumptions* about the code -
48 | we use the various `trace` functions as a "hack" to print variables, functions inputs, functions output
49 | or even just say "got here" from anywhere in the code.
50 |
51 | After finding something that does not match our assumptions, such as unexpected input or output
52 | of a function, we try to think what piece of code could be responsible for the discrepancy or even use
53 | trace debugging again to pinpoint the exact location, and try to use "equational reasoning" to
54 | evaluate the offending code that betrayed our expectations. If it's easy to do, we try running
55 | the function in `ghci` with different inputs to check our assumptions as well.
56 |
57 | Because Haskell focuses on immutability, composability, and using types to eliminate many
58 | classes of possible errors, "local reasoning" becomes possible, and trace debugging
59 | becomes a viable strategy for debugging Haskell programs.
60 |
61 | ### How to understand type errors
62 |
63 | GHC type errors are often not the most friendly error messages, but they mean well! They are just
64 | trying to help us find inconsistencies in our code - often with regard to type usage, they help us
65 | avoid making errors.
66 |
67 | When you run into error messages, start by reading them carefully
68 | until you get used to them, and then the offending code hinted at by the error message.
69 | As you gain experience, it is likely that the most important part of an error will be the location
70 | of the offending code, and by reading the code, we can find the error without the actual error message.
71 |
72 | Adding type signatures and annotations to test your understanding of the types also helps greatly.
73 | We can even ask GHC for the expected type in a certain place by using
74 | [typed holes](https://downloads.haskell.org/ghc/latest/docs/users_guide/exts/typed_holes.html).
75 |
76 | ### My program is slow. Why?
77 |
78 | There could be various reasons. From inefficient algorithms or
79 | [unsuited data structures](https://github.com/soupi/haskell-study-plan#data-structures) for the task
80 | in terms of time complexity of the common operations, to less efficient memory representations
81 | (this is another reminder to use `Text` over `String` in most cases),
82 | and laziness issues (again, the evaluation strategy!).
83 |
84 | The [performance section](https://github.com/soupi/haskell-study-plan#performance) in my Haskell
85 | study plan links to various resources on Haskell evaluation, profiling, and case studies.
86 |
87 | ## Design
88 |
89 | ### How to structure programs
90 |
91 | Start with the imperative shell functional core approach, define EDSLs with the combinator
92 | pattern for logic if needed, use capabilities such as `State` locally if needed,
93 | maybe add an environment configuration with `ReaderT`, and see how it goes.
94 |
95 | If that approach fails you, look at why it fails and examine other solutions according to your needs.
96 |
97 | ### How to model data
98 |
99 | Modeling data using ADTs are usually the way to go. Often programmers coming from object-oriented
100 | background tend to look at type classes as a way to define methods similar to inheritance,
101 | but this often isn't the right approach, and ADTs with different constructors for different alternatives
102 | go a long way. Remember that even OOP people often preach for composition over inheritance.
103 |
104 | Use functions to define behavior on data rather than trying to couple the two together.
105 |
--------------------------------------------------------------------------------
/src/03-html/07-internal_modules.md:
--------------------------------------------------------------------------------
1 | # Exposing internal functionality (Internal modules)
2 |
3 | We have now built a very small but convenient and safe way to write
4 | HTML code in Haskell. This is something that we could (potentially)
5 | publish as a *library* and share with the world by uploading it
6 | to a package repository such as [Hackage](https://hackage.haskell.org/).
7 | Users interested in our library could use a package manager
8 | to include it in their project and build their own HTML pages.
9 |
10 | It is important to note that users are building their projects against
11 | the API that we expose to them, and the package manager doesn't generally
12 | provide access to the source code, so they can't, for example,
13 | modify the `Html` module (that we expose) in their project directly
14 | without jumping through some hoops.
15 |
16 | Because we wanted our `Html` EDSL to be safe, we **hid the internal
17 | implementation from the user**, and the only way to interact with the
18 | library is via the API we provide.
19 |
20 | This provides the safety we wanted to provide, but in this case, it also
21 | *blocks* the user from extending our library *in their own project* with
22 | things we haven't implemented yet, such as lists or code blocks.
23 |
24 | When a user runs into trouble with a library (such as missing features)
25 | the best course of action usually is to open an issue in the repository or
26 | submit a pull request, but sometimes the user needs things to work *now*.
27 |
28 | We admit that we are not perfect and can't think of all use cases for our
29 | library. Sometimes the restrictions we add are too great and may limit
30 | the usage of advanced users who know how things work under the hood and
31 | need certain functionality to use our library.
32 |
33 | ### Internal modules
34 |
35 | For that, we can expose internal modules to provide some flexibility for
36 | advanced users. Internal modules are not a language concept but
37 | rather a (fairly common) design pattern (or idiom) in Haskell.
38 |
39 | Internal modules are simply modules named `.Internal`,
40 | which export all of the functionality and implementation details in that module.
41 |
42 | Instead of writing the implementation in (for example) the `Html` module,
43 | we write it in the `Html.Internal` module, which will export everything.
44 | Then we will import that module in the `Html` module and write an explicit export list
45 | to only export the API we'd like to export (as before).
46 |
47 | `Internal` modules are considered unstable and risky to use by convention.
48 | If you end up using one yourself when using an external Haskell library,
49 | make sure to open a ticket in the library's repository after the storm has passed!
50 |
51 | ### Let's make the changes
52 |
53 | We will create a new directory named `Html` and inside it a new file
54 | named `Internal.hs`. The name of this module should be `Html.Internal`.
55 |
56 | This module will contain all of the code we previously had in the `Html`
57 | module, but **we will change the module declaration in `Html.Internal`
58 | and _omit_ the export list**:
59 |
60 | ```hs
61 | -- Html/Internal.hs
62 |
63 | module Html.Internal where
64 |
65 | ...
66 | ```
67 |
68 | And now in `Html.hs`, we will remove the code that we moved to `Html/Internal.hs`
69 | and in its stead we'll import the internal module:
70 |
71 | ```hs
72 | -- Html.hs
73 |
74 | module Html
75 | ( Html
76 | , Title
77 | , Structure
78 | , html_
79 | , p_
80 | , h1_
81 | , append_
82 | , render
83 | )
84 | where
85 |
86 | import Html.Internal
87 | ```
88 |
89 | Now, users of our library can still import `Html` and safely use our library,
90 | but if they run into trouble and have a dire need to implement unordered lists
91 | to work with our library, they could always work with `Html.Internal` instead.
92 |
93 |
94 | Our revised Html.hs and Html/Internal.hs
95 |
96 | ```hs
97 | -- Html.hs
98 |
99 | module Html
100 | ( Html
101 | , Title
102 | , Structure
103 | , html_
104 | , p_
105 | , h1_
106 | , append_
107 | , render
108 | )
109 | where
110 |
111 | import Html.Internal
112 | ```
113 |
114 | ```hs
115 | -- Html/Internal.hs
116 |
117 | module Html.Internal where
118 |
119 | -- * Types
120 |
121 | newtype Html
122 | = Html String
123 |
124 | newtype Structure
125 | = Structure String
126 |
127 | type Title
128 | = String
129 |
130 | -- * EDSL
131 |
132 | html_ :: Title -> Structure -> Html
133 | html_ title content =
134 | Html
135 | ( el "html"
136 | ( el "head" (el "title" (escape title))
137 | <> el "body" (getStructureString content)
138 | )
139 | )
140 |
141 | p_ :: String -> Structure
142 | p_ = Structure . el "p" . escape
143 |
144 | h1_ :: String -> Structure
145 | h1_ = Structure . el "h1" . escape
146 |
147 | append_ :: Structure -> Structure -> Structure
148 | append_ c1 c2 =
149 | Structure (getStructureString c1 <> getStructureString c2)
150 |
151 | -- * Render
152 |
153 | render :: Html -> String
154 | render html =
155 | case html of
156 | Html str -> str
157 |
158 | -- * Utilities
159 |
160 | el :: String -> String -> String
161 | el tag content =
162 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
163 |
164 | getStructureString :: Structure -> String
165 | getStructureString content =
166 | case content of
167 | Structure str -> str
168 |
169 | escape :: String -> String
170 | escape =
171 | let
172 | escapeChar c =
173 | case c of
174 | '<' -> "<"
175 | '>' -> ">"
176 | '&' -> "&"
177 | '"' -> """
178 | '\'' -> "'"
179 | _ -> [c]
180 | in
181 | concat . map escapeChar
182 | ```
183 |
184 |
185 |
186 |
187 | ## Summary
188 |
189 | For our particular project, `Internal` modules aren't necessary.
190 | Because our project and the source code for the HTML EDSL are
191 | part of the same project, and we have access to the `Html`
192 | module directly, we can always go and edit it if we want
193 | (and we are going to do that throughout the book).
194 |
195 | However, if we were planning to release our HTML EDSL as a *library*
196 | for other developers to use, it would be nice
197 | to also expose the internal implementation as an `Internal`
198 | module. Just so we can save some trouble for potential users!
199 |
200 | In a later chapter, we will see how to create a package from our source code.
201 |
--------------------------------------------------------------------------------
/src/04-markup/03-displaying_results.md:
--------------------------------------------------------------------------------
1 | # Displaying the parsing results (type classes)
2 |
3 | We want to be able to print a textual representation of values
4 | of our `Document` type. There are a few ways to do that:
5 |
6 | 1. Write our own function of type `Document -> String`, which we could then print, or
7 | 2. Have Haskell write one for us
8 |
9 | Haskell provides us with a mechanism that can automatically generate the implementation of a
10 | *type class* function called `show`, that will convert our type to `String`.
11 |
12 | The type of the function `show` looks like this:
13 |
14 | ```hs
15 | show :: Show a => a -> String
16 | ```
17 |
18 | This is something new we haven't seen before. Between `::` and `=>`
19 | you see what is called a __type class constraint__ on the type `a`. What
20 | we say in this signature is that the function `show` can work on any
21 | type that is a member of the type class `Show`.
22 |
23 | Type classes is a feature in Haskell that allows us to declare a common
24 | interface for different types. In our case, Haskell's standard library
25 | defines the type class `Show` in the following way (this is a simplified
26 | version but good enough for our purposes):
27 |
28 | ```hs
29 | class Show a where
30 | show :: a -> String
31 | ```
32 |
33 | A type class declaration describes a common interface for Haskell types.
34 | `show` is an overloaded function that will work for any type that is an *instance*
35 | of the type class `Show`.
36 | We can define an instance of a type class manually like this:
37 |
38 | ```hs
39 | instance Show Bool where
40 | show x =
41 | case x of
42 | True -> "True"
43 | False -> "False"
44 | ```
45 |
46 | Defining an instance means providing an implementation for the interface of a specific type.
47 | When we call the function `show` on a data type, the compiler will search the type's `Show` instance,
48 | and use the implementation provided in the instance declaration.
49 |
50 | ```hs
51 | ghci> show True
52 | "True"
53 | ghci> show 187
54 | "187"
55 | ghci> show "Hello"
56 | "\"Hello\""
57 | ```
58 |
59 | As seen above, the `show` function converts a value to its textual representation.
60 | That is why `"Hello"` includes the quotes as well. The `Show` type class is usually
61 | used for debugging purposes.
62 |
63 | ## Deriving instances
64 |
65 | It is also possible to automatically generate implementations of a few selected
66 | type classes. Fortunately, `Show` is one of them.
67 |
68 | If all the types in the definition of our data type already implement
69 | an instance of `Show`, we can *automatically derive* it by adding `deriving Show` at the
70 | end of the data definition.
71 |
72 | ```hs
73 | data Structure
74 | = Heading Natural String
75 | | Paragraph String
76 | | UnorderedList [String]
77 | | OrderedList [String]
78 | | CodeBlock [String]
79 | deriving Show
80 | ```
81 |
82 | Now we can use the function `show :: Show a => a -> String` for any
83 | type that implements an instance of the `Show` type class. For example, with `print`:
84 |
85 | ```hs
86 | print :: Show a => a -> IO ()
87 | print = putStrLn . show
88 | ```
89 |
90 | We can first convert our type to `String` and then write it to the
91 | standard output.
92 |
93 | And because lists also implement `Show` for any element type that has
94 | a `Show` instance, we can now print `Document`s, because they are just
95 | aliases for `[Structure]`. Try it!
96 |
97 | There are many type classes Haskellers use everyday. A couple more are
98 | `Eq` for equality and `Ord` for ordering. These are also special type classes
99 | that can be derived automatically.
100 |
101 | ## Laws
102 |
103 | Type classes often come with "rules" or "laws" that instances should satisfy,
104 | the purpose of these laws is to provide *predictable behaviour* across
105 | instances, so that when we run into a new instance we can be confident
106 | that it will behave in an expected way, and we can write code
107 | that works generically for all instances of a type class while expecting
108 | them to adhere to these rules.
109 |
110 | As an example, let's look at the `Semigroup` type class:
111 |
112 | ```hs
113 | class Semigroup a where
114 | (<>) :: a -> a -> a
115 | ```
116 |
117 | This type class provides a common interface for types with an operation `<>`
118 | that can combine two values into one in some way.
119 |
120 | This type class also mentions that this `<>` operation should be associative,
121 | meaning that these two sides should evaluate to the same result:
122 |
123 | ```
124 | x <> (y <> z) = (x <> y) <> z
125 | ```
126 |
127 | An example of a lawful instance of `Semigroup` is lists with the append operation (`++`):
128 |
129 | ```hs
130 | instance Semigroup [a] where
131 | (<>) = (++)
132 | ```
133 |
134 | Unfortunately, the Haskell type system cannot "prove" that instances
135 | satisfy these laws, but as a community, we often shun unlawful instances.
136 |
137 | Many data types (together with their respective operations) can
138 | form a `Semigroup`, and instances
139 | don't even have to look similar or have a common analogy/metaphor
140 | (and this is true for many other type classes as well).
141 |
142 | **Type classes are often just _interfaces_ with _laws_** (or expected behaviours if you will).
143 | Approaching them with this mindset can be very liberating!
144 |
145 | To put it differently, **type classes can be used to create abstractions** -
146 | interfaces with laws/expected behaviours where we don't actually care about the
147 | concrete details of the underlying type, just that it *implements a certain
148 | API and behaves in a certain way*.
149 |
150 | Regarding `Semigroup`, we have [previously](../03-html/04-safer_construction.html#appending-htmlstructure)
151 | created a function that looks like `<>` for our `Html` EDSL!
152 | We can add a `Semigroup` instance for our `Structure` data type
153 | and have a nicer API!
154 |
155 | ---
156 |
157 | Exercise: Please do this and remove the `append_` function from the API.
158 |
159 |
160 | Solution
161 |
162 | Replace this:
163 |
164 | ```hs
165 | append_ :: Structure -> Structure -> Structure
166 | append_ c1 c2 =
167 | Structure (getStructureString c1 <> getStructureString c2)
168 | ```
169 |
170 | With this:
171 |
172 | ```hs
173 | instance Semigroup Structure where
174 | (<>) c1 c2 =
175 | Structure (getStructureString c1 <> getStructureString c2)
176 | ```
177 |
178 | And remove the export of `append_` in `Html.hs`. You won't need to further export anything
179 | as type class instances are exported automatically.
180 |
181 | You will also need to replace the usage of `append_` with `<>` in `hello.hs`.
182 |
183 |
184 |
185 | ---
186 |
--------------------------------------------------------------------------------
/src/06-errors_and_files/02-except.md:
--------------------------------------------------------------------------------
1 | # Either with IO?
2 |
3 | When we create `IO` actions that may require I/O, we risk running into all kinds of errors.
4 | For example, when we use `writeFile`, we could run out of disk space in the middle of writing,
5 | or the file might be write-protected. While these scenarios aren't super common, they are definitely
6 | possible.
7 |
8 | We could've potentially encoded Haskell functions like `readFile` and `writeFile` as `IO` operations
9 | that return `Either`, for example:
10 |
11 | ```hs
12 | readFile :: FilePath -> IO (Either ReadFileError String)
13 | writeFile :: FilePath -> String -> IO (Either WriteFileError ())
14 | ```
15 |
16 | However, there are a couple of issues here; the first is that composing `IO` actions
17 | becomes more difficult. Previously we could write:
18 |
19 | ```hs
20 | readFile "input.txt" >>= writeFile "output.html"
21 | ```
22 |
23 | But now the types no longer match - `readFile` will return an `Either ReadFileError String` when executed,
24 | but `writeFile` wants to take a `String` as input. We are forced to handle the error
25 | before calling `writeFile`.
26 |
27 | ## Composing IO + Either using ExceptT
28 |
29 | One way to handle this is by using **monad transformers**. Monad transformers provide a way
30 | to stack monad capabilities on top of one another. They are called transformers because
31 | **they take a type that has an instance of monad as input and return a new type that
32 | implements the monad interface, stacking a new capability on top of it**.
33 |
34 | For example, if we want to compose values of a type that is equivalent to `IO (Either Error a)`,
35 | using the monadic interface (the function `>>=`), we can use a monad transformer
36 | called [`ExceptT`](https://hackage.haskell.org/package/mtl-2.3.1/docs/Control-Monad-Except.html#g:2)
37 | and stack it over `IO`.
38 | Let's see how `ExceptT` is defined:
39 |
40 | ```hs
41 | newtype ExceptT e m a = ExceptT (m (Either e a))
42 | ```
43 |
44 | Remember, a `newtype` is a new name for an existing type. And if we substitute
45 | `e` with `Error` and `m` with `IO`, we get exactly `IO (Either Error a)` as we wanted.
46 | And we can convert an `ExceptT Error IO a` into `IO (Either Error a)` using
47 | the function `runExceptT`:
48 |
49 | ```hs
50 | runExceptT :: ExceptT e m a -> m (Either e a)
51 | ```
52 |
53 | `ExceptT` implements the monadic interface in a way that combines the capabilities of
54 | `Either`, and whatever `m` it takes. Because `ExceptT e m` has a `Monad` instance,
55 | a specialized version of `>>=` would look like this:
56 |
57 | ```hs
58 | -- Generalized version
59 | (>>=) :: Monad m => m a -> (a -> m b) -> m b
60 |
61 | -- Specialized version, replace the `m` above with `ExceptT e m`.
62 | (>>=) :: Monad m => ExceptT e m a -> (a -> ExceptT e m b) -> ExceptT e m b
63 | ```
64 |
65 | Note that the `m` in the specialized version still needs to be an instance of `Monad`.
66 |
67 | ---
68 |
69 | Unsure how this works? Try to implement `>>=` for `IO (Either Error a)`:
70 |
71 | ```hs
72 | bindExceptT :: IO (Either Error a) -> (a -> IO (Either Error b)) -> IO (Either Error b)
73 | ```
74 |
75 | Solution
76 |
77 | ```hs
78 | bindExceptT :: IO (Either Error a) -> (a -> IO (Either Error b)) -> IO (Either Error b)
79 | bindExceptT mx f = do
80 | x <- mx -- `x` has the type `Either Error a`
81 | case x of
82 | Left err -> pure (Left err)
83 | Right y -> f y
84 | ```
85 |
86 | Note that we didn't actually use the implementation details of `Error` or `IO`,
87 | `Error` isn't mentioned at all, and for `IO`, we only used the monadic interface with
88 | the do notation. We could write the same function with a more generalized type signature:
89 |
90 | ```hs
91 | bindExceptT :: Monad m => m (Either e a) -> (a -> m (Either e b)) -> m (Either e b)
92 | bindExceptT mx f = do
93 | x <- mx -- `x` has the type `Either e a`
94 | case x of
95 | Left err -> pure (Left err)
96 | Right y -> f y
97 | ```
98 |
99 | And because `newtype ExceptT e m a = ExceptT (m (Either e a))`, we can just
100 | pack and unpack that `ExceptT` constructor and get:
101 |
102 |
103 | ```hs
104 | bindExceptT :: Monad m => ExceptT e m a -> (a -> ExceptT e m b) -> ExceptT e m b
105 | bindExceptT mx f = ExceptT $ do
106 | -- `runExceptT mx` has the type `m (Either e a)`
107 | -- `x` has the type `Either e a`
108 | x <- runExceptT mx
109 | case x of
110 | Left err -> pure (Left err)
111 | Right y -> runExceptT (f y)
112 | ```
113 |
114 |
115 |
116 | ---
117 |
118 | > Note that when stacking monad transformers, the order in which we stack them matters.
119 | > With `ExceptT Error IO a`, we have an `IO` operation that, when run will return `Either`
120 | > an error or a value.
121 |
122 | `ExceptT` can enjoy both worlds - we can return error values using the function `throwError`:
123 |
124 | ```hs
125 | throwError :: e -> ExceptT e m a
126 | ```
127 |
128 | and we can "lift" functions that return a value of the underlying monadic type `m` to return
129 | a value of `ExceptT e m a` instead:
130 |
131 | ```hs
132 | lift :: m a -> ExceptT e m a
133 | ```
134 |
135 | for example:
136 |
137 | ```hs
138 | getLine :: IO String
139 |
140 | lift getLine :: ExceptT e IO String
141 | ```
142 |
143 | > Actually, `lift` is also a type class function from `MonadTrans`, the type class
144 | > of monad transformers. So technically, `lift getLine :: MonadTrans t => t IO String`,
145 | > but we are specializing for concreteness.
146 |
147 |
148 | Now, if we had:
149 |
150 | ```hs
151 | readFile :: FilePath -> ExceptT IOError IO String
152 |
153 | writeFile :: FilePath -> String -> ExceptT IOError IO ()
154 | ```
155 |
156 | We could compose them again without issue:
157 |
158 | ```hs
159 | readFile "input.txt" >>= writeFile "output.html"
160 | ```
161 |
162 | But remember - the error type `e` (in both the case `Either` and `Except`)
163 | must be the same between composed functions! This means that the type representing
164 | errors for both `readFile` and `writeFile` must be the same - that would also
165 | force anyone using these functions to handle these errors - should a user who
166 | called `writeFile` be required to handle a "file not found" error? Should a user
167 | who called `readFile` be required to handle an "out of disk space" error?
168 | There are many, many more possible IO errors! "network unreachable", "out of memory",
169 | "cancelled thread", we cannot require a user to handle all these errors, or
170 | even cover them all in a data type.
171 |
172 | So what do we do?
173 |
174 | We give up on this approach **for IO code**, and use a different one: Exceptions,
175 | as we'll see in the next chapter.
176 |
177 | > Note - when we stack `ExceptT` on top of a different type called
178 | > [`Identity`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-Functor-Identity.html)
179 | > that also implements the `Monad` interface, we get a type that is exactly like `Either`
180 | > called [`Except`](https://hackage.haskell.org/package/transformers-0.6.0.2/docs/Control-Monad-Trans-Except.html#t:Except)
181 | > (without the `T` at the end). You might sometimes want to use `Except` instead of `Either`
182 | > because it has a more appropriate name and better API for error handling than `Either`.
183 |
--------------------------------------------------------------------------------
/src/03-html/01-html_content.md:
--------------------------------------------------------------------------------
1 | # Flexible HTML content (functions)
2 |
3 | We'd like to be able to write different HTML pages without having to write the whole
4 | structure of HTML and body tags over and over again. We can do that with functions.
5 |
6 | To define a function, we create a definition as we saw previously and add the argument
7 | names after the name and before the equals sign (`=`).
8 | So a function definition has the following form:
9 |
10 | ```hs
11 | ... =
12 | ```
13 |
14 | The argument names will be available in scope on the right side of the equals sign
15 | (in the ``), and the function name will be ``.
16 |
17 | We'll define a function that takes a string, which is the content of the page, and wraps it in
18 | the relevant `html` and `body` tags by concatenating them before and after the content.
19 | We use the operator `<>` to concatenate two strings.
20 |
21 | ```hs
22 | wrapHtml content = "" <> content <> ""
23 | ```
24 |
25 | This function, `wrapHtml`, takes one argument named `content` and returns a string
26 | that prefixes `` before the content and appends `` after it.
27 | Note that it is common to use camelCase in Haskell for names.
28 |
29 | Now we can adjust our `myhtml` definition from the previous chapter:
30 |
31 | ```hs
32 | myhtml = wrapHtml "Hello, world!"
33 | ```
34 |
35 | Again, notice that we don't need parenthesis when calling functions. Function calls have the form:
36 |
37 | ```hs
38 | ...
39 | ```
40 |
41 | However, if we wanted to substitute `myhtml` with the expression `myhtml` is bound
42 | to in `main = putStrLn myhtml`, we would have to wrap the expression in parenthesis:
43 |
44 | ```hs
45 | main = putStrLn (wrapHtml "Hello, world!")
46 | ```
47 |
48 | If we accidentally write this instead:
49 |
50 | ```hs
51 | main = putStrLn wrapHtml "Hello, world!"
52 | ```
53 |
54 |
55 | we'll get an error from GHC stating that `putStrLn` is applied to two arguments,
56 | but it only takes one. This is because the above is of the form ` `
57 | in which, as we defined earlier, `` and `` are arguments to ``.
58 |
59 | Using parenthesis, we can group the expressions together in the correct order.
60 |
61 | > #### An aside about operator precedence and fixity
62 | >
63 | > operators (like `<>`) are infix functions that take two arguments - one from each side.
64 | >
65 | > When there are multiple operators in the same expression without parenthesis, the operator
66 | > *fixity* (left or right) and *precedence* (a number between 0 and 10) determine which
67 | > operator binds more tightly.
68 | >
69 | > In our case, `<>` has *right* fixity, so Haskell adds an invisible parenthesis on the right side
70 | > of `<>`. So, for example:
71 | >
72 | > ```hs
73 | > "" <> content <> ""
74 | > ```
75 | >
76 | > is viewed by Haskell as:
77 | >
78 | > ```hs
79 | > "" <> (content <> "")
80 | > ```
81 | >
82 | > For an example of precedence, in the expression `1 + 2 * 3`,
83 | > the operator `+` has precedence 6, and the operator `*` has precedence 7,
84 | > so we give precedence to `*` over `+`. Haskell will view this expression as:
85 | >
86 | > ```hs
87 | > 1 + (2 * 3)
88 | > ```
89 | >
90 | > You might run into errors when mixing different operators with the *same precedence*
91 | > but *different fixity*, because Haskell won't understand how to group these expressions.
92 | > In that case, we can solve the problem by adding parenthesis explicitly.
93 |
94 | ---
95 |
96 | Exercises:
97 |
98 | 1. Separate the functionality of `wrapHtml` into two functions:
99 | 1. One that wraps content in `html` tag
100 | 2. one that wraps content in a `body` tag
101 |
102 | Name the new functions `html_` and `body_`.
103 | 2. Change `myhtml` to use these two functions.
104 | 3. Add another two similar functions for the tags `` and ``
105 | and name them `head_` and `title_`.
106 | 4. Create a new function, `makeHtml`, which takes two strings as input:
107 | 1. One string for the title
108 | 2. One string for the body content
109 |
110 | And construct an HTML string using the functions implemented in the previous exercises.
111 |
112 | The output for:
113 |
114 | ```hs
115 | makeHtml "My page title" "My page content"
116 | ```
117 |
118 | should be:
119 |
120 | ```html
121 | My page titleMy page content
122 | ```
123 | 5. Use `makeHtml` in `myhtml` instead of using `html_` and `body_` directly
124 |
125 | ---
126 |
127 | Solutions:
128 |
129 |
130 | Solution for exercise #1
131 |
132 | ```hs
133 | html_ content = "" <> content <> ""
134 |
135 | body_ content = "" <> content <> ""
136 | ```
137 |
138 |
139 |
140 |
141 | Solution for exercise #2
142 |
143 | ```hs
144 | myhtml = html_ (body_ "Hello, world!")
145 | ```
146 |
147 |
148 |
149 |
150 | Solution for exercise #3
151 |
152 | ```hs
153 | head_ content = "" <> content <> ""
154 |
155 | title_ content = "" <> content <> ""
156 | ```
157 |
158 |
159 |
160 |
161 | Solution for exercise #4
162 |
163 | ```hs
164 | makeHtml title content = html_ (head_ (title_ title) <> body_ content)
165 | ```
166 |
167 |
168 |
169 |
170 |
171 | Solution for exercise #5
172 |
173 | ```hs
174 | myhtml = makeHtml "Hello title" "Hello, world!"
175 | ```
176 |
177 |
178 |
179 |
180 |
181 | Our final program
182 |
183 | ```hs
184 | -- hello.hs
185 |
186 | main = putStrLn myhtml
187 |
188 | myhtml = makeHtml "Hello title" "Hello, world!"
189 |
190 | makeHtml title content = html_ (head_ (title_ title) <> body_ content)
191 |
192 | html_ content = "" <> content <> ""
193 |
194 | body_ content = "" <> content <> ""
195 |
196 | head_ content = "" <> content <> ""
197 |
198 | title_ content = "" <> content <> ""
199 | ```
200 |
201 | We can now run our `hello.hs` program, pipeline the output into a file,
202 | and open it in our browser:
203 |
204 | ```sh
205 | runghc hello.hs > hello.html
206 | firefox hello.html
207 | ```
208 |
209 | It should display `Hello, world!` on the page and `Hello title` on the page's title.
210 |
211 |
212 |
213 |
214 | ---
215 |
216 | ## Indentation
217 |
218 | You might ask how does Haskell know a definition is complete?
219 | The answer is: Haskell uses indentation to know when things should be grouped together.
220 |
221 | Indentation in Haskell can be a bit tricky, but in general: code that is supposed to be
222 | part of some expression should be indented further than the beginning of that expression.
223 |
224 | We know the two definitions are separate because the second one is not indented further than the first one.
225 |
226 |
227 | ### Indentation tips
228 |
229 | 1. Choose a specific amount of spaces for indentation (2 spaces, 4 spaces, etc.) and stick to it.
230 | Always use spaces over tabs.
231 | 2. Do not indent more than once at any given time.
232 | 3. When in doubt, drop the line as needed and indent once.
233 |
234 | Here are a few examples:
235 |
236 | ```hs
237 | main =
238 | putStrLn "Hello, world!"
239 | ```
240 |
241 | or:
242 |
243 | ```hs
244 | main =
245 | putStrLn
246 | (wrapHtml "Hello, world!")
247 | ```
248 |
249 | __Avoid the following styles__, which use more than one indentation step, or completely disregard
250 | indentation steps:
251 |
252 | ```hs
253 | main = putStrLn
254 | (wrapHtml "Hello, world!")
255 | ```
256 |
257 | ```hs
258 | main = putStrLn
259 | (wrapHtml "Hello, world!")
260 | ```
261 |
262 |
--------------------------------------------------------------------------------
/src/06-errors_and_files.md:
--------------------------------------------------------------------------------
1 | # Handling errors and multiple files
2 |
3 | We left an unimplemented function last chapter,
4 | and there are a few more things left for us to do to actually call our program a static blog generator.
5 | We still need to process multiple files in a directory and create an index landing page with links to other pages.
6 |
7 | ## Links in HTML
8 |
9 | Our HTML EDSL currently does not support links or other content modifiers such as bold and italics.
10 | We should add these so we can use them when creating an index.
11 |
12 | Up until now, we've passed `String` to `Structure`, creating functions such as `p_`
13 | and `h_`. Instead, we can create and pass them a new type, `Content`, which
14 | can be regular text, links, images, and so on.
15 |
16 | ---
17 |
18 | **Exercise**: implement what we've just discussed. Follow the compiler errors and refactor what needs refactoring.
19 |
20 | Solution
21 |
22 | src/Html/Internal.hs
23 |
24 | ```hs
25 | module HsBlog.Html.Internal where
26 |
27 | import Numeric.Natural
28 |
29 | -- * Types
30 |
31 | newtype Html
32 | = Html String
33 |
34 | newtype Structure
35 | = Structure String
36 |
37 | newtype Content
38 | = Content String
39 |
40 | type Title
41 | = String
42 |
43 | -- * EDSL
44 |
45 | html_ :: Title -> Structure -> Html
46 | html_ title content =
47 | Html
48 | ( el "html"
49 | ( el "head" (el "title" (escape title))
50 | <> el "body" (getStructureString content)
51 | )
52 | )
53 |
54 | -- * Structure
55 |
56 | p_ :: Content -> Structure
57 | p_ = Structure . el "p" . getContentString
58 |
59 | h_ :: Natural -> Content -> Structure
60 | h_ n = Structure . el ("h" <> show n) . getContentString
61 |
62 | ul_ :: [Structure] -> Structure
63 | ul_ =
64 | Structure . el "ul" . concat . map (el "li" . getStructureString)
65 |
66 | ol_ :: [Structure] -> Structure
67 | ol_ =
68 | Structure . el "ol" . concat . map (el "li" . getStructureString)
69 |
70 | code_ :: String -> Structure
71 | code_ = Structure . el "pre" . escape
72 |
73 | instance Semigroup Structure where
74 | (<>) c1 c2 =
75 | Structure (getStructureString c1 <> getStructureString c2)
76 |
77 | instance Monoid Structure where
78 | mempty = Structure ""
79 |
80 | -- * Content
81 |
82 | txt_ :: String -> Content
83 | txt_ = Content . escape
84 |
85 | link_ :: FilePath -> Content -> Content
86 | link_ path content =
87 | Content $
88 | elAttr
89 | "a"
90 | ("href=\"" <> escape path <> "\"")
91 | (getContentString content)
92 |
93 | img_ :: FilePath -> Content
94 | img_ path =
95 | Content $ "
escape path <> "\">"
96 |
97 | b_ :: Content -> Content
98 | b_ content =
99 | Content $ el "b" (getContentString content)
100 |
101 | i_ :: Content -> Content
102 | i_ content =
103 | Content $ el "i" (getContentString content)
104 |
105 | instance Semigroup Content where
106 | (<>) c1 c2 =
107 | Content (getContentString c1 <> getContentString c2)
108 |
109 | instance Monoid Content where
110 | mempty = Content ""
111 |
112 | -- * Render
113 |
114 | render :: Html -> String
115 | render html =
116 | case html of
117 | Html str -> str
118 |
119 | -- * Utilities
120 |
121 | el :: String -> String -> String
122 | el tag content =
123 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
124 |
125 | elAttr :: String -> String -> String -> String
126 | elAttr tag attrs content =
127 | "<" <> tag <> " " <> attrs <> ">" <> content <> "" <> tag <> ">"
128 |
129 | getStructureString :: Structure -> String
130 | getStructureString structure =
131 | case structure of
132 | Structure str -> str
133 |
134 | getContentString :: Content -> String
135 | getContentString content =
136 | case content of
137 | Content str -> str
138 |
139 | escape :: String -> String
140 | escape =
141 | let
142 | escapeChar c =
143 | case c of
144 | '<' -> "<"
145 | '>' -> ">"
146 | '&' -> "&"
147 | '"' -> """
148 | '\'' -> "'"
149 | _ -> [c]
150 | in
151 | concat . map escapeChar
152 |
153 |
154 | ```
155 |
156 |
157 |
158 | src/Html.hs
159 |
160 | ```hs
161 | module HsBlog.Html
162 | ( Html
163 | , Title
164 | , Structure
165 | , html_
166 | , p_
167 | , h_
168 | , ul_
169 | , ol_
170 | , code_
171 | , Content
172 | , txt_
173 | , img_
174 | , link_
175 | , b_
176 | , i_
177 | , render
178 | )
179 | where
180 |
181 | import HsBlog.Html.Internal
182 | ```
183 |
184 |
185 |
186 | src/Convert.hs
187 |
188 | ```hs
189 | module HsBlog.Convert where
190 |
191 | import qualified HsBlog.Markup as Markup
192 | import qualified HsBlog.Html as Html
193 |
194 | convert :: Html.Title -> Markup.Document -> Html.Html
195 | convert title = Html.html_ title . foldMap convertStructure
196 |
197 | convertStructure :: Markup.Structure -> Html.Structure
198 | convertStructure structure =
199 | case structure of
200 | Markup.Heading n txt ->
201 | Html.h_ n $ Html.txt_ txt
202 |
203 | Markup.Paragraph p ->
204 | Html.p_ $ Html.txt_ p
205 |
206 | Markup.UnorderedList list ->
207 | Html.ul_ $ map (Html.p_ . Html.txt_) list
208 |
209 | Markup.OrderedList list ->
210 | Html.ol_ $ map (Html.p_ . Html.txt_) list
211 |
212 | Markup.CodeBlock list ->
213 | Html.code_ (unlines list)
214 | ```
215 |
216 |
217 |
218 |
219 |
220 | ---
221 |
222 | > You can view the git commit of
223 | > [the changes we've made](https://github.com/soupi/learn-haskell-blog-generator/commit/110a19029f0be42eb2ac656f5d38356dbf9c5746)
224 | > and the [code up until now](https://github.com/soupi/learn-haskell-blog-generator/tree/110a19029f0be42eb2ac656f5d38356dbf9c5746).
225 |
226 | ## Creating an index page
227 |
228 | With our extended HTML EDSL, we can now create an index page with links to the other pages.
229 |
230 | To create an index page, we need a list of files with their *target destinations*,
231 | as well as their `Markup` (so we can extract information to include in our index page,
232 | such as the first heading and paragraph). Our output should be an `Html` page.
233 |
234 | ---
235 |
236 | We need to implement the following function:
237 |
238 | ```hs
239 | buildIndex :: [(FilePath, Markup.Document)] -> Html.Html
240 | ```
241 |
242 | Solution
243 |
244 | ```hs
245 | buildIndex :: [(FilePath, Markup.Document)] -> Html.Html
246 | buildIndex files =
247 | let
248 | previews =
249 | map
250 | ( \(file, doc) ->
251 | case doc of
252 | Markup.Heading 1 heading : article ->
253 | Html.h_ 3 (Html.link_ file (Html.txt_ heading))
254 | <> foldMap convertStructure (take 3 article)
255 | <> Html.p_ (Html.link_ file (Html.txt_ "..."))
256 | _ ->
257 | Html.h_ 3 (Html.link_ file (Html.txt_ file))
258 | )
259 | files
260 | in
261 | Html.html_
262 | "Blog"
263 | ( Html.h_ 1 (Html.link_ "index.html" (Html.txt_ "Blog"))
264 | <> Html.h_ 2 (Html.txt_ "Posts")
265 | <> mconcat previews
266 | )
267 | ```
268 |
269 |
270 |
271 |
272 | ---
273 |
274 | ## Processing directories
275 |
276 | Our general strategy for processing whole directories is going to be:
277 |
278 | - Create the output directory
279 | - Grab all file names in a directory
280 | - Filter them according to their extension; we want to process the `txt` files and
281 | copy other files without modification
282 | - We want to parse each text file, build an index of the result,
283 | convert the files to HTML, and write everything to the target directory
284 |
285 | While our parsing function can't really fail, trying to read or write a file
286 | to the file-system can fail in several ways. It would be nice if our
287 | static blog generator was robust enough that it wouldn't fail completely if one
288 | single file gave it some trouble. This is an excellent opportunity to learn about
289 | error handling in Haskell, both in uneffectful code and for I/O code.
290 |
291 | In the next few chapters, we'll survey the landscape of error handling in Haskell
292 | before figuring out the right approach for our use case.
293 |
--------------------------------------------------------------------------------
/src/03-html/06-escaping_characters.md:
--------------------------------------------------------------------------------
1 | # Escaping characters
2 |
3 | Now that `Html` has its own source file and module, and creating
4 | HTML code can be done only via the functions we exported,
5 | we can also handle user input that may contain characters
6 | that may conflict with our meta language, HTML,
7 | such as `<` and `>`, which are used for creating HTML tags.
8 |
9 | We can convert these characters into different strings that HTML can handle.
10 |
11 | See [Stack overflow question](https://stackoverflow.com/questions/7381974/which-characters-need-to-be-escaped-in-html)
12 | for a list of characters, we need to escape.
13 |
14 | Let's create a new function called `escape`:
15 |
16 | ```hs
17 | escape :: String -> String
18 | escape =
19 | let
20 | escapeChar c =
21 | case c of
22 | '<' -> "<"
23 | '>' -> ">"
24 | '&' -> "&"
25 | '"' -> """
26 | '\'' -> "'"
27 | _ -> [c]
28 | in
29 | concat . map escapeChar
30 | ```
31 |
32 | In `escape` we see a few new things:
33 |
34 | 1. Let expressions: we can define local names using this syntax:
35 |
36 | ```hs
37 | let
38 | =
39 | in
40 |
41 | ```
42 |
43 | This will make `` available as a variable `in` the second ``.
44 |
45 | 2. Pattern matching with multiple patterns: we match on different
46 | characters and convert them to a string. Note that `_` is a "catch
47 | all" pattern that will always succeed.
48 |
49 | 3. Two new functions: `map` and `concat`; we'll talk about these in more in-depth
50 |
51 | 4. The syntax highlighting broke a bit for this snippet for some reason. Don't worry about it.
52 |
53 | ## Linked lists briefly
54 |
55 | Linked lists are very common data structures in Haskell, so common that
56 | they have their own special syntax:
57 |
58 | 1. The list types are denoted with brackets, and inside them is the type of the element. For example:
59 | - `[Int]` - a list of integers
60 | - `[Char]` - a list of characters
61 | - `[String]` - a list of strings
62 | - `[[String]]` - a list of a list of strings
63 | - `[a]` - a list of any single type (all elements must be of the same type)
64 | 2. An empty list is written like this: `[]`
65 | 3. Prepending an element to a list is done with the operator `:` (pronounced cons), which is right-associative (like `->`).
66 | For example: `1 : []`, or `1 : 2 : 3 : []`.
67 | 4. The above lists can also be written like `[1]` and `[1, 2, 3]`.
68 |
69 | Also, Strings are linked lists of characters - String is defined as:
70 | `type String = [Char]`, so we can use them the same way we use lists.
71 |
72 | > Do note, however, that linked lists, despite their convenience, are often
73 | > not the right tool for the job. They are not particularly space efficient
74 | > and are slow for appending, random access, and more. That also makes `String`
75 | > a lot less efficient than what it could be. And I generally recommend using a
76 | > different string type, `Text`, instead, which is available in an external package.
77 | > We will talk about lists, `Text`, and other data structures in the future!
78 |
79 | We can implement our own operations on lists by using pattern matching and recursion.
80 | And we'll touch on this subject later when talking about ADTs.
81 |
82 | For now, we will use the various functions found in the
83 | [Data.List](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-List.html) module.
84 | Specifically, [map](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-List.html#v:map)
85 | and [concat](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-List.html#v:concat).
86 |
87 | ### `map`
88 |
89 | Using `map`, we can apply a function to each element in a list. Its type signature is:
90 |
91 | ```hs
92 | map :: (a -> b) -> [a] -> [b]
93 | ```
94 |
95 | For example:
96 |
97 | ```hs
98 | map not [False, True, False] == [True, False, True]
99 | ```
100 |
101 | Or as can be seen in our `escape` function, this can help us escape each character:
102 |
103 | ```hs
104 | map escapeChar ['<','h','1','>'] == ["<","h","1",">"]
105 | ```
106 |
107 | However, note that the `escapeChar` has the type `Char -> String`,
108 | so the result type of `map escapeChar ['<','h','1','>']` is `[String]`,
109 | and what we really want is a `String` and not `[String]`.
110 |
111 | This is where `concat` enters the picture to help us flatten the list.
112 |
113 | ### `concat`
114 |
115 | `concat` has the type:
116 |
117 | ```hs
118 | concat :: [[a]] -> [a]
119 | ```
120 |
121 | It flattens a list of list of something into a list of something.
122 | In our case it will flatten `[String]` into `String`, remember that
123 | `String` is a **type alias** for `[Char]`, so we actually have
124 | `[[Char]] -> [Char]`.
125 |
126 | ## GHCi
127 |
128 | One way we can quickly see our code in action is by using the interactive development environment **GHCi**.
129 | Running `ghci` will open an interactive prompt where Haskell expressions can be written and
130 | evaluated. This is called a "Read-Evaluate-Print Loop" (for short - REPL).
131 |
132 | For example:
133 |
134 | ```
135 | ghci> 1 + 1
136 | 2
137 | ghci> putStrLn "Hello, world!"
138 | Hello, world!
139 | ```
140 |
141 | We can define new names:
142 |
143 | ```
144 | ghci> double x = x + x
145 | ghci> double 2
146 | 4
147 | ```
148 |
149 | We can write multi-line code by surrounding it with `:{` and `:}`:
150 |
151 | ```
152 | ghci> :{
153 | | escape :: String -> String
154 | | escape =
155 | | let
156 | | escapeChar c =
157 | | case c of
158 | | '<' -> "<"
159 | | '>' -> ">"
160 | | '&' -> "&"
161 | | '"' -> """
162 | | '\'' -> "'"
163 | | _ -> [c]
164 | | in
165 | | concat . map escapeChar
166 | | :}
167 |
168 | ghci> escape ""
169 | "<html>"
170 |
171 | ```
172 |
173 | We can import Haskell source files using the `:load` command (`:l` for short):
174 |
175 | ```
176 | ghci> :load Html.hs
177 | [1 of 1] Compiling Html ( Html.hs, interpreted )
178 | Ok, one module loaded.
179 | ghci> render (html_ "" (p_ ""))
180 | ""
181 | ```
182 |
183 | As well as import library modules:
184 |
185 | ```
186 | ghci> import Data.Bits
187 | ghci> shiftL 32 1
188 | 64
189 | ghci> clearBit 33 0
190 | 32
191 | ```
192 |
193 | We can even ask the type of an expression using the `:type` command
194 | (`:t` for short):
195 |
196 | ```
197 | λ> :type escape
198 | escape :: String -> String
199 | ```
200 |
201 | To exit `ghci`, use the `:quit` command (or `:q` for short)
202 |
203 | ```
204 | ghci> :quit
205 | Leaving GHCi.
206 | ```
207 |
208 | GHCi is a very useful tool for quick experiments and exploration.
209 | We've seen a couple of examples of that above - passing the string `""` to our
210 | `escape` function returns the string `"<html>"`, which can be rendered by
211 | a browser as `` instead of an HTML tag.
212 |
213 | If you are having a hard time figuring out what a particular function does, consider
214 | testing it in GHCi - pass it different inputs and see if it matches your expectations.
215 | Concrete examples of running code can aid a lot in understanding it!
216 |
217 | > If you'd like to learn more about GHCi, you can find a more thorough introduction in the
218 | > [GHC user guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/ghci.html).
219 |
220 | ## Escaping
221 |
222 | ---
223 |
224 | The user of our library can currently only supply strings in a few places:
225 |
226 | 1. Page title
227 | 2. Paragraphs
228 | 3. Headings
229 |
230 | We can apply our escape function at these places before doing anything else with it.
231 | That way, all HTML constructions are safe.
232 |
233 | Try adding the escaping function in those places.
234 |
235 |
236 | Solution
237 |
238 | ```hs
239 | html_ :: Title -> Structure -> Html
240 | html_ title content =
241 | Html
242 | ( el "html"
243 | ( el "head" (el "title" (escape title))
244 | <> el "body" (getStructureString content)
245 | )
246 | )
247 |
248 | p_ :: String -> Structure
249 | p_ = Structure . el "p" . escape
250 |
251 | h1_ :: String -> Structure
252 | h1_ = Structure . el "h1" . escape
253 | ```
254 |
255 |
256 |
257 | ---
258 |
259 |
260 | Our revised Html.hs
261 |
262 | ```hs
263 | -- Html.hs
264 |
265 | module Html
266 | ( Html
267 | , Title
268 | , Structure
269 | , html_
270 | , p_
271 | , h1_
272 | , append_
273 | , render
274 | )
275 | where
276 |
277 | -- * Types
278 |
279 | newtype Html
280 | = Html String
281 |
282 | newtype Structure
283 | = Structure String
284 |
285 | type Title
286 | = String
287 |
288 | -- * EDSL
289 |
290 | html_ :: Title -> Structure -> Html
291 | html_ title content =
292 | Html
293 | ( el "html"
294 | ( el "head" (el "title" (escape title))
295 | <> el "body" (getStructureString content)
296 | )
297 | )
298 |
299 | p_ :: String -> Structure
300 | p_ = Structure . el "p" . escape
301 |
302 | h1_ :: String -> Structure
303 | h1_ = Structure . el "h1" . escape
304 |
305 | append_ :: Structure -> Structure -> Structure
306 | append_ c1 c2 =
307 | Structure (getStructureString c1 <> getStructureString c2)
308 |
309 | -- * Render
310 |
311 | render :: Html -> String
312 | render html =
313 | case html of
314 | Html str -> str
315 |
316 | -- * Utilities
317 |
318 | el :: String -> String -> String
319 | el tag content =
320 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
321 |
322 | getStructureString :: Structure -> String
323 | getStructureString content =
324 | case content of
325 | Structure str -> str
326 |
327 | escape :: String -> String
328 | escape =
329 | let
330 | escapeChar c =
331 | case c of
332 | '<' -> "<"
333 | '>' -> ">"
334 | '&' -> "&"
335 | '"' -> """
336 | '\'' -> "'"
337 | _ -> [c]
338 | in
339 | concat . map escapeChar
340 | ```
341 |
342 |
343 |
344 | Try constructing an invalid HTML in `hello.hs` to see if this works or not!
345 |
346 | Now we can use our tiny HTML library safely. But what if the user
347 | wants to use our library with a valid use case we didn't think about, for
348 | example, adding unordered lists? We are completely blocking them from
349 | extending our library. We'll talk about this next.
350 |
--------------------------------------------------------------------------------
/src/03-html/02-type_signatures.md:
--------------------------------------------------------------------------------
1 | # Adding type signatures
2 |
3 | Haskell is a **statically typed** programming language. That means that every
4 | expression has a type, and we check that the types are valid with
5 | regard to each other before running the program. If we discover that
6 | they are not valid, an error message will be printed, and the program
7 | will not run.
8 |
9 | An example of a type error would be if we'd pass 3 arguments to a function
10 | that takes only 2, or pass a number instead of a string.
11 |
12 | Haskell is also **type inferred**, so we don't *need* to specify the type
13 | of expressions - Haskell can *infer* from the context of the expression
14 | what its type should be, and that's what we have done until now. However, **specifying
15 | types is useful** - it adds a layer of documentation for you or others
16 | that will look at the code later, and it helps verify to some degree
17 | that what was intended (with the type signature) is what was
18 | written (with the expression). It is generally recommended to annotate all *top-level*
19 | definitions with type signatures.
20 |
21 | We use a double-colon (`::`) to specify the type of names. We usually
22 | write it right above the definition of the name itself.
23 |
24 | Here are a few examples of types we can write:
25 |
26 | - `Int` - The type of integer numbers
27 | - `String` - The type of strings
28 | - `Bool` - The type of booleans
29 | - `()` - The type of the expression `()`, also called unit
30 | - `a -> b` - The type of a function from an expression of type `a` to an expression of type `b`
31 | - `IO ()` - The type of an expression that represents an IO subroutine that returns `()`
32 |
33 | Let's specify the type of `title_`:
34 |
35 | ```hs
36 | title_ :: String -> String
37 | ```
38 |
39 | We can see in the code that the type of `title_` is a function that takes
40 | a `String` and returns a `String`.
41 |
42 | Let's also specify the type of `makeHtml`:
43 |
44 | ```hs
45 | makeHtml :: String -> String -> String
46 | ```
47 |
48 | Previously, we thought about `makeHtml` as a function that takes
49 | two strings and returns a string.
50 |
51 | But actually, all functions in Haskell take **exactly one argument** as input
52 | and return **exactly one value** as output. It's just convenient to refer
53 | to functions like `makeHtml` as functions with multiple inputs.
54 |
55 | In our case, `makeHtml` is a function that takes **one** string argument
56 | and returns a **function**. _The function it returns_ takes a string argument
57 | as well and finally returns a string.
58 |
59 | The magic here is that `->` is right-associative. This means that when we write:
60 |
61 | ```hs
62 | makeHtml :: String -> String -> String
63 | ```
64 |
65 | Haskell parses it as:
66 |
67 | ```hs
68 | makeHtml :: String -> (String -> String)
69 | ```
70 |
71 | Consequently, the expression `makeHtml "My title"` is also a function!
72 | One that takes a string (the content, the second argument of `makeHtml`)
73 | and returns the expected HTML string with "My title" in the title.
74 |
75 | This is called **partial application**.
76 |
77 | To illustrate, let's define `html_` and `body_` in a different way by
78 | defining a new function, `el`.
79 |
80 | ```hs
81 | el :: String -> String -> String
82 | el tag content =
83 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
84 | ```
85 |
86 | el is a function that takes a tag and content, and wraps the content
87 | with the tag.
88 |
89 | We can now implement `html_` and `body_` by partially applying `el` and
90 | only provide the tag.
91 |
92 | ```hs
93 | html_ :: String -> String
94 | html_ = el "html"
95 |
96 | body_ :: String -> String
97 | body_ = el "body"
98 | ```
99 |
100 | Note that we didn't need to add the argument on the left side of
101 | equals sign because Haskell functions are "**first class**" - they behave
102 | exactly like values of primitive types like `Int` or `String`.
103 | We can name a function like any other value,
104 | put it in data structures, pass it to functions, and so on!
105 |
106 | The way Haskell treats names is very similar to copy-paste. Anywhere
107 | you see `html_` in the code, you can replace it with `el "html"`. They are
108 | the same (this is what the equals signs say, right? That the two sides
109 | are the same). This property of being able to *substitute* the two sides of the
110 | equals sign with one another is called **referential transparency**. And
111 | it is pretty unique to Haskell (and a few similar languages such as PureScript and Elm)!
112 | We'll talk more about referential transparency in a later chapter.
113 |
114 | ### Anonymous/lambda functions
115 |
116 | To further drive the point that Haskell functions are first class and
117 | all functions take exactly one argument,
118 | I'll mention that the syntax we've been using up until
119 | now to define function is just syntactic sugar! We can also define
120 | **anonymous functions** - functions without a name, anywhere we'd like.
121 | Anonymous functions are also known as **lambda functions**
122 | as a tribute to the formal mathematical system
123 | which is at the heart of all functional programming
124 | languages - the lambda calculus.
125 |
126 | We can create an anonymous function anywhere we'd expect an expression,
127 | such as `"hello"`, using the following syntax:
128 |
129 | ```hs
130 | \ ->
131 | ```
132 |
133 | This little `\` (which bears some resemblance to the lowercase Greek letter lambda 'λ')
134 | marks the head of the lambda function,
135 | and the arrow (`->`) marks the beginning of the function's body.
136 | We can even chain lambda functions, making them "multiple argument functions" by
137 | defining another lambda in the body of another, like this:
138 |
139 | ```hs
140 | three = (\num1 -> \num2 -> num1 + num2) 1 2
141 | ```
142 |
143 | As before, we evaluate functions by substituting the function argument with
144 | the applied value. In the example above, we substitute `num1` with `1` and get
145 | `(\num2 -> 1 + num2) 2`. Then substitute `num2` with `2` and get `1 + 2`.
146 | We'll talk more about substitution later.
147 |
148 | So, when we write:
149 |
150 | ```hs
151 | el :: String -> String -> String
152 | el tag content =
153 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
154 | ```
155 |
156 | Haskell actually translates this under the hood to:
157 |
158 | ```hs
159 | el :: String -> (String -> String)
160 | el = \tag -> \content ->
161 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
162 | ```
163 |
164 | Hopefully, this form makes it a bit clearer why Haskell functions
165 | always take one argument, even when we have syntactic sugar that
166 | might suggest otherwise.
167 |
168 | I'll mention one more syntactic sugar for anonymous functions:
169 | We don't actually have to write multiple argument anonymous functions
170 | this way, we can write:
171 |
172 | ```hs
173 | \ ... ->
174 | ```
175 |
176 | to save us some trouble. For example:
177 |
178 | ```hs
179 | three = (\num1 num2 -> num1 + num2) 1 2
180 | ```
181 |
182 | But it's worth remembering what they are under the hood.
183 |
184 | We won't be needing anonymous/lambda functions at this point,
185 | but we'll discuss them later and see where they can be useful.
186 |
187 | ---
188 |
189 | Exercises:
190 |
191 | 1. Add types for all of the functions we created until now
192 |
193 | 2. Change the implementation of the HTML functions we built to use `el` instead
194 |
195 | 3. Add a couple more functions for defining paragraphs and headings:
196 | 1. `p_` which uses the tag `` for paragraphs
197 | 2. `h1_` which uses the tag `
` for headings
198 |
199 | 4. Replace our `Hello, world!` string with richer content, use `h1_` and `p_`.
200 | We can append HTML strings created by `h1_` and `p_` using the append operator `<>`.
201 |
202 | Bonus: rewrite a couple of functions using lambda functions, just for fun!
203 |
204 | ---
205 |
206 | Solutions:
207 |
208 |
209 | Solution for exercise #1
210 |
211 | ```hs
212 | myhtml :: String
213 | myhtml = makeHtml "Hello title" "Hello, world!"
214 |
215 | makeHtml :: String -> String -> String
216 | makeHtml title content = html_ (head_ (title_ title) <> body_ content)
217 |
218 | html_ :: String -> String
219 | html_ content = "" <> content <> ""
220 |
221 | body_ :: String -> String
222 | body_ content = "" <> content <> ""
223 |
224 | head_ :: String -> String
225 | head_ content = "" <> content <> ""
226 |
227 | title_ :: String -> String
228 | title_ content = "" <> content <> ""
229 | ```
230 |
231 |
232 |
233 |
234 | Solution for exercise #2
235 |
236 | ```hs
237 | html_ :: String -> String
238 | html_ = el "html"
239 |
240 | body_ :: String -> String
241 | body_ = el "body"
242 |
243 | head_ :: String -> String
244 | head_ = el "head"
245 |
246 | title_ :: String -> String
247 | title_ = el "title"
248 | ```
249 |
250 |
251 |
252 |
253 |
254 | Solution for exercise #3
255 |
256 | ```hs
257 | p_ :: String -> String
258 | p_ = el "p"
259 |
260 | h1_ :: String -> String
261 | h1_ = el "h1"
262 | ```
263 |
264 |
265 |
266 |
267 | Solution for exercise #4
268 |
269 | ```hs
270 | myhtml :: String
271 | myhtml =
272 | makeHtml
273 | "Hello title"
274 | (h1_ "Hello, world!" <> p_ "Let's learn about Haskell!")
275 | ```
276 |
277 |
278 |
279 |
280 |
281 | ---
282 |
283 |
284 | Our final program
285 |
286 | ```hs
287 | -- hello.hs
288 |
289 | main :: IO ()
290 | main = putStrLn myhtml
291 |
292 | myhtml :: String
293 | myhtml =
294 | makeHtml
295 | "Hello title"
296 | (h1_ "Hello, world!" <> p_ "Let's learn about Haskell!")
297 |
298 |
299 | makeHtml :: String -> String -> String
300 | makeHtml title content = html_ (head_ (title_ title) <> body_ content)
301 |
302 | html_ :: String -> String
303 | html_ = el "html"
304 |
305 | body_ :: String -> String
306 | body_ = el "body"
307 |
308 | head_ :: String -> String
309 | head_ = el "head"
310 |
311 | title_ :: String -> String
312 | title_ = el "title"
313 |
314 | p_ :: String -> String
315 | p_ = el "p"
316 |
317 | h1_ :: String -> String
318 | h1_ = el "h1"
319 |
320 | el :: String -> String -> String
321 | el tag content =
322 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
323 | ```
324 |
325 |
326 |
--------------------------------------------------------------------------------
/src/09-documentation.md:
--------------------------------------------------------------------------------
1 | # Generating documentation
2 |
3 | There are [many ways](https://documentation.divio.com/)
4 | to help others to get started with our projects and libraries.
5 | For example, we can write tutorials, provide runnable examples,
6 | describe the system's internals, and create an API reference.
7 |
8 | In this chapter, we will focus on generating API reference pages (the kind that can be seen on Hackage)
9 | from annotated Haskell source code using [Haddock](https://haskell-haddock.readthedocs.io/en/latest/).
10 |
11 | ## Running Haddock
12 |
13 | We can generate API reference pages (a.k.a. haddocks in the Haskell world) for our project
14 | using our favorite package manager:
15 |
16 | ### Cabal
17 |
18 | We can run `cabal haddock` to generate haddocks:
19 |
20 | ```sh
21 | ➜ cabal haddock
22 | Resolving dependencies...
23 | Build profile: -w ghc-9.0.1 -O1
24 | In order, the following will be built (use -v for more details):
25 | - hs-blog-0.1.0.0 (lib) (first run)
26 | Configuring library for hs-blog-0.1.0.0..
27 | Preprocessing library for hs-blog-0.1.0.0..
28 | Running Haddock on library for hs-blog-0.1.0.0..
29 | Haddock coverage:
30 | 0% ( 0 / 3) in 'HsBlog.Env'
31 | Missing documentation for:
32 | Module header
33 | Env (src/HsBlog/Env.hs:3)
34 | defaultEnv (src/HsBlog/Env.hs:10)
35 | 21% ( 7 / 33) in 'HsBlog.Html.Internal'
36 | Missing documentation for:
37 | Module header
38 | Html (src/HsBlog/Html/Internal.hs:8)
39 | ...
40 | Documentation created:
41 | /tmp/learn-haskell-blog-generator/dist-newstyle/build/x86_64-linux/ghc-9.0.1/hs-blog-0.1.0.0/doc/html/hs-blog/index.html
42 | ```
43 |
44 | Cabal and Haddock will build our project and generate HTML pages for us at:
45 |
46 | ```html
47 | ./dist-newstyle/build///-/doc/html//
48 | ```
49 |
50 | We can then open the `index.html` file from that directory in a web browser and view our package documentation.
51 |
52 | ### Stack
53 |
54 | We can run `stack haddock` to generate haddocks:
55 |
56 | ```sh
57 | ➜ stack haddock
58 | ...
59 | hs-blog> build (lib + exe)
60 | Preprocessing library for hs-blog-0.1.0.0..
61 | Building library for hs-blog-0.1.0.0..
62 | [1 of 7] Compiling HsBlog.Env
63 | [2 of 7] Compiling HsBlog.Html.Internal
64 | ...
65 | hs-blog> haddock
66 | Preprocessing library for hs-blog-0.1.0.0..
67 | Running Haddock on library for hs-blog-0.1.0.0..
68 | Haddock coverage:
69 | 0% ( 0 / 3) in 'HsBlog.Env'
70 | Missing documentation for:
71 | Module header
72 | Env (src/HsBlog/Env.hs:3)
73 | defaultEnv (src/HsBlog/Env.hs:10)
74 | 21% ( 7 / 33) in 'HsBlog.Html.Internal'
75 | Missing documentation for:
76 | Module header
77 | Html (src/HsBlog/Html/Internal.hs:8)
78 | ...
79 | Documentation created:
80 | .stack-work/dist/x86_64-linux-tinfo6/Cabal-3.2.1.0/doc/html/hs-blog/index.html,
81 | .stack-work/dist/x86_64-linux-tinfo6/Cabal-3.2.1.0/doc/html/hs-blog/hs-blog.txt
82 | Preprocessing executable 'hs-blog-gen' for hs-blog-0.1.0.0..
83 | ...
84 | ```
85 |
86 | Stack and Haddock will build our project and generate HTML pages for us at:
87 |
88 | ```html
89 | ./.stack-work/dist//Cabal-/doc/html//
90 | ```
91 |
92 | We can then open the `index.html` file from that directory in a web browser and view our package documentation.
93 |
94 | ### Haddock coverage
95 |
96 | Haddock will also output a coverage report when run and mention user-exposed constructs that are missing
97 | documentation. These constructs could be module headers, types, data constructors, type classes, functions, values, etc.
98 |
99 | For example:
100 |
101 | ```hs
102 | Haddock coverage:
103 | ...
104 | 0% ( 0 / 3) in 'HsBlog.Convert'
105 | Missing documentation for:
106 | Module header
107 | convert (src/HsBlog/Convert.hs:8)
108 | convertStructure (src/HsBlog/Convert.hs:23)
109 | 67% ( 2 / 3) in 'HsBlog.Directory'
110 | Missing documentation for:
111 | buildIndex (src/HsBlog/Directory.hs:80)
112 | ...
113 | ```
114 |
115 | We can see that we did not document the `HsBlog.Convert` at all, and we are missing
116 | documentation for the module header, the `convert` function, and the `convertStructure` function.
117 |
118 | On the other hand, it seems that we do currently have some documentation written for the `HsBlog.Directory`
119 | module! We'll see why, but first - try to generate haddocks, visit the module hierarchy, browse around
120 | the different modules, follow the links of the types, imagine what this API reference could look like,
121 | and let's see how we can improve it.
122 |
123 | ## Haddock markup
124 |
125 | Haddock builds the API reference pages by building our project, examining the exported modules
126 | and their exported definitions, and grabbing source code comments written in special markup format.
127 |
128 | Let's take a quick look at this markup format. We will go over a few important bits,
129 | but if you'd like to learn more, a complete guide for Haddock markup can be found in the
130 | [Haddock documentation](https://haskell-haddock.readthedocs.io/en/latest/markup.html).
131 |
132 |
133 | ### Documenting definitions
134 |
135 | All haddock annotations appear as part of regular Haskell comments.
136 | They can be used with both single-line form (`--`) and multi-line form (`{-` and `-}`).
137 | The placements of a comment block and the haddock marker determine which Haskell
138 | definition the haddock string is attached to.
139 |
140 | We can annotate a Haskell definition by writing a comment block prefixed with `|` *before*
141 | the definition, or by writing a comment block prefixed with `^` *after* the definition.
142 |
143 | For example:
144 |
145 | ```hs
146 | -- | Construct an HTML page from a `Head`
147 | -- and a `Structure`.
148 | html_
149 | :: Head -- ^ Represents the @\@ section in an HTML file
150 | -> Structure -- ^ Represents the @\@ section in an HTML file
151 | -> Html
152 | html_ = ...
153 | ...
154 | ```
155 |
156 | Here's another example:
157 |
158 | ```hs
159 | {- | Represents a single markup structure. Such as:
160 |
161 | - A paragraph
162 | - An unordered list
163 | - A code block
164 | -}
165 | data Structure
166 | = Heading Natural String
167 | -- ^ A section heading with a level
168 | | Paragraph String
169 | -- ^ A paragraph
170 | | UnorderedList [String]
171 | -- ^ An unordered list of strings
172 | | OrderedList [String]
173 | -- ^ An ordered list of strings
174 | | CodeBlock [String]
175 | -- ^ A code block
176 | ```
177 |
178 | And another:
179 |
180 | ```hs
181 | {- | Markup to HTML conversion module.
182 |
183 | This module handles converting documents written in our custom
184 | Markup language into HTML pages.
185 | -}
186 | module HsBlog.Convert where
187 | ```
188 |
189 | As you can see, `|` and `^` can be used to document functions, function arguments,
190 | types, data constructors, modules, and more. They are probably the most important
191 | Haddock annotations to remember (and even then, `|` alone will suffice).
192 |
193 | > **Tip**: Annotate the modules, types, and the top-level definitions
194 | > which are exported from your project
195 | > with some high-level description of what they are used for (at the very least).
196 | >
197 | > Your users and collaborators will thank you!
198 |
199 | ### Section headings
200 |
201 | We can separate our module into sections by adding headings.
202 | Headings are comments prefixed with a number of `*` (just like in our markup language).
203 |
204 | For example:
205 |
206 | ```hs
207 | -- * HTML EDSL
208 |
209 | html_ :: Head -> Structure -> Html
210 | html_ = ...
211 |
212 | -- ** Structure
213 |
214 | p_ :: Content -> Structure
215 | p_ = ..
216 |
217 | h_ :: Content -> Structure
218 | h_ = ..
219 |
220 | ...
221 |
222 | -- ** Content
223 |
224 | txt_ :: String -> Content
225 | txt_ = ...
226 |
227 | link_ :: FilePath -> Content -> Content
228 | link_ = ...
229 | ```
230 |
231 | It is also possible to add headings to the export list instead:
232 |
233 | ```hs
234 | module HsBlog.Html
235 | ( -- * HTML EDSL
236 | Html
237 | , html_
238 |
239 | -- ** Combinators used to construct the @\@ section
240 | , Head
241 | , title_
242 | , stylesheet_
243 | , meta_
244 |
245 | -- ** Combinators used to construct the @\@ section
246 | , Structure
247 | , p_
248 | , h_
249 | , ul_
250 | , ol_
251 | , code_
252 |
253 | -- ** Combinators used to construct content inside structures
254 | , Content
255 | , txt_
256 | , img_
257 | , link_
258 | , b_
259 | , i_
260 |
261 | -- ** Render HTML to String
262 | , render
263 | )
264 | where
265 | ```
266 |
267 | Separating parts of the module into sections helps keep the important things together
268 | and Haddock will create a table of contents at the top of a module page for us as well.
269 |
270 | Sometimes it's also easier to figure out whether a module should be split into multiple
271 | modules or not after splitting it into sections using headings.
272 |
273 | ---
274 |
275 | **Exercise**: Try to re-arrange the modules in our project to your liking and add headings to sections.
276 |
277 | ---
278 |
279 | ### Formatting
280 |
281 | As we saw earlier, we can also add formatting in the content of our comments.
282 | For example, we can:
283 |
284 | - Hyperlink identifiers by surrounding them with `` ` ``
285 |
286 | For example: `` `Heading` ``
287 | - Write `monospaced text` by surrounding it with `@`
288 |
289 | For example: `@Paragraph "Hello"@`
290 | - Add _emphasis_ to text by surrounding it with `/`
291 |
292 | For example: `/this is emphasised/`
293 | - Add __bold__ to text by surrounding it with `__`
294 |
295 | For example: `__this is bold__`
296 |
297 | ### More
298 |
299 | In this chapter, we've covered the basics of the Haddock markup language.
300 | If you'd like to know more, the [Haddock markup guide](https://haskell-haddock.readthedocs.io/en/latest/markup.html)
301 | contains information on creating even more interesting documentation structures, such as
302 | code blocks, grid tables, images, and examples.
303 |
304 | ## Summary
305 |
306 | We've briefly covered one aspect of documenting Haskell programs:
307 | using Haddock to generate informative API reference pages created from source code
308 | comments which are annotated with Haddock markup.
309 |
310 | While API references are incredibly valuable, remember that there are other forms of
311 | documentation that can help your users get started quickly, such as examples and tutorials.
312 |
313 |
314 | ---
315 |
316 | **Exercise**: Add haddock annotation to the top-level definitions in our project and test your understanding
317 | of the program and the various parts - sometimes, the best way to learn something is to try explaining it!
318 |
319 | ---
320 |
--------------------------------------------------------------------------------
/src/06-errors_and_files/03-exceptions.md:
--------------------------------------------------------------------------------
1 | # Exceptions
2 |
3 | The [Control.Exception](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html)
4 | module provides us with the ability to
5 | [throw](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#v:throwIO)
6 | exceptions from `IO` code,
7 | [`catch`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#g:5)
8 | Haskell exceptions in `IO` code, and even convert them to `IO (Either ...)`
9 | with the function [`try`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#g:7):
10 |
11 | ```hs
12 | throwIO :: Exception e => e -> IO a
13 |
14 | catch
15 | :: Exception e
16 | => IO a -- The computation to run
17 | -> (e -> IO a) -- Handler to invoke if an exception is raised
18 | -> IO a
19 |
20 | try :: Exception e => IO a -> IO (Either e a)
21 | ```
22 |
23 | The important part of these type signatures is the
24 | [`Exception`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#t:Exception)
25 | type class. By making a type an instance of the `Exception` type class, we can throw it
26 | and catch it in `IO` code:
27 |
28 | ```hs
29 | {-# language LambdaCase #-}
30 |
31 | import Control.Exception
32 | import System.IO
33 |
34 | data MyException
35 | = ErrZero
36 | | ErrOdd Int
37 | deriving Show
38 |
39 | instance Exception MyException
40 |
41 | sayDiv2 :: Int -> IO ()
42 | sayDiv2 n
43 | | n == 0 = throwIO ErrZero
44 | | n `mod` 2 /= 0 = throwIO (ErrOdd n)
45 | | otherwise = print (n `div` 2)
46 |
47 | main :: IO ()
48 | main =
49 | catch
50 | ( do
51 | putStrLn "Going to print a number now."
52 | sayDiv2 7
53 | putStrLn "Did you like it?"
54 | )
55 | ( \case
56 | ErrZero ->
57 | hPutStrLn stderr "Error: we don't support dividing zeroes for some reason"
58 | ErrOdd n ->
59 | hPutStrLn stderr ("Error: " <> show n <> " is odd and cannot be divided by 2")
60 | )
61 | ```
62 |
63 | > Note: we are using two new things here: guards and the `LambdaCase` language extension.
64 | >
65 | > 1. Guards, as seen in `sayDiv2` are just a nicer syntax around `if-then-else` expressions.
66 | > Using guards, we can have multiple `if` branches and finally use the `else` branch
67 | > by using `otherwise`. After each guard (`|`), there's a condition; after the condition, there's
68 | > a `=` and then the expression (the part after `then` in an `if` expression)
69 | >
70 | > 2. LambdaCase, as seen in `catch`, is just a syntactic sugar to save a few characters,
71 | > instead of writing `\e -> case e of`, we can write `\case`. It requires enabling the
72 | > `LambdaCase` extension
73 | >
74 | > #### Language extensions
75 | >
76 | > Haskell is a standardized language. However, GHC provides *extensions* to the language -
77 | > additional features that aren't covered in the 98 or 2010 standards of Haskell.
78 | > Features such as syntactic extensions (like LambdaCase above), extensions to the type checker,
79 | > and more.
80 | >
81 | > These extensions can be added by adding `{-# language #-}`
82 | > (the `language` part is case insensitive)
83 | > to the top of a Haskell source file, or they can be set globally for an entire project by
84 | > specifying them in the
85 | > [default-extensions](https://cabal.readthedocs.io/en/stable/cabal-package.html#pkg-field-default-extensions)
86 | > section in the `.cabal file`.
87 | >
88 | > The list of language extensions can be found in the
89 | > [GHC manual](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts.html),
90 | > feel free to browse it, but don't worry about trying to memorize all the extensions.
91 |
92 |
93 | Of course, this example would work much better using `Either` and separating
94 | the division and printing à la 'functional core, imperative shell'. But as an example, it works.
95 | We created a custom exception and handled it specifically outside an `IO` block.
96 | However, we have not handled exceptions that might be raised by `putStrLn`.
97 | What if, for example, for some reason, we close the `stdout` handle before this block:
98 |
99 | ```hs
100 | main :: IO ()
101 | main = do
102 | hClose stdout
103 | catch
104 | ( do
105 | putStrLn "Going to print a number now."
106 | sayDiv2 7
107 | putStrLn "Did you like it?"
108 | )
109 | ( \case
110 | ErrZero ->
111 | hPutStrLn stderr "Error: we don't support dividing zeroes for some reason"
112 | ErrOdd n ->
113 | hPutStrLn stderr ("Error: " <> show n <> " is odd and cannot be divided by 2")
114 | )
115 | ```
116 |
117 | Our program will crash with an error:
118 |
119 | ```
120 | ghc: : hFlush: illegal operation (handle is closed)
121 | ```
122 |
123 | First, how do we know which exception we should handle? Some functions' documentation
124 | include this, but unfortunately, `putStrLn`'s does not. We could guess from the
125 | [list of instances](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#i:Exception)
126 | the `Exception` type class has; I think
127 | [`IOException`](https://hackage.haskell.org/package/base-4.16.4.0/docs/GHC-IO-Exception.html#t:IOException) fits. Now, how can we handle this case as well? We can chain catches:
128 |
129 | ```hs
130 | -- need to add these at the top
131 |
132 | {-# language ScopedTypeVariables #-}
133 |
134 | import GHC.IO.Exception (IOException(..))
135 |
136 | main :: IO ()
137 | main = do
138 | hClose stdout
139 | catch
140 | ( catch
141 | ( do
142 | putStrLn "Going to print a number now."
143 | sayDiv2 7
144 | putStrLn "Did you like it?"
145 | )
146 | ( \case
147 | ErrZero ->
148 | hPutStrLn stderr "Error: we don't support dividing zeroes for some reason"
149 | ErrOdd n ->
150 | hPutStrLn stderr ("Error: " <> show n <> " is odd and cannot be divided by 2")
151 | )
152 | )
153 | ( \(e :: IOException) ->
154 | -- we can check if the error was an illegal operation on the stderr handle
155 | if ioe_handle e /= Just stderr && ioe_type e /= IllegalOperation
156 | then pure () -- we can't write to stderr because it is closed
157 | else hPutStrLn stderr (displayException e)
158 | )
159 | ```
160 |
161 | > We use the `ScopedTypeVariables` to be able to specify types inside let expressions,
162 | > lambdas, pattern matching, and more.
163 |
164 | Or we could use the convenient function
165 | [`catches`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#v:catches)
166 | to pass a list of exception
167 | [handlers](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#t:Handler):
168 |
169 | ```hs
170 | main :: IO ()
171 | main = do
172 | hClose stdout
173 | catches
174 | ( do
175 | putStrLn "Going to print a number now."
176 | sayDiv2 7
177 | putStrLn "Did you like it?"
178 | )
179 | [ Handler $ \case
180 | ErrZero ->
181 | hPutStrLn stderr "Error: we don't support dividing zeroes for some reason"
182 | ErrOdd n ->
183 | hPutStrLn stderr ("Error: " <> show n <> " is odd and cannot be divided by 2")
184 |
185 | , Handler $ \(e :: IOException) ->
186 | -- we can check if the error was an illegal operation on the stderr handle
187 | if ioe_handle e /= Just stderr && ioe_type e /= IllegalOperation
188 | then pure () -- we can't write to stderr because it is closed
189 | else hPutStrLn stderr (displayException e)
190 | ]
191 | ```
192 |
193 | > As an aside, `Handler` uses a concept called
194 | > [existentially quantified types](https://en.m.wikibooks.org/wiki/Haskell/Existentially_quantified_types)
195 | > to hide inside it a function that takes an arbitrary type that implements `Exception`.
196 | > This is why we can encode a seemingly heterogeneous list of functions that handle exceptions
197 | > for `catches` to take as input.
198 | > This pattern is rarely useful, but I've included it here to avoid confusion.
199 |
200 | And if we wanted to catch any exception, we'd catch `SomeException`:
201 |
202 | ```hs
203 | main :: IO ()
204 | main = do
205 | hClose stdout
206 | catch
207 | ( do
208 | putStrLn "Going to print a number now."
209 | sayDiv2 7
210 | putStrLn "Did you like it?"
211 | )
212 | ( \(SomeException e) ->
213 | hPutStrLn stderr (show e)
214 | )
215 | ```
216 |
217 | This could also go in `catches` as the last element in the list if we wanted specialized
218 | handling for other scenarios.
219 |
220 | A couple more functions worth knowing are
221 | [`bracket`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#v:bracket)
222 | and [`finally`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Exception.html#v:finally).
223 | These functions can help us handle resource acquisition more safely when errors are present.
224 |
225 | ---
226 |
227 | In our `main` in the `app/Main.hs` file, we do a small ritual of opening and closing handles.
228 | Are there scenarios where we would clean-up after ourselves (meaning, close handles we've
229 | opened)? Which parts of the code could throw an exception? Which handles won't get closed?
230 |
231 | - Try to use `bracket` to make sure we always close a handle afterward, even if an exception
232 | is thrown, and avoid closing the handle for the `stdin` and `stdout` cases
233 | Hint
We might need to use continuation-passing style,
234 | passing a function that takes a parameter to a function that produces a parameter
235 | and calls it with that parameter.
236 |
237 | - How can we avoid duplicating the `outputHandle` code for the `Stdin` and `InputFile`
238 | branches? Hint
Use `let`.
239 |
240 | Answer
241 |
242 | ```hs
243 | import Control.Exception (bracket)
244 |
245 | main :: IO ()
246 | main = do
247 | ...
248 |
249 | ConvertSingle input output ->
250 | let
251 | -- Here, action is the next steps we want to do.
252 | -- It takes as input the values we produce,
253 | -- uses it, and then returns control for us to clean-up
254 | -- afterwards.
255 | withInputHandle :: (String -> Handle -> IO a) -> IO a
256 | withInputHandle action =
257 | case input of
258 | Stdin ->
259 | action "" stdin
260 | InputFile file ->
261 | bracket
262 | (openFile file ReadMode)
263 | hClose
264 | (action file)
265 |
266 | -- Note that in both functions our action can return any `a`
267 | -- it wants.
268 | withOutputHandle :: (Handle -> IO a) -> IO a
269 | withOutputHandle action =
270 | case output of
271 | Stdout ->
272 | action stdout
273 | OutputFile file -> do
274 | exists <- doesFileExist file
275 | shouldOpenFile <-
276 | if exists
277 | then confirm
278 | else pure True
279 | if shouldOpenFile
280 | then
281 | bracket (openFile file WriteMode) hClose action
282 | else
283 | exitFailure
284 | in
285 | withInputHandle (\title -> withOutputHandle . HsBlog.convertSingle title)
286 | ```
287 |
288 |
289 |
290 | There's actually a custom function that does a similar thing to
291 | `bracket (openFile file ) hClose`, it's called
292 | [withFile](https://hackage.haskell.org/package/base-4.17.0.0/docs/System-IO.html#v:withFile).
293 | Keep an eye out for functions that start with the prefix `with`; they are probably using the
294 | same pattern of continuation-passing style.
295 |
296 | ---
297 |
298 | ## Summary
299 |
300 | Exceptions are useful and often necessary when we work with `IO` and want to make sure
301 | our program is handling errors gracefully. They have an advantage over `Either` in that
302 | we can easily compose functions that may throw errors of different types, but also have
303 | a disadvantage of not encoding types as return values, and therefore does not force us
304 | to handle them.
305 |
306 | For Haskell, the language designers have made a choice for us by designing `IO` to
307 | use exceptions instead of `Either`. And this is what I would recommend for
308 | handling your own effectful computations. However, I think that `Either` is more
309 | appropriate for uneffectful code, because it forces us to acknowledge and handle errors
310 | (eventually), thus making our programs more robust. And also because we can only
311 | catch exceptions in `IO` code.
312 |
--------------------------------------------------------------------------------
/src/04-markup/01-data_type.md:
--------------------------------------------------------------------------------
1 | # Representing the markup language as a Haskell data type
2 |
3 | One of the clear differentiators between Haskell (also other ML-family of languages)
4 | and most mainstream languages is the ability to represent data precisely and succinctly.
5 |
6 | So how do we represent our markup language using Haskell?
7 |
8 | Previously, in our HTML builder library, we used `newtype`s to differentiate
9 | between HTML documents, structures, and titles, but we didn't really need to
10 | differentiate between different kinds of structures, such as paragraphs and headings,
11 | not without parsing the data, at least.
12 |
13 | In this case, we have a list of structures, and each structure could be
14 | one of a few specific options (a paragraph, a heading, a list, etc.),
15 | and we want to be able to know which structure is which so we can easily
16 | convert it into the equivalent HTML representation.
17 |
18 | For that, we have `data` definitions. Using `data` we can
19 | create custom types by grouping multiple types together and having
20 | alternative structures. Think of them as a combination of both structs and enums.
21 |
22 | `data` declarations look like this:
23 |
24 | ```hs
25 | data
26 | =
27 | |
28 | | ...
29 | ```
30 |
31 | It looks really similar to `newtype`, but there are two important
32 | differences:
33 |
34 | 1. In the `` part, we can write many types (Like `Int`, `String`, or `Bool`).
35 | For `newtype`s, we can only write one.
36 | 2. We can have alternative structures using `|`, `newtype`s have no
37 | alternatives.
38 |
39 | This is because `newtype` is used to provide a type-safe __alias__, and `data`
40 | is used to build a new **composite** type that can potentially have *alternatives*.
41 |
42 | Let's see a few examples of data types:
43 |
44 | 1. Bool
45 |
46 | ```hs
47 | data Bool
48 | = True
49 | | False
50 | ```
51 |
52 | We created a new data type named `Bool` with the possible values `True` or `False`.
53 | In this case, we only have *constructor* alternatives, and none of the constructors
54 | carry additional values. This is similar to enums in other languages.
55 |
56 | 2. Person
57 |
58 | ```hs
59 | data Person
60 | = Person String Int -- where the first is the name and the second is
61 | -- the age
62 | ```
63 |
64 | We created a new data type named `Person`. Values of the type `Person`
65 | look like this:
66 |
67 | ```
68 | Person
69 | ```
70 |
71 | For example:
72 |
73 | ```hs
74 | Person "Gil" 32
75 | ```
76 |
77 | In this case, we create a *composite* of multiple types without alternatives.
78 | This is similar to structs in other languages, but structs give each field
79 | a name, and here we distinguish them by position.
80 |
81 | Alternatively, Haskell has *syntactic sugar* for naming fields called **records**.
82 | The above definition can also be written like this:
83 |
84 |
85 | ```hs
86 | data Person
87 | = Person
88 | { name :: String
89 | , age :: Int
90 | }
91 | ```
92 |
93 | Values of this type can be written exactly as before,
94 |
95 | ```hs
96 | Person "Gil" 32
97 | ```
98 |
99 | Or with this syntax:
100 |
101 | ```hs
102 | Person { name = "Gil", age = 32 }
103 | ```
104 |
105 | Haskell will also generate functions that can be used to extract the fields from the composite type:
106 |
107 | ```hs
108 | name :: Person -> String
109 | age :: Person -> Int
110 | ```
111 |
112 | Which can be used like this:
113 |
114 | ```hs
115 | ghci> age (Person { name = "Gil", age = 32 })
116 | 32
117 | ```
118 |
119 | We even have a special syntax for updating specific fields in a record. Of course,
120 | we do not update records in place - we generate a new value instead.
121 |
122 | ```hs
123 | ghci> gil = Person { name = "Gil", age = 32 }
124 | ghci> age (gil { age = 33 })
125 | 33
126 | ghci> age gil
127 | 32
128 | ```
129 |
130 | Unfortunately, having specialized functions for each field also means that if we
131 | defined a different data type with the field `age`, the functions which GHC needs
132 | to generate will clash.
133 |
134 | The easiest way to solve this is to give fields unique names, for example
135 | by adding a prefix:
136 |
137 | ```hs
138 | data Person
139 | = Person
140 | { pName :: String
141 | , pAge :: Int
142 | }
143 | ```
144 |
145 | Another way is by using extensions to the Haskell language, which we will cover
146 | in later chapters.
147 |
148 | 3. Tuple
149 |
150 | ```hs
151 | data Tuple a b
152 | = Tuple a b
153 | ```
154 |
155 | This is pretty similar to `Person`, but we can plug any type we want
156 | for this definition. For example:
157 |
158 | ```hs
159 | Tuple "Clicked" True :: Tuple String Bool
160 |
161 | Tuple 'a' 'z' :: Tuple Char Char
162 | ```
163 |
164 | This type has special syntax in Haskell:
165 |
166 | ```hs
167 | ("Clicked", True) :: (String, Bool)
168 |
169 | ('a', 'z') :: (Char, Char)
170 | ```
171 |
172 | This `Tuple` definition is polymorphic; we define the structure but are able to
173 | plug different types into the structure to get concrete types. You can think of `Tuple`
174 | as a *template* for a data type waiting to be filled or as a **function** waiting
175 | for types as input in order to return a data type. We can even take a look at the "type"
176 | signature of `Tuple` in `ghci` using the `:kind` command.
177 |
178 | ```hs
179 | ghci> data Tuple a b = Tuple a b
180 | ghci> :kind Tuple
181 | Tuple :: * -> * -> *
182 | ```
183 |
184 | > #### Quick detour: Kinds
185 | >
186 | > The `:kind` command is called as such because the "type" of a type is called a **kind**.
187 | > Kinds can be one of two things, either a `*`, which means a saturated (or concrete) type,
188 | > such as `Int` or `Person`, or an `->` of two kinds, which is, as you might have guessed,
189 | > a type function, taking kind and returning a kind.
190 | >
191 | > Note that only types that have the kind `*` can have values. So, for example, while `Tuple Int`
192 | > is a valid Haskell concept that has the *kind* `* -> *`, and we can write code that will
193 | > work "generically" for all types that have a certain kind (e.g. `* -> *`), we cannot
194 | > construct a value that has the kind `* -> *`. All values have types and all
195 | > types that have values have the kind `*`.
196 | >
197 | > We will talk more about kinds later; let's focus on types for now!
198 |
199 | 4. Either
200 |
201 | ```hs
202 | data Either a b
203 | = Left a
204 | | Right b
205 | ```
206 |
207 | Similar to Tuple, but instead of having only one constructor, we have
208 | two. This means that we can choose which side we want. Here are a
209 | couple of values of type `Either String Int`:
210 |
211 | ```hs
212 | Left "Hello"
213 |
214 | Right 17
215 | ```
216 |
217 | This type is useful for modeling errors. Either we succeeded and got
218 | what we wanted (The `Right` constructor with the value), or we didn't
219 | and got an error instead (The `Left` constructor with a string or a
220 | custom error type).
221 |
222 | In our program, we use `data` types to model the different kinds of content types
223 | in our markup language. We tag each structure using the data constructor
224 | and provide the rest of the information (the paragraph text, the list items, etc.)
225 | in the `` section of the data declaration for each constructor:
226 |
227 | ```hs
228 | type Document
229 | = [Structure]
230 |
231 | data Structure
232 | = Heading Natural String
233 | | Paragraph String
234 | | UnorderedList [String]
235 | | OrderedList [String]
236 | | CodeBlock [String]
237 | ```
238 |
239 | Note: `Natural` is defined in the `base` package but not exported from `Prelude`.
240 | Find out which module to import `Natural` by using [Hoogle](https://hoogle.haskell.org).
241 |
242 | ---
243 |
244 | ### Exercises
245 |
246 | Represent the following markup documents as values of `Document`:
247 |
248 | 1. ```org
249 | Hello, world!
250 | ```
251 |
252 | 2. ```org
253 | * Welcome
254 |
255 | To this tutorial about Haskell.
256 | ```
257 |
258 | 3. ```org
259 | Remember that multiple lines with no separation
260 | are grouped together into a single paragraph
261 | but list items remain separate.
262 |
263 | # Item 1 of a list
264 | # Item 2 of the same list
265 | ```
266 |
267 | 4. ```org
268 | * Compiling programs with ghc
269 |
270 | Running ghc invokes the Glasgow Haskell Compiler (GHC),
271 | and can be used to compile Haskell modules and programs into native
272 | executables and libraries.
273 |
274 | Create a new Haskell source file named hello.hs, and write
275 | the following code in it:
276 |
277 | > main = putStrLn "Hello, Haskell!"
278 |
279 | Now, we can compile the program by invoking ghc with the file name:
280 |
281 | > ➜ ghc hello.hs
282 | > [1 of 1] Compiling Main ( hello.hs, hello.o )
283 | > Linking hello ...
284 |
285 | GHC created the following files:
286 |
287 | - hello.hi - Haskell interface file
288 | - hello.o - Object file, the output of the compiler before linking
289 | - hello (or hello.exe on Microsoft Windows) - A native runnable executable.
290 |
291 | GHC will produce an executable when the source file satisfies both conditions:
292 |
293 | # Defines the main function in the source file
294 | # Defines the module name to be Main or does not have a module declaration
295 |
296 | Otherwise, it will only produce the .o and .hi files.
297 | ```
298 |
299 | Solutions:
300 |
301 |
302 | Solution 1
303 |
304 | ```hs
305 | example1 :: Document
306 | example1 =
307 | [ Paragraph "Hello, world!"
308 | ]
309 | ```
310 |
311 |
312 |
313 |
314 | Solution 2
315 |
316 | ```hs
317 | example2 :: Document
318 | example2 =
319 | [ Heading 1 "Welcome"
320 | , Paragraph "To this tutorial about Haskell."
321 | ]
322 | ```
323 |
324 |
325 |
326 |
327 | Solution 3
328 |
329 | ```hs
330 | example3 :: Document
331 | example3 =
332 | [ Paragraph "Remember that multiple lines with no separation are grouped together into a single paragraph but list items remain separate."
333 | , OrderedList
334 | [ "Item 1 of a list"
335 | , "Item 2 of the same list"
336 | ]
337 | ]
338 | ```
339 |
340 |
341 |
342 |
343 | Solution 4
344 |
345 | ```hs
346 | example4 :: Document
347 | example4 =
348 | [ Heading 1 "Compiling programs with ghc"
349 | , Paragraph "Running ghc invokes the Glasgow Haskell Compiler (GHC), and can be used to compile Haskell modules and programs into native executables and libraries."
350 | , Paragraph "Create a new Haskell source file named hello.hs, and write the following code in it:"
351 | , CodeBlock
352 | [ "main = putStrLn \"Hello, Haskell!\""
353 | ]
354 | , Paragraph "Now, we can compile the program by invoking ghc with the file name:"
355 | , CodeBlock
356 | [ "➜ ghc hello.hs"
357 | , "[1 of 1] Compiling Main ( hello.hs, hello.o )"
358 | , "Linking hello ..."
359 | ]
360 | , Paragraph "GHC created the following files:"
361 | , UnorderedList
362 | [ "hello.hi - Haskell interface file"
363 | , "hello.o - Object file, the output of the compiler before linking"
364 | , "hello (or hello.exe on Microsoft Windows) - A native runnable executable."
365 | ]
366 | , Paragraph "GHC will produce an executable when the source file satisfies both conditions:"
367 | , OrderedList
368 | [ "Defines the main function in the source file"
369 | , "Defines the module name to be Main or does not have a module declaration"
370 | ]
371 | , Paragraph "Otherwise, it will only produce the .o and .hi files."
372 | ]
373 | ```
374 |
375 |
376 |
377 | Add a new module named `Markup` and add the data type definition to it.
378 | Note that in this case, we *do* want to export the constructors of `Structure`.
379 |
380 |
381 | Solution
382 |
383 | ```hs
384 | -- Markup.hs
385 |
386 | module Markup
387 | ( Document
388 | , Structure(..)
389 | )
390 | where
391 |
392 | import Numeric.Natural
393 |
394 | type Document
395 | = [Structure]
396 |
397 | data Structure
398 | = Heading Natural String
399 | | Paragraph String
400 | | UnorderedList [String]
401 | | OrderedList [String]
402 | | CodeBlock [String]
403 | ```
404 |
405 |
406 |
407 | ---
408 |
409 | ## Translating directly?
410 |
411 | You might ask, "Why do we even need to represent the markup as a type?
412 | Why don't we convert it into HTML as soon as we parse it
413 | instead?". That's a good question and a valid strategy. The reason we
414 | first represent it as a Haskell type is for flexibility and modularity.
415 |
416 | If the parsing code is coupled with HTML generation, we lose the
417 | ability to pre-process the markup document. For example, we might want
418 | to take only a small part of the document (for a summary) and present
419 | it, or create a table of content from headings. Or maybe we'd like to
420 | add other targets and not just HTML - maybe markdown format or a GUI reader?
421 |
422 | Parsing to an "abstract data type" (ADT) representation (one that does
423 | not contain the details of the language, for example, '#' for
424 | ordered lists) gives us the freedom to do so much more than just
425 | conversion to HTML that it's usually worth it, in my opinion, unless you
426 | really need to optimize the process.
427 |
--------------------------------------------------------------------------------
/src/08-testing.md:
--------------------------------------------------------------------------------
1 | # Testing
2 |
3 | We want to add some tests to our blog generator. At the very least
4 | a few regression tests to make sure that if we extend or change our markup parsing code,
5 | HTML generation code, or translation from markup to HTML code, and make a mistake, we'll
6 | have a safety net alerting us of issues.
7 |
8 | We will use the [Hspec](https://hspec.github.io/) testing framework to write our tests.
9 | There are other testing frameworks in Haskell, for example,
10 | [tasty](https://hackage.haskell.org/package/tasty), but I like Hspec's documentation,
11 | so we'll use that.
12 |
13 | ## Initial setup
14 |
15 | ### Cabal file additions
16 |
17 | We're going to define a new section in our `hs-blog-gen.cabal` file for our new test suite.
18 | This section is called `test-suite` and is fairly similar to the `library` and
19 | `executable` sections.
20 |
21 | The interfaces for how to define a test suite are described in the
22 | [Cabal documentation](https://cabal.readthedocs.io/en/stable/cabal-package.html#test-suites).
23 | We are going to use the `exitcode-stdio-1.0` interface. Let's go over the different settings
24 | and options:
25 |
26 | ```cabal
27 | test-suite hs-blog-gen-test
28 | import: common-settings
29 | type: exitcode-stdio-1.0
30 | hs-source-dirs: test
31 | main-is: Spec.hs
32 |
33 | -- other-modules:
34 | build-depends:
35 | base
36 | , hspec
37 | , hspec-discover
38 | , raw-strings-qq
39 | , hs-blog
40 | ghc-options:
41 | -O -threaded -rtsopts -with-rtsopts=-N
42 | build-tool-depends:
43 | hspec-discover:hspec-discover
44 | ```
45 |
46 | - `hs-source-dirs: test` - The directory of the source files for the test suite
47 | - `main-is: Spec.hs` - The entry point to the test suite
48 | - `other-modules` - The modules in our test suite.
49 | Currently commented out because we haven't added any yet
50 | - `build-depends` - The packages we are going to use:
51 | - [`base`](https://hackage.haskell.org/package/base) -
52 | The standard library for Haskell, as we've used before
53 | - [`hspec`](https://hackage.haskell.org/package/hspec) -
54 | The test framework we are going to use
55 | - [`hspec-discover`](https://hackage.haskell.org/package/hspec-discover) -
56 | Automatic discovery of Hspec tests
57 | - [`raw-strings-qq`](https://hackage.haskell.org/package/raw-strings-qq) -
58 | Additional syntax for writing raw string literals
59 | - `hs-blog` - Our library
60 | - [`ghc-options`](https://cabal.readthedocs.io/en/stable/cabal-package.html#pkg-field-ghc-options) -
61 | Extra options and flags for GHC:
62 | - [`-O`](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/using-optimisation.html#options-optimise) -
63 | Compile with optimizations
64 | - [`-threaded`](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/phases.html#ghc-flag--threaded) -
65 | Use the multi-core runtime instead of the single-core runtime. The multi-core
66 | runtime is generally a bit slower in my experience, but when writing code that actually uses
67 | multiple cores (such as a test framework that runs tests in parallel), it can give a good
68 | performance boost
69 | - [`-rtsopts`](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/phases.html#ghc-flag--rtsopts[=%E2%9F%A8none|some|all|ignore|ignoreAll%E2%9F%A9]) -
70 | Let us configure the Haskell runtime system by passing command-line arguments to our application
71 | - [`-with-rtsopts=-N`](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/phases.html#ghc-flag--with-rtsopts=%E2%9F%A8opts%E2%9F%A9) -
72 | Set specific default options for the program at link-time.
73 | Specifically, [`-N`](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/using-concurrent.html#rts-flag--N%20%E2%9F%A8x%E2%9F%A9)
74 | Sets the number of cores to use in our program
75 | - [`build-tool-depends`](https://cabal.readthedocs.io/en/stable/cabal-package.html#pkg-field-build-tool-depends) -
76 | Use a specific executable from a package dependency in aid of building the package.
77 | In this case, we are using the `hspec-discover` executable from the
78 | [`hspec-discover`](https://hackage.haskell.org/package/hspec-discover) package, which
79 | goes over the source directory for the tests, finds all of the `Spec` files
80 | and creates an entry point for the program that will run all the tests it discovered
81 |
82 |
83 | ### Hspec discovery
84 |
85 | For `hspec-discover` to work, we need to add the following
86 | to the "main" file of the test suite, for us, this is `test/Spec.hs`:
87 |
88 | ```hs
89 | {-# OPTIONS_GHC -F -pgmF hspec-discover #-}
90 | ```
91 |
92 | That's it! `hspec-discover` will automatically define a `main` for us.
93 | Now we can run the tests using `stack test` or `cabal test` (your choice).
94 | Because we haven't defined any tests, our output is:
95 |
96 | ```sh
97 | Finished in 0.0000 seconds
98 | 0 examples, 0 failures
99 | ```
100 |
101 | When we add new Hspec tests, `hspec-discover` will find and run them automatically
102 | (though we will still need to add them to the `other-modules` section in the cabal file).
103 |
104 | For `hspec-discover` to identify modules as test modules, the modules must follow
105 | a convention:
106 |
107 | 1. Their module names must end with `Spec`
108 | 2. They must define a value `spec :: Spec` (which describes the test) and export it
109 | outside of the module (by adding it to the export list of the module, for example)
110 |
111 | ## Writing tests
112 |
113 | Let's write our first test. We'll create a new module to test
114 | markup parsing. We'll call it `MarkupParsingSpec.hs`. We'll need
115 | the following imports as well:
116 |
117 | ```hs
118 | module MarkupParsingSpec where
119 |
120 | import Test.Hspec
121 | import HsBlog.Markup
122 | ```
123 |
124 | `Hspec` provides us with a monadic interface for describing, composing and
125 | nesting test specifications (`Spec`s).
126 |
127 | Using the `describe` function, we can
128 | describe a group of tests; using the `it` function, we can add a new test,
129 | and using a function like `shouldBe`, we can compare two values and make
130 | sure they are equal by using their `Eq` instance.
131 | If they are, the test will pass; if not, it will fail with a descriptive error.
132 |
133 | Let's try it and write a test that obviously fails!
134 |
135 | ```hs
136 | spec :: Spec
137 | spec = do
138 | describe "Markup parsing tests" $ do
139 | it "empty" $
140 | shouldBe
141 | (parse "")
142 | [Heading 1 "bug"]
143 | ```
144 |
145 | After adding the module to the `other-modules` list in the cabal file:
146 |
147 | ```hs
148 | other-modules:
149 | MarkupParsingSpec
150 | ```
151 |
152 | And running the tests, we get this output:
153 |
154 | ```hs
155 | MarkupParsing
156 | Markup parsing tests
157 | empty FAILED [1]
158 |
159 | Failures:
160 |
161 | test/MarkupParsingSpec.hs:10:7:
162 | 1) MarkupParsing, Markup parsing tests, empty
163 | expected: [Heading 1 "bug"]
164 | but got: []
165 |
166 | To rerun use: --match "/MarkupParsing/Markup parsing tests/empty/"
167 |
168 | Randomized with seed 763489823
169 |
170 | Finished in 0.0004 seconds
171 | 1 example, 1 failure
172 | ```
173 |
174 | The output describes which tests are running in a hierarchy tree (module, group, and test),
175 | whether the tests pass or fail, and if they fail, the output and the expected output.
176 |
177 | We can fix our test by matching the expected output:
178 |
179 | ```hs
180 | shouldBe
181 | (parse "")
182 | []
183 | ```
184 |
185 | Now, running the tests will produce:
186 |
187 | ```hs
188 | MarkupParsing
189 | Markup parsing tests
190 | empty
191 |
192 | Finished in 0.0001 seconds
193 | 1 example, 0 failures
194 | ```
195 |
196 | We can add a few more tests:
197 |
198 | ```hs
199 | it "paragraph" $
200 | shouldBe
201 | (parse "hello world")
202 | [Paragraph "hello world"]
203 |
204 | it "heading 1" $
205 | shouldBe
206 | (parse "* Heading 1")
207 | [Heading 1 "Heading 1"]
208 |
209 | it "code" $
210 | shouldBe
211 | (parse "> main = putStrLn \"hello world!\"")
212 | [CodeBlock ["main = putStrLn \"hello world!\""]]
213 | ```
214 |
215 | And run the tests again:
216 |
217 | ```sh
218 | MarkupParsing
219 | Markup parsing tests
220 | Test empty
221 | paragraph
222 | heading 1
223 | code
224 |
225 | Finished in 0.0003 seconds
226 | 4 examples, 0 failures
227 | ```
228 |
229 | This is the gist of writing unit tests with Hspec. It's important to note
230 | that we can nest `Spec`s that are declared with `describe` to create trees,
231 | and, of course, refactor and move things to different functions and modules
232 | to make our test suite better organized.
233 |
234 | For example, we can write our tests like this:
235 |
236 | ```hs
237 | spec :: Spec
238 | spec = do
239 | describe "Markup parsing tests" $ do
240 | simple
241 |
242 | simple :: Spec
243 | simple = do
244 | describe "simple" $ do
245 | it "empty" $
246 | shouldBe
247 | (parse "")
248 | []
249 |
250 | it "paragraph" $
251 | shouldBe
252 | (parse "hello world")
253 | [Paragraph "hello world"]
254 |
255 | it "heading 1" $
256 | shouldBe
257 | (parse "* Heading 1")
258 | [Heading 1 "Heading 1"]
259 |
260 | it "code" $
261 | shouldBe
262 | (parse "> main = putStrLn \"hello world!\"")
263 | [CodeBlock ["main = putStrLn \"hello world!\""]]
264 | ```
265 |
266 | Also, there are other "expectations" like `shouldBe` that we can use when writing tests.
267 | They are described in the [Hspec tutorial](https://hspec.github.io/expectations.html)
268 | and can be found in the
269 | [haddock documentation](https://hackage.haskell.org/package/hspec-expectations-0.8.2/docs/Test-Hspec-Expectations.html) as well.
270 |
271 | ### Raw strings
272 |
273 | If we want to write multi-line strings or avoid escaping strings as we did in the "code"
274 | test, we can use a library called
275 | [raw-strings-qq](https://hackage.haskell.org/package/raw-strings-qq)
276 | which uses a language extension called
277 | [`QuasiQuotes`](https://downloads.haskell.org/ghc/latest/docs/html/users_guide/exts/template_haskell.html#extension-QuasiQuotes).
278 | `QuasiQuotes` is a meta-programming extension that provides a mechanism for extending the
279 | syntax of Haskell.
280 |
281 | A quasi-quote has the form `[quoter| string |]`, where the quoter is the name
282 | of the function providing the syntax we wish to use, and the string is our input.
283 |
284 | In our case, we use the quoter `r`, which is defined in
285 | [raw-strings-qq](https://hackage.haskell.org/package/raw-strings-qq-1.1/docs/Text-RawString-QQ.html),
286 | and write any string we want, with multi-lines and unescaped characters!
287 | We could use this to write the tests
288 | [we previously wrote](04-markup/01-data_type.html#exercises):
289 |
290 | ```hs
291 | {-# language QuasiQuotes #-}
292 |
293 | ...
294 |
295 | import Text.RawString.QQ
296 |
297 | ...
298 |
299 | example3 :: String
300 | example3 = [r|
301 | Remember that multiple lines with no separation
302 | are grouped together into a single paragraph
303 | but list items remain separate.
304 |
305 | # Item 1 of a list
306 | # Item 2 of the same list
307 | |]
308 | ```
309 |
310 | And add multi-line tests:
311 |
312 | ```hs
313 | spec :: Spec
314 | spec = do
315 | describe "Markup parsing tests" $ do
316 | simple
317 | multiline
318 |
319 |
320 | multiline :: Spec
321 | multiline = do
322 | describe "Multi-line tests" $ do
323 | it "example3" $
324 | shouldBe
325 | (parse example3)
326 | example3Result
327 |
328 |
329 | example3 :: String
330 | example3 = [r|
331 | Remember that multiple lines with no separation
332 | are grouped together into a single paragraph
333 | but list items remain separate.
334 |
335 | # Item 1 of a list
336 | # Item 2 of the same list
337 | |]
338 |
339 | example3Result :: Document
340 | example3Result =
341 | [ Paragraph "Remember that multiple lines with no separation are grouped together into a single paragraph but list items remain separate."
342 | , OrderedList
343 | [ "Item 1 of a list"
344 | , "Item 2 of the same list"
345 | ]
346 | ]
347 | ```
348 |
349 | Running the tests:
350 |
351 | ```hs
352 | MarkupParsing
353 | Markup parsing tests
354 | simple
355 | Test empty
356 | paragraph
357 | heading 1
358 | code
359 | Multi-line tests
360 | example3
361 |
362 | Finished in 0.0004 seconds
363 | 5 examples, 0 failures
364 | ```
365 |
366 | ---
367 |
368 | **Exercise**: Add a test for the fourth example described in the
369 | [previous exercises](04-markup/01-data_type.html#exercises).
370 |
371 |
372 | Solution
373 |
374 | ```hs
375 | multiline :: Spec
376 | multiline = do
377 | describe "Multi-line tests" $ do
378 | it "example3" $
379 | shouldBe
380 | (parse example3)
381 | example3Result
382 |
383 | it "example4" $
384 | shouldBe
385 | (parse example4)
386 | example4Result
387 |
388 |
389 | example4 :: String
390 | example4 = [r|
391 | * Compiling programs with ghc
392 |
393 | Running ghc invokes the Glasgow Haskell Compiler (GHC),
394 | and can be used to compile Haskell modules and programs into native
395 | executables and libraries.
396 |
397 | Create a new Haskell source file named hello.hs, and write
398 | the following code in it:
399 |
400 | > main = putStrLn "Hello, Haskell!"
401 |
402 | Now, we can compile the program by invoking ghc with the file name:
403 |
404 | > ➜ ghc hello.hs
405 | > [1 of 1] Compiling Main ( hello.hs, hello.o )
406 | > Linking hello ...
407 |
408 | GHC created the following files:
409 |
410 | - hello.hi - Haskell interface file
411 | - hello.o - Object file, the output of the compiler before linking
412 | - hello (or hello.exe on Microsoft Windows) - A native runnable executable.
413 |
414 | GHC will produce an executable when the source file satisfies both conditions:
415 |
416 | # Defines the main function in the source file
417 | # Defines the module name to be Main or does not have a module declaration
418 |
419 | Otherwise, it will only produce the .o and .hi files.
420 | |]
421 |
422 | example4Result :: Document
423 | example4Result =
424 | [ Heading 1 "Compiling programs with ghc"
425 | , Paragraph "Running ghc invokes the Glasgow Haskell Compiler (GHC), and can be used to compile Haskell modules and programs into native executables and libraries."
426 | , Paragraph "Create a new Haskell source file named hello.hs, and write the following code in it:"
427 | , CodeBlock
428 | [ "main = putStrLn \"Hello, Haskell!\""
429 | ]
430 | , Paragraph "Now, we can compile the program by invoking ghc with the file name:"
431 | , CodeBlock
432 | [ "➜ ghc hello.hs"
433 | , "[1 of 1] Compiling Main ( hello.hs, hello.o )"
434 | , "Linking hello ..."
435 | ]
436 | , Paragraph "GHC created the following files:"
437 | , UnorderedList
438 | [ "hello.hi - Haskell interface file"
439 | , "hello.o - Object file, the output of the compiler before linking"
440 | , "hello (or hello.exe on Microsoft Windows) - A native runnable executable."
441 | ]
442 | , Paragraph "GHC will produce an executable when the source file satisfies both conditions:"
443 | , OrderedList
444 | [ "Defines the main function in the source file"
445 | , "Defines the module name to be Main or does not have a module declaration"
446 | ]
447 | , Paragraph "Otherwise, it will only produce the .o and .hi files."
448 | ]
449 | ```
450 |
451 |
452 |
453 | ---
454 |
455 | ## Parallel test execution
456 |
457 | Without further configuration, Hspec will run all
458 | of our tests on the main thread sequentially.
459 |
460 | There are a couple of ways to configure tests to run
461 | in parallel. One is to manually mark a `Spec`
462 | as parallel by passing it to the `parallel` function,
463 | and another is by creating a /hook/ that will apply
464 | `parallel` to each `Spec` automatically with
465 | `hspec-discover`.
466 |
467 | Consult the [Hspec manual](https://hspec.github.io/parallel-spec-execution.html#running-all-tests-in-parallel-with-hspec-discover)
468 | on this topic and try both methods. Remember that
469 | we already enabled the threaded runtime and set it to
470 | use multiple cores in the cabal file.
471 |
472 | ## Summary
473 |
474 | This chapter has been just the tip of the iceberg of the Haskell testing landscape.
475 | We haven't talked about
476 | [property testing](https://www.scs.stanford.edu/16wi-cs240h/slides/testing.html) or
477 | [golden testing](https://ro-che.info/articles/2017-12-04-golden-tests),
478 | testing expected failures, testing IO code, inspection testing, benchmarking, and more.
479 | There's just too much to cover!
480 |
481 | I hope this chapter
482 | provided you with the basics of how to start writing tests for your projects.
483 | Please consult the tutorial for your chosen testing framework, and read more about
484 | this very important subject on your own.
485 |
486 | > You can view the git commit of
487 | > [the changes we've made](https://github.com/soupi/learn-haskell-blog-generator/commit/da1615b6e0a2a4ff2728528240d790754853bf02)
488 | > and the [code up until now](https://github.com/soupi/learn-haskell-blog-generator/tree/da1615b6e0a2a4ff2728528240d790754853bf02).
489 |
--------------------------------------------------------------------------------
/src/05-glue/01-markup_to_html.md:
--------------------------------------------------------------------------------
1 | # Converting Markup to HTML
2 |
3 | One key part is missing before we can glue everything together, and that is
4 | to convert our `Markup` data types to `Html`.
5 |
6 | We'll start by creating a new module and importing both the `Markup` and the `Html` modules.
7 |
8 | ```hs
9 | module Convert where
10 |
11 | import qualified Markup
12 | import qualified Html
13 | ```
14 |
15 | ## Qualified Imports
16 |
17 | This time, we've imported the modules qualified. Qualified imports mean that
18 | instead of exposing the names that we've defined in the imported module to
19 | the general module namespace, they now have to be prefixed with the module name.
20 |
21 | For example, `parse` becomes `Markup.parse`.
22 | If we would've imported `Html.Internal` qualified, we'd have to write
23 | `Html.Internal.el`, which is a bit long.
24 |
25 | We can also give the module a new name with the `as` keyword:
26 |
27 | ```hs
28 | import qualified Html.Internal as HI
29 | ```
30 |
31 | And write `HI.el` instead.
32 |
33 | I like using qualified imports because readers do not have to guess where a
34 | name comes from. Some modules are even designed to be imported qualified.
35 | For example, the APIs of many container types, such as maps, sets, and vectors, are very similar.
36 | If we want to use multiple containers in a single module, we pretty much have
37 | to use qualified imports so that when we write a function such as `singleton`,
38 | which creates a container with a single value, GHC will know which `singleton`
39 | function we are referring to.
40 |
41 | Some people prefer to use import lists instead of qualified imports,
42 | because qualified names can be a bit verbose and noisy.
43 | I will often prefer qualified imports to import lists, but feel free to
44 | try both solutions and see which fits you better.
45 | For more information about imports,
46 | see this [wiki article](https://wiki.haskell.org/Import).
47 |
48 | ## Converting `Markup.Structure` to `Html.Structure`
49 |
50 | Converting a markup structure to an HTML structure is mostly straightforward
51 | at this point, we need to pattern match on the markup structure and use
52 | the relevant HTML API.
53 |
54 | ```hs
55 | convertStructure :: Markup.Structure -> Html.Structure
56 | convertStructure structure =
57 | case structure of
58 | Markup.Heading 1 txt ->
59 | Html.h1_ txt
60 |
61 | Markup.Paragraph p ->
62 | Html.p_ p
63 |
64 | Markup.UnorderedList list ->
65 | Html.ul_ $ map Html.p_ list
66 |
67 | Markup.OrderedList list ->
68 | Html.ol_ $ map Html.p_ list
69 |
70 | Markup.CodeBlock list ->
71 | Html.code_ (unlines list)
72 | ```
73 |
74 | Notice that running this code with `-Wall` will reveal that the pattern matching
75 | is *non-exhaustive*. This is because we don't currently have a way to build
76 | headings that are not `h1`. There are a few ways to handle this:
77 |
78 | - Ignore the warning - this will likely fail at runtime one day, and the user will be sad
79 | - Pattern match other cases and add a nice error with the `error` function - it has
80 | the same disadvantage above, but will also no longer notify of the unhandled
81 | cases at compile time
82 | - Pattern match and do the wrong thing - user is still sad
83 | - Encode errors in the type system using `Either`, we'll see how to do this in later
84 | chapters
85 | - Restrict the input - change `Markup.Heading` to not include a number but rather
86 | specific supported headings. This is a reasonable approach
87 | - Implement an HTML function supporting arbitrary headings. Should be straightforward
88 | to do
89 |
90 | > #### What are these `$`?
91 | >
92 | > The dollar sign (`$`) is an operator that we can use to group expressions, like we do with parenthesis.
93 | > we can replace the `$` with invisible parenthesis around the expressions to the left of it,
94 | > and around the expression to the right of it. So that:
95 | >
96 | > ```hs
97 | > Html.ul_ $ map Html.p_ list
98 | > ```
99 | > is understood as:
100 | > ```hs
101 | > (Html.ul_) (map Html.p_ list)
102 | > ```
103 | >
104 | > It is a function application operator, it applies the argument on the right of the dollar
105 | > to the function on the left of the dollar.
106 | >
107 | > `$` is right-associative and has very low precedence, which means that:
108 | > it groups to the right, and other operators bind more tightly.
109 | > For example the following expression:
110 | >
111 | > ```hs
112 | > filter (2<) $ map abs $ [-1, -2, -3] <> [4, 5, 6]
113 | > ```
114 | > is understood as:
115 | > ```hs
116 | > (filter (2<)) ((map abs) ([1, -2, 3] <> [-4, 5, 6]))
117 | > ```
118 | >
119 | > Which is also equivalent to the following code with less parenthesis:
120 | > ```hs
121 | > filter (2<) (map abs ([1, -2, 3] <> [-4, 5, 6]))
122 | > ```
123 | > See how information flows from right to left and that `<>` binds more tightly?
124 | >
125 | > This operator is fairly common in Haskell code and it helps us reduce some clutter,
126 | > but feel free to avoid it in favor of parenthesis if you'd like, it's not
127 | > like we're even saving keystrokes with `$`!
128 |
129 | ---
130 |
131 | Exercise: Implement `h_ :: Natural -> String -> Structure`
132 | which we'll use to define arbitrary headings (such as ``, ``, and so on).
133 |
134 | Solution
135 |
136 | ```hs
137 | import Numeric.Natural
138 |
139 | h_ :: Natural -> String -> Structure
140 | h_ n = Structure . el ("h" <> show n) . escape
141 | ```
142 |
143 | Don't forget to export it from `Html.hs`!
144 |
145 |
146 |
147 |
148 |
149 | Exercise: Fix `convertStructure` using `h_`.
150 |
151 |
152 | Solution
153 |
154 | ```hs
155 | convertStructure :: Markup.Structure -> Html.Structure
156 | convertStructure structure =
157 | case structure of
158 | Markup.Heading n txt ->
159 | Html.h_ n txt
160 |
161 | Markup.Paragraph p ->
162 | Html.p_ p
163 |
164 | Markup.UnorderedList list ->
165 | Html.ul_ $ map Html.p_ list
166 |
167 | Markup.OrderedList list ->
168 | Html.ol_ $ map Html.p_ list
169 |
170 | Markup.CodeBlock list ->
171 | Html.code_ (unlines list)
172 | ```
173 |
174 |
175 |
176 | ---
177 |
178 | ## Document -> Html
179 |
180 | To create an `Html` document, we need to use the `html_` function.
181 | This function expects two things: a `Title` and a `Structure`.
182 |
183 | For a title, we could just supply it from outside using the file name.
184 |
185 | To convert our markup `Document` (which is a list of markup `Structure`)
186 | to an HTML `Structure`, we need to convert each markup `Structure` and then
187 | concatenate them together.
188 |
189 | We already know how to convert each markup `Structure`; we can use the
190 | `convertStructure` function we wrote and `map`. This will provide
191 | us with the following function:
192 |
193 | ```
194 | map convertStructure :: Markup.Document -> [Html.Structure]
195 | ```
196 |
197 | To concatenate all of the `Html.Structure`, we could try to write a recursive
198 | function. However, we will quickly run into an issue
199 | with the base case: what to do when the list is empty?
200 |
201 | We could just provide a dummy `Html.Structure` that represents an empty
202 | HTML structure.
203 |
204 | Let's add this to `Html.Internal`:
205 |
206 | ```hs
207 | empty_ :: Structure
208 | empty_ = Structure ""
209 | ```
210 |
211 | ---
212 |
213 | Now we can write our recursive function. Try it!
214 |
215 | Solution
216 |
217 | ```hs
218 | concatStructure :: [Structure] -> Structure
219 | concatStructure list =
220 | case list of
221 | [] -> empty_
222 | x : xs -> x <> concatStructure xs
223 | ```
224 |
225 |
226 |
227 | ---
228 |
229 | Remember the `<>` function we implemented as an instance of the `Semigroup`
230 | type class? We mentioned that `Semigroup` is an **abstraction** for things
231 | that implements `(<>) :: a -> a -> a`, where `<>` is associative
232 | (`a <> (b <> c) = (a <> b) <> c`).
233 |
234 | It turns out that having an instance of `Semigroup` and also having a value that represents
235 | an "empty" value is a fairly common pattern. For example, a string can be concatenated,
236 | and the empty string can serve as an "empty" value.
237 | And this is actually a well known **abstraction** called **monoid**.
238 |
239 | ## Monoids
240 |
241 | Actually, "empty" isn't a very good description of what we want,
242 | and isn't very useful as an abstraction. Instead, we can describe it as
243 | an "identity" element that satisfies the following laws:
244 |
245 | - `x <> = x`
246 | - ` <> x = x`
247 |
248 | In other words, if we try to use this "empty" - this identity value,
249 | as one argument to `<>`, we will always get the other argument back.
250 |
251 | For `String`, the empty string, `""`, satisfies this:
252 |
253 | ```hs
254 | "" <> "world" = "world"
255 | "hello" <> "" = "hello"
256 | ```
257 |
258 | This is, of course, true for any value we'd write and not just "world" and "hello".
259 |
260 | Actually, if we move out of the Haskell world for a second, even integers
261 | with `+` as the associative binary operations `+` (in place of `<>`)
262 | and `0` in place of the identity member form a monoid:
263 |
264 | ```hs
265 | 17 + 0 = 17
266 | 0 + 99 = 99
267 | ```
268 |
269 | So integers together with the `+` operation form a semigroup, and
270 | together with `0` form a monoid.
271 |
272 | We learn new things from this:
273 |
274 | 1. A monoid is a more specific abstraction over semigroup; it builds on it
275 | by adding a new condition (the existence of an identity member)
276 | 2. This abstraction can be useful! We can write a general `concatStructure`
277 | that could work for any monoid
278 |
279 | And indeed, there exists a type class in `base` called `Monoid`, which has
280 | `Semigroup` as a **super class**.
281 |
282 | ```hs
283 | class Semigroup a => Monoid a where
284 | mempty :: a
285 | ```
286 |
287 | > Note: this is a simplified version. The
288 | > [actual](https://hackage.haskell.org/package/base-4.16.4.0/docs/Prelude.html#t:Monoid)
289 | > is a bit more complicated because of backward compatibility and performance reasons.
290 | > `Semigroup` was actually introduced in Haskell after `Monoid`!
291 |
292 | We could add an instance of `Monoid` for our HTML `Structure` data type:
293 |
294 |
295 | ```hs
296 | instance Monoid Structure where
297 | mempty = empty_
298 | ```
299 |
300 | And now, instead of using our own `concatStructure`, we can use the library function:
301 |
302 | ```hs
303 | mconcat :: Monoid a => [a] -> a
304 | ```
305 |
306 | Which could theoretically be implemented as:
307 |
308 | ```hs
309 | mconcat :: Monoid a => [a] -> a
310 | mconcat list =
311 | case list of
312 | [] -> mempty
313 | x : xs -> x <> mconcat xs
314 | ```
315 |
316 | Notice that because `Semigroup` is a *super class* of `Monoid`,
317 | we can still use the `<>` function from the `Semigroup` class
318 | without adding the `Semigroup a` constraint to the left side of `=>`.
319 | By adding the `Monoid a` constraint, we implicitly add a `Semigroup a`
320 | constraint as well!
321 |
322 | This `mconcat` function is very similar to the `concatStructure` function,
323 | but this one works for any `Monoid`, including `Structure`!
324 | Abstractions help us identify common patterns and **reuse** code!
325 |
326 | > Side note: integers with `+` and `0` aren't actually an instance of `Monoid` in Haskell.
327 | > This is because integers can also form a monoid with `*` and `1`! But **there can only
328 | > be one instance per type**. Instead, two other `newtype`s exist that provide that
329 | > functionality, [Sum](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-Monoid.html#t:Sum)
330 | > and [Product](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-Monoid.html#t:Product).
331 | > See how they can be used in `ghci`:
332 | >
333 | > ```hs
334 | > ghci> import Data.Monoid
335 | > ghci> Product 2 <> Product 3 -- note, Product is a data constructor
336 | > Product {getProduct = 6}
337 | > ghci> getProduct (Product 2 <> Product 3)
338 | > 6
339 | > ghci> getProduct $ mconcat $ map Product [1..5]
340 | > 120
341 | > ```
342 |
343 | ## Another abstraction?
344 |
345 | We've used `map` and then `mconcat` twice now. Surely there has to be a function
346 | that unifies this pattern. And indeed, it is called
347 | [`foldMap`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-Foldable.html#v:foldMap),
348 | and it works not only for lists but also for any data structure that can be "folded",
349 | or "reduced", into a summary value. This abstraction and type class is called **Foldable**.
350 |
351 | `Foldable` is actually the first type class we meet that constraints a **higher-kinded type**.
352 |
353 | > We talked briefly about kinds in Haskell previously, we mentioned that kinds are
354 | > "the types of types". Like how types of values help us formalize and differentiate
355 | > between different values like `True` and `'a'`, kinds help us formalize and differentiate
356 | > types like `Int` and `Maybe` (written here without a payload type on purpose!).
357 | >
358 | > `Maybe Bool`, for example, is a type with three possible values - `Nothing`,
359 | > `Just False`, and `Just True`. `Maybe` on its own cannot have values, because
360 | > we did not specify of which type the payload values are. But still, we can talk
361 | > about `Maybe` as a structure that can take a payload type and return a new type
362 | > for which we can have values (for example, `Maybe Bool`).
363 | >
364 | > In other words, `Maybe` is a **type function**.
365 | > It has the **kind** `* -> *` (Or `Type -> Type` in later GHC releases).
366 | >
367 | > By adding a "Kind" layer on top of the Haskell type system,
368 | > we can talk about type functions in a generic way, and in the `Foldable` class
369 | > we do just that. We talk about types of kind `* -> *`, like `Maybe`,
370 | > that can be "folded".
371 |
372 |
373 | To make `Foldable` a bit simpler to understand, we can look at `fold`:
374 |
375 | ```hs
376 | fold :: (Foldable t, Monoid m) => t m -> m
377 |
378 | -- compare with
379 | mconcat :: Monoid m => [m] -> m
380 | ```
381 |
382 | `mconcat` is just a specialized version of `fold` specific for lists.
383 |
384 | `fold` can be used for any pair of a data structure (of kind `* -> *`) that implements
385 | `Foldable`, and a payload type that implements `Monoid`. This
386 | could be `[]` (list without the payload type) with `Structure`,
387 | or `Maybe` with `Product Int`, or
388 | your new shiny and generic tree with `String` as the payload type.
389 |
390 | But note again that the type `t` for which we wish
391 | to implement an instance of the `Foldable`
392 | typeclass must be of *kind* `* -> *`. Like `[]`, `Maybe`, or `Product`.
393 |
394 | We can also consider `mconcat` as an *implementation* of `fold` specifically for lists,
395 | and actually if you look at the source code of the `Foldable` class in `base`, you will find:
396 |
397 | ```hs
398 | instance Foldable [] where
399 | ...
400 | fold = List.mconcat
401 | ```
402 |
403 | > Note that while a type of a list of ints can be written like this: `[Int]`,
404 | > it can also be written in prefix form like this: `[] Int`.
405 | > And also note that we must define a `Foldable` instance for `[]` without
406 | > mentioning a payload type! If we were to do the same for `Maybe`, we would write
407 | > `instance Foldable Maybe where` as well.
408 |
409 | And finally to give a counterexample, `Html` cannot be a `Foldable`, because it cannot carry a payload type.
410 | In other words, it has the kind `*`, and not `* -> *`.
411 |
412 | Let's look at another function from the `Foldable` class.
413 | `foldMap` is a function that allows us to apply a function to the
414 | values of the payload type of the `Foldable` type right before combining them
415 | with the `<>` function.
416 |
417 | ```hs
418 | foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
419 |
420 | -- compare to a specialized version with:
421 | -- - t ~ []
422 | -- - m ~ Html.Structure
423 | -- - a ~ Markup.Structure
424 | foldMap
425 | :: (Markup.Structure -> Html.Structure)
426 | -> [Markup.Structure]
427 | -> Html.Structure
428 | ```
429 |
430 | True to its name, it really "maps" before it "folds".
431 |
432 | You might pause here
433 | and think, "this 'map' we are talking about isn't specific for lists; maybe
434 | that's another abstraction?" Yes. It is actually a very important and
435 | fundamental abstraction called `Functor`.
436 | But I think we have enough abstractions for this chapter.
437 | We'll cover it in a later chapter!
438 |
439 | ## Finishing our conversion module
440 |
441 | Let's finish our code by writing `convert`:
442 |
443 | ```hs
444 | convert :: Html.Title -> Markup.Document -> Html.Html
445 | convert title = Html.html_ title . foldMap convertStructure
446 | ```
447 |
448 | Now we have a full implementation and can convert markup documents
449 | to HTML:
450 |
451 | ```hs
452 | -- Convert.hs
453 | module Convert where
454 |
455 | import qualified Markup
456 | import qualified Html
457 |
458 | convert :: Html.Title -> Markup.Document -> Html.Html
459 | convert title = Html.html_ title . foldMap convertStructure
460 |
461 | convertStructure :: Markup.Structure -> Html.Structure
462 | convertStructure structure =
463 | case structure of
464 | Markup.Heading n txt ->
465 | Html.h_ n txt
466 |
467 | Markup.Paragraph p ->
468 | Html.p_ p
469 |
470 | Markup.UnorderedList list ->
471 | Html.ul_ $ map Html.p_ list
472 |
473 | Markup.OrderedList list ->
474 | Html.ol_ $ map Html.p_ list
475 |
476 | Markup.CodeBlock list ->
477 | Html.code_ (unlines list)
478 | ```
479 |
480 | ## Summary
481 |
482 | We learned about:
483 |
484 | - Qualified imports
485 | - Ways to handle errors
486 | - The `Monoid` type class and abstraction
487 | - The `Foldable` type class and abstraction
488 | - Higher-kinded types
489 |
490 | Next, we are going to glue our functionality together and learn about
491 | I/O in Haskell!
492 |
493 | > You can view the git commit of
494 | > [the changes we've made](https://github.com/soupi/learn-haskell-blog-generator/commit/ad34f2264e9114f2d7436ff472c78da47055fcfe)
495 | > and the [code up until now](https://github.com/soupi/learn-haskell-blog-generator/tree/ad34f2264e9114f2d7436ff472c78da47055fcfe).
496 |
--------------------------------------------------------------------------------
/src/06-errors_and_files/01-either.md:
--------------------------------------------------------------------------------
1 | # Handling errors with Either
2 |
3 | There are quite a few ways to indicate and handle errors in Haskell.
4 | We are going to look at one solution: using the type
5 | [Either](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Either.html).
6 | Either is defined like this:
7 |
8 | ```hs
9 | data Either a b
10 | = Left a
11 | | Right b
12 | ```
13 |
14 | Simply put, a value of type `Either a b` can contain either a value of type `a`,
15 | or a value of type `b`.
16 | We can tell them apart from the constructor used.
17 |
18 | ```hs
19 | Left True :: Either Bool b
20 | Right 'a' :: Either a Char
21 | ```
22 |
23 | With this type, we can use the
24 | `Left` constructor to indicate failure with some error value attached,
25 | and the `Right` constructor with one type to represent success with the
26 | expected result.
27 |
28 | Since `Either` is polymorphic, we can use any two types to represent
29 | failure and success. It is often useful to describe the failure modes
30 | using an ADT.
31 |
32 | For example, let's say that we want to parse a `Char` as a decimal digit
33 | to an `Int`. This operation could fail if the Character is not a digit.
34 | We can represent this error as a data type:
35 |
36 | ```hs
37 | data ParseDigitError
38 | = NotADigit Char
39 | deriving Show
40 | ```
41 |
42 | And our parsing function can have the type:
43 |
44 | ```hs
45 | parseDigit :: Char -> Either ParseDigitError Int
46 | ```
47 |
48 | Now when we implement our parsing function, we can return `Left` on an error
49 | describing the problem, and `Right` with the parsed value on successful parsing:
50 |
51 |
52 | ```hs
53 | parseDigit :: Char -> Either ParseDigitError Int
54 | parseDigit c =
55 | case c of
56 | '0' -> Right 0
57 | '1' -> Right 1
58 | '2' -> Right 2
59 | '3' -> Right 3
60 | '4' -> Right 4
61 | '5' -> Right 5
62 | '6' -> Right 6
63 | '7' -> Right 7
64 | '8' -> Right 8
65 | '9' -> Right 9
66 | _ -> Left (NotADigit c)
67 | ```
68 |
69 | `Either a` is also an instance of `Functor` and `Applicative`,
70 | so we have some combinators to work with if we want to combine these
71 | kinds of computations.
72 |
73 | For example, if we had three characters and we wanted to try and parse
74 | each of them and then find the maximum between them; we could use the
75 | applicative interface:
76 |
77 | ```hs
78 | max3chars :: Char -> Char -> Char -> Either ParseDigitError Int
79 | max3chars x y z =
80 | (\a b c -> max a (max b c))
81 | <$> parseDigit x
82 | <*> parseDigit y
83 | <*> parseDigit z
84 | ```
85 |
86 |
87 | The `Functor` and `Applicative` interfaces of `Either a` allow us to
88 | apply functions to the payload values and **delay** the error handling to a
89 | later phase. Semantically, the first Either in order that returns a `Left`
90 | will be the return value. We can see how this works in the implementation
91 | of the applicative instance:
92 |
93 | ```hs
94 | instance Applicative (Either e) where
95 | pure = Right
96 | Left e <*> _ = Left e
97 | Right f <*> r = fmap f r
98 | ```
99 |
100 | At some point, someone will actually want to **inspect** the result
101 | and see if we get an error (with the `Left` constructor) or the expected value
102 | (with the `Right` constructor) and they can do that by pattern-matching the result.
103 |
104 | ## Applicative + Traversable
105 |
106 | The `Applicative` interface of `Either` is very powerful and can be combined
107 | with another abstraction called
108 | [`Traversable`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-Traversable.html#g:1) -
109 | for data structures that can be traversed from left to right, like a linked list or a binary tree.
110 | With these, we can combine an unspecified amount of values such as `Either ParseDigitError Int`,
111 | as long as they are all in a data structure that implements `Traversable`.
112 |
113 | Let's see an example:
114 |
115 | ```hs
116 | ghci> :t "1234567"
117 | "1234567" :: String
118 | -- remember, a String is an alias for a list of Char
119 | ghci> :info String
120 | type String :: *
121 | type String = [Char]
122 | -- Defined in ‘GHC.Base’
123 |
124 | ghci> :t map parseDigit "1234567"
125 | map parseDigit "1234567" :: [Either ParseDigitError Int]
126 | ghci> map parseDigit "1234567"
127 | [Right 1,Right 2,Right 3,Right 4,Right 5,Right 6,Right 7]
128 |
129 | ghci> :t sequenceA
130 | sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
131 | -- Substitute `t` with `[]`, and `f` with `Either Error` for a specialized version
132 |
133 | ghci> sequenceA (map parseDigit "1234567")
134 | Right [1,2,3,4,5,6,7]
135 |
136 | ghci> map parseDigit "1a2"
137 | [Right 1,Left (NotADigit 'a'),Right 2]
138 | ghci> sequenceA (map parseDigit "1a2")
139 | Left (NotADigit 'a')
140 | ```
141 |
142 | The pattern of doing `map` and then `sequenceA` is another function called `traverse`:
143 |
144 | ```hs
145 | ghci> :t traverse
146 | traverse
147 | :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
148 | ghci> traverse parseDigit "1234567"
149 | Right [1,2,3,4,5,6,7]
150 | ghci> traverse parseDigit "1a2"
151 | Left (NotADigit 'a')
152 | ```
153 |
154 | We can use `traverse` on any two types where one implements the `Applicative`
155 | interface, like `Either a` or `IO`, and the other implements the `Traversable` interface,
156 | like `[]` (linked lists) and
157 | [`Map k`](https://hackage.haskell.org/package/containers-0.6.5.1/docs/Data-Map-Strict.html#t:Map)
158 | (also known as a dictionary in other languages - a mapping from keys to values).
159 | For example, using `IO` and `Map`. Note that we can construct a `Map` data structure
160 | from a list of tuples using the
161 | [`fromList`](https://hackage.haskell.org/package/containers-0.6.5.1/docs/Data-Map-Strict.html#v:fromList)
162 | function - the first value in the tuple is the key, and the second is the type.
163 |
164 | ```hs
165 | ghci> import qualified Data.Map as M -- from the containers package
166 |
167 | ghci> file1 = ("output/file1.html", "input/file1.txt")
168 | ghci> file2 = ("output/file2.html", "input/file2.txt")
169 | ghci> file3 = ("output/file3.html", "input/file3.txt")
170 | ghci> files = M.fromList [file1, file2, file3]
171 | ghci> :t files :: M.Map FilePath FilePath -- FilePath is an alias of String
172 | files :: M.Map FilePath FilePath :: M.Map FilePath FilePath
173 |
174 | ghci> readFiles = traverse readFile
175 | ghci> :t readFiles
176 | readFiles :: Traversable t => t FilePath -> IO (t String)
177 |
178 | ghci> readFiles files
179 | fromList [("output/file1.html","I'm the content of file1.txt\n"),("output/file2.html","I'm the content of file2.txt\n"),("output/file3.html","I'm the content of file3.txt\n")]
180 | ghci> :t readFiles files
181 | readFiles files :: IO (Map String String)
182 | ```
183 |
184 | Above, we created a function `readFiles` that will take a mapping from *output file path*
185 | to *input file path* and returns an IO operation that, when run will read the input files
186 | and replace their contents right there in the map! Surely this will be useful later.
187 |
188 | ## Multiple errors
189 |
190 | Note, since `Either` has the kind `* -> * -> *` (it takes two type
191 | parameters) `Either` cannot be an instance of `Functor` or `Applicative`:
192 | instances of these type classes must have the
193 | kind `* -> *`.
194 | Remember that when we look at a type class function signature like:
195 |
196 | ```hs
197 | fmap :: Functor f => (a -> b) -> f a -> f b
198 | ```
199 |
200 | And if we want to implement it for a specific type (in place of the `f`),
201 | we need to be able to *substitute* the `f` with the target type. If we'd try
202 | to do it with `Either` we would get:
203 |
204 | ```hs
205 | fmap :: (a -> b) -> Either a -> Either b
206 | ```
207 |
208 | And neither `Either a` or `Either b` are *saturated*, so this won't type check.
209 | For the same reason, if we'll try to substitute `f` with, say, `Int`, we'll get:
210 |
211 | ```hs
212 | fmap :: (a -> b) -> Int a -> Int b
213 | ```
214 |
215 | Which also doesn't make sense.
216 |
217 | While we can't use `Either`, we can use `Either e`, which has the kind
218 | `* -> *`. Now let's try substituting `f` with `Either e` in this signature:
219 |
220 | ```hs
221 | liftA2 :: Applicative => (a -> b -> c) -> f a -> f b -> f c
222 | ```
223 |
224 | And we'll get:
225 |
226 | ```hs
227 | liftA2 :: (a -> b -> c) -> Either e a -> Either e b -> Either e c
228 | ```
229 |
230 | What this teaches us is that we can only use the applicative interface to
231 | combine two *`Either`s with the same type for the `Left` constructor*.
232 |
233 | So what can we do if we have two functions that can return different errors?
234 | There are a few approaches; the most prominent ones are:
235 |
236 | 1. Make them return the same error type. Write an ADT that holds all possible
237 | error descriptions. This can work in some cases but isn't always ideal.
238 | For example, a user calling `parseDigit` shouldn't be forced to
239 | handle a possible case that the input might be an empty string
240 | 2. Use a specialized error type for each type, and when they are composed together,
241 | map the error type of each function to a more general error type. This can
242 | be done with the function
243 | [`first`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Data-Bifunctor.html#v:first)
244 | from the `Bifunctor` type class
245 |
246 | ## Monadic interface
247 |
248 | The applicative interface allows us to lift a function to work on multiple
249 | `Either` values (or other applicative functor instances such as `IO` and `Parser`).
250 | But more often than not, we'd like to use a value from one computation
251 | that might return an error in another computation that might return an error.
252 |
253 | For example, a compiler such as GHC operates in stages, such as lexical analysis,
254 | parsing, type-checking, and so on. Each stage depends on the output of the stage
255 | before it, and each stage might fail. We can write the types for these functions:
256 |
257 | ```hs
258 | tokenize :: String -> Either Error [Token]
259 |
260 | parse :: [Token] -> Either Error AST
261 |
262 | typecheck :: AST -> Either Error TypedAST
263 | ```
264 |
265 | We want to compose these functions so that they work in a chain. The output of `tokenize`
266 | goes to `parse`, and the output of `parse` goes to `typecheck`.
267 |
268 | We know that we can lift a function over an `Either` (and other functors),
269 | we can also lift a function that returns an `Either`:
270 |
271 | ```hs
272 | -- reminder the type of fmap
273 | fmap :: Functor f => (a -> b) -> f a -> f b
274 | -- specialized for `Either Error`
275 | fmap :: (a -> b) -> Either Error a -> Either Error b
276 |
277 | -- here, `a` is [Token] and `b` is `Either Error AST`:
278 |
279 | > fmap parse (tokenize string) :: Either Error (Either Error AST)
280 | ```
281 |
282 | While this code compiles, it isn't great, because we are building
283 | layers of `Either Error`, and we can't use this trick again with
284 | `typecheck`! `typecheck` expects an `AST`, but if we try to fmap it
285 | on `fmap parse (tokenize string)`, the `a` will be `Either Error AST`
286 | instead.
287 |
288 | What we would really like is to flatten this structure instead of nesting it.
289 | If we look at the kind of values `Either Error (Either Error AST)` could have,
290 | it looks something like this:
291 |
292 | - `Left `
293 | - `Right (Left error)`
294 | - `Right (Right )`
295 |
296 | ---
297 |
298 | **Exercise**: What if we just used pattern matching for this instead? What would this look like?
299 |
300 | Solution
301 |
302 | ```hs
303 | case tokenize string of
304 | Left err ->
305 | Left err
306 | Right tokens ->
307 | case parse tokens of
308 | Left err ->
309 | Left err
310 | Right ast ->
311 | typecheck ast
312 | ```
313 |
314 | If we run into an error in a stage, we return that error and stop. If we succeed, we
315 | use the value in the next stage.
316 |
317 |
318 |
319 | ---
320 |
321 | Flattening this structure for `Either` is very similar to that last part - the body
322 | of the `Right tokens` case:
323 |
324 | ```hs
325 | flatten :: Either e (Either e a) -> Either e a
326 | flatten e =
327 | case e of
328 | Left l -> Left l
329 | Right x -> x
330 | ```
331 |
332 | Because we have this function, we can now use it on the output of
333 | `fmap parse (tokenize string) :: Either Error (Either Error AST)`
334 | from before:
335 |
336 | ```
337 | > flatten (fmap parse (tokenize string)) :: Either Error AST
338 | ```
339 |
340 | And now, we can use this function again to compose with `typecheck`:
341 |
342 | ```hs
343 | > flatten (fmap typecheck (flatten (fmap parse (tokenize string)))) :: Either Error TypedAST
344 | ```
345 |
346 | This `flatten` + `fmap` combination looks like a recurring pattern which
347 | we can combine into a function:
348 |
349 | ```hs
350 | flatMap :: (a -> Either e b) -> Either e a -> Either e b
351 | flatMap func val = flatten (fmap func val)
352 | ```
353 |
354 | And now, we can write the code this way:
355 |
356 | ```hs
357 | > flatMap typecheck (flatMap parse (tokenize string)) :: Either Error TypedAST
358 |
359 | -- Or using backticks syntax to convert the function to infix form:
360 | > typecheck `flatMap` parse `flatMap` tokenize string
361 |
362 | -- Or create a custom infix operator: (=<<) = flatMap
363 | > typeCheck =<< parse =<< tokenize string
364 | ```
365 |
366 |
367 | This function, `flatten` (and `flatMap` as well), have different names in Haskell.
368 | They are called
369 | [`join`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Monad.html#v:join)
370 | and [`=<<`](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Monad.html#v:-61--60--60-)
371 | (pronounced "reverse bind"),
372 | and they are the essence of another incredibly useful abstraction in Haskell.
373 |
374 | If we have a type that can implement:
375 |
376 | 1. The `Functor` interface, specifically the `fmap` function
377 | 2. The `Applicative` interface, most importantly the `pure` function
378 | 3. This `join` function
379 |
380 | They can implement an instance of the `Monad` type class.
381 |
382 | With functors, we were able to "lift" a function to work over the type implementing the functor type class:
383 |
384 | ```hs
385 | fmap :: (a -> b) -> f a -> f b
386 | ```
387 |
388 | With applicative functors we were able to "lift" a function of multiple arguments
389 | over multiple values of a type implementing the applicative functor type class,
390 | and also lift a value into that type:
391 |
392 | ```hs
393 | pure :: a -> f a
394 |
395 | liftA2 :: (a -> b -> c) -> f a -> f b -> f c
396 | ```
397 |
398 | With monads we can now flatten (or "join" in Haskell terminology) types that implement
399 | the `Monad` interface:
400 |
401 | ```hs
402 | join :: m (m a) -> m a
403 |
404 | -- this is =<< with the arguments reversed, pronounced "bind"
405 | (>>=) :: m a -> (a -> m b) -> m b
406 | ```
407 |
408 | With `>>=`, we can write our compilation pipeline from before in a left-to-right
409 | manner, which seems to be more popular for monads:
410 |
411 | ```hs
412 | > tokenize string >>= parse >>= typecheck
413 | ```
414 |
415 | We had already met this function before when we talked about `IO`. Yes,
416 | `IO` also implements the `Monad` interface. The monadic interface for `IO`
417 | helped us with creating a proper ordering of effects.
418 |
419 | The essence of the `Monad` interface is the `join`/`>>=` functions, and as we've seen
420 | we can implement `>>=` in terms of `join`, we can also implement `join` in terms
421 | of `>>=` (try it!).
422 |
423 | The monadic interface can mean very different things for different types. For `IO` this
424 | is ordering of effects, for `Either` it is early cutoff,
425 | for [`Logic`](https://hackage.haskell.org/package/logict-0.7.1.0) this means backtracking computation, etc.
426 |
427 | Again, don't worry about analogies and metaphors; focus on the API and the
428 | [laws](https://wiki.haskell.org/Monad_laws).
429 |
430 | > Hey, did you check the monad laws? left identity, right identity, and associativity? We've already
431 | > discussed a type class with exactly these laws - the `Monoid` type class. Maybe this is related
432 | > to the famous quote about monads being just monoids in something something...
433 |
434 | ### Do notation?
435 |
436 | Remember the [do notation](../05-glue/02-io.html#do-notation)? It turns out it works for any type that is
437 | an instance of `Monad`. How cool is that? Instead of writing:
438 |
439 | ```hs
440 | pipeline :: String -> Either Error TypedAST
441 | pipeline string =
442 | tokenize string >>= \tokens ->
443 | parse tokens >>= \ast ->
444 | typecheck ast
445 | ```
446 |
447 | We can write:
448 |
449 | ```hs
450 | pipeline :: String -> Either Error TypedAST
451 | pipeline string = do
452 | tokens <- tokenize string
453 | ast <- parse tokens
454 | typecheck ast
455 | ```
456 |
457 | And it will work! Still, in this particular case, `tokenize string >>= parse >>= typecheck`
458 | is so concise it can only be beaten by using
459 | [>=>](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Monad.html#v:-62--61--62-)
460 | or
461 | [<=<](https://hackage.haskell.org/package/base-4.16.4.0/docs/Control-Monad.html#v:-60--61--60-):
462 |
463 | ```hs
464 | (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
465 | (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
466 |
467 | -- compare with function composition:
468 | (.) :: (b -> c) -> (a -> b) -> a -> c
469 | ```
470 |
471 | ```hs
472 | pipeline = tokenize >=> parse >=> typecheck
473 | ```
474 |
475 | or
476 |
477 | ```hs
478 | pipeline = typecheck <=< parse <=< tokenize
479 | ```
480 |
481 | Haskell's ability to create very concise code using abstractions is
482 | great once one is familiar with the abstractions. Knowing the monad abstraction,
483 | we are now already familiar with the core composition API of many libraries - for example:
484 |
485 | - [Concurrent](https://hackage.haskell.org/package/stm)
486 | and [asynchronous programming](https://hackage.haskell.org/package/async)
487 | - [Web programming](https://gilmi.me/blog/post/2020/12/05/scotty-bulletin-board)
488 | - [Testing](http://hspec.github.io/)
489 | - [Emulating stateful computation](https://hackage.haskell.org/package/mtl-2.3.1/docs/Control-Monad-State-Lazy.html#g:2)
490 | - [sharing environment between computations](https://hackage.haskell.org/package/mtl-2.3.1/docs/Control-Monad-Reader.html#g:2)
491 | - and many more.
492 |
493 | ## Summary
494 |
495 | Using `Either` for error handling is useful for two reasons:
496 |
497 | 1. We encode possible errors using types, and we **force users to acknowledge and handle** them, thus
498 | making our code more resilient to crashes and bad behaviours
499 | 2. The `Functor`, `Applicative`, and `Monad` interfaces provide us with mechanisms for
500 | **composing** functions that might fail (almost) effortlessly - reducing boilerplate while
501 | maintaining strong guarantees about our code and delaying the need to handle errors until
502 | it is appropriate
503 |
--------------------------------------------------------------------------------
/src/03-html/04-safer_construction.md:
--------------------------------------------------------------------------------
1 | # Safer HTML construction with types
2 |
3 | In this section, we'll learn how to create our own distinguished types
4 | for HTML, and how they can help us avoid the invalid construction of HTML strings.
5 |
6 | There are a few ways of defining new types in Haskell; in this section,
7 | we are going to meet two ways: `newtype` and `type`.
8 |
9 | ## `newtype`
10 |
11 | A `newtype` declaration is a way to define a new, distinct type for an existing set of values.
12 | This is useful when we want to reuse existing values but give them a different meaning
13 | and ensure we can't mix the two.
14 | For example, we can represent seconds, minutes, grams, and yens using integer values,
15 | but we don't want to mix grams and seconds accidentally.
16 |
17 | In our case, we want to represent structured HTML using textual values,
18 | but distinguish them from everyday strings that are not valid HTML.
19 |
20 | A `newtype` declaration looks like this:
21 |
22 | ```
23 | newtype =
24 | ```
25 |
26 | For example, in our case, we can define a distinct type for `Html` like this:
27 |
28 | ```hs
29 | newtype Html = Html String
30 | ```
31 |
32 | The first `Html`, to the left of the equals sign, lives in the _types_
33 | name space, meaning that you will only see that name to the right of a
34 | double-colon sign (`::`).
35 |
36 | The second `Html` lives in the _expressions_ (or terms/values) namespace,
37 | meaning that you will see it where you expect expressions (we'll touch where
38 | exactly that can be in a moment).
39 |
40 | The two names, `` and ``, do not have to be the
41 | same, but they often are. And note that both have to start with a
42 | capital letter.
43 |
44 | The right-hand side of the newtype declaration describes the shape of a
45 | value of that type. In our case, we expect a value of
46 | type `Html` to have the constructor `Html` and then an expression of
47 | type string, for example: `Html "hello"` or `Html ("hello " <>
48 | "world")`.
49 |
50 | You can think of the constructor as a function that takes the argument
51 | and returns something of our new type:
52 |
53 | ```hs
54 | Html :: String -> Html
55 | ```
56 |
57 | **Note**: We cannot use an expression of type `Html` the same way we'd
58 | use a `String`. So `"hello " <> Html "world"` would fail at type
59 | checking.
60 |
61 | This is useful when we want *encapsulation*. We can define and use
62 | existing representation and functions for our underlying type, but not
63 | mix them with other unrelated (to our domain) types. Similar as
64 | meters and feet can both be numbers, but we don't want to accidentally
65 | add feet to meters without any conversion.
66 |
67 | ---
68 |
69 | For now, let's create a couple of types for our use case.
70 | We want two separate types to represent:
71 |
72 | 1. A complete Html document
73 | 2. A type for html structures such as headings and paragraphs that can go inside the tag
74 |
75 | We want them to be distinct because we don't want to mix them.
76 |
77 |
78 | Solution
79 |
80 | ```hs
81 | newtype Html = Html String
82 |
83 | newtype Structure = Structure String
84 | ```
85 |
86 |
87 |
88 | ---
89 |
90 | ## Using `newtype`s
91 |
92 | To use the underlying type that the newtype wraps, we first
93 | need to extract it out of the type. We do this using pattern matching.
94 |
95 | Pattern matching can be used in two ways, in case-expressions and in
96 | function definitions.
97 |
98 | 1. case expressions are kind of beefed up switch expressions and look like this:
99 |
100 | ```
101 | case of
102 | ->
103 | ...
104 | ->
105 | ```
106 |
107 | The `` is the thing we want to unpack, and the `pattern`
108 | is its concrete shape. For example, if we wanted to extract the `String`
109 | out of the type `Structure` we defined in the exercise above, we do:
110 |
111 | ```hs
112 | getStructureString :: Structure -> String
113 | getStructureString struct =
114 | case struct of
115 | Structure str -> str
116 | ```
117 |
118 | This way, we can extract the `String` out of `Structure` and return
119 | it.
120 |
121 | > In later chapters we'll introduce `data` declarations (which are kind of
122 | > a struct + enum chimera), where we can define multiple constructors to
123 | > a type. Then the multiple patterns of a case expression will make more
124 | > sense.
125 |
126 | 2. Alternatively, when declaring a function, we can also use pattern matching on the
127 | arguments:
128 |
129 | ```
130 | func =
131 | ```
132 |
133 | For example:
134 |
135 | ```hs
136 | getStructureString :: Structure -> String
137 | getStructureString (Structure str) = str
138 | ```
139 |
140 | Using the types we created, we can change the HTML functions we've defined before,
141 | namely `html_`, `body_`, `p_`, etc., to operate on these types instead of `String`s.
142 |
143 | But first, let's meet another operator that will make our code more concise.
144 |
145 | One very cool thing about `newtype` is that wrapping and extracting expressions doesn't actually
146 | have a performance cost! The compiler knows how to remove any wrapping and extraction
147 | of the `newtype` constructor and use the underlying type.
148 |
149 | The new type and the constructor we defined are only there to help us *distinguish* between
150 | the type we created and the underlying type when *we write our code*, they are not
151 | needed *when the code is running*.
152 |
153 | `newtype`s provide us with type safety with no performance penalty!
154 |
155 | ## Chaining functions
156 |
157 | Another interesting and extremely common operator
158 | (which is a regular library function in Haskell) is `.` (pronounced compose).
159 | This operator was made to look like the composition operator
160 | you may know from math (`∘`).
161 |
162 | Let's look at its type and implementation:
163 |
164 | ```hs
165 | (.) :: (b -> c) -> (a -> b) -> a -> c
166 | (.) f g x = f (g x)
167 | ```
168 |
169 | Compose takes 3 arguments: two functions (named `f` and `g` here) and
170 | a third argument named `x`. It then passes the argument `x` to the second
171 | function `g` and calls the first function `f` with the result of `g x`.
172 |
173 | Note that `g` takes as input something of the type
174 | `a` and returns something of the type `b`, and `f` takes
175 | something of the type `b` and returns something of the type `c`.
176 |
177 | Another important thing to note is that types that start with
178 | a _lowercase letter_ are **type variables**.
179 | Think of them as similar to regular variables. Just like
180 | `content` could be any string, like `"hello"` or `"world"`, a type variable
181 | can be any type: `Bool`, `String`, `String -> String`, etc.
182 | This ability is called *parametric polymorphism* (other languages often call this generics).
183 |
184 | The catch is that type variables must match in a signature, so if for
185 | example, we write a function with the type signature `a -> a`, the
186 | input type and the return type **must** match, but it could be
187 | any type - we cannot know what it is. So the only way to implement a
188 | function with that signature is:
189 |
190 | ```hs
191 | id :: a -> a
192 | id x = x
193 | ```
194 |
195 | `id`, short for the identity function, returns the exact value it received.
196 | If we tried any other way, for example, returning some made-up value
197 | like `"hello"`, or trying to use `x` as a value of a type we know, like
198 | writing `x + x`, the type checker will complain.
199 |
200 | Also, remember that `->` is right-associative? This signature is equivalent to:
201 |
202 | ```hs
203 | (.) :: (b -> c) -> (a -> b) -> (a -> c)
204 | ```
205 |
206 | Doesn't it look like a function that takes two functions and returns a
207 | third function that is the composition of the two?
208 |
209 | We can now use this operator to change our HTML functions. Let's start
210 | with one example: `p_`.
211 |
212 | Before, we had:
213 |
214 | ```hs
215 | p_ :: String -> String
216 | p_ = el "p"
217 | ```
218 |
219 | And now, we can write:
220 |
221 | ```hs
222 | p_ :: String -> Structure
223 | p_ = Structure . el "p"
224 | ```
225 |
226 | The function `p_` will take an arbitrary `String`, which is the content
227 | of the paragraph we wish to create, wrap it in `` and `
` tags,
228 | and then wrap it in the `Structure` constructor to produce the
229 | output type `Structure` (remember: newtype constructors can be used as functions!).
230 |
231 | Let's take a deeper look at the types:
232 |
233 | - `Structure :: String -> Structure`
234 | - `el "p" :: String -> String`
235 | - `(.) :: (b -> c) -> (a -> b) -> (a -> c)`
236 | - `Structure . el "p" :: String -> Structure`
237 |
238 | Let's see why the expression `Structure . el "p"` type checks,
239 | and why its type is `String -> Structure`.
240 |
241 | ### Type checking with pen and paper
242 |
243 | If we want to figure out if and how exactly an expression type-checks,
244 | we can do that rather systematically. Let's look at an example
245 | where we try and type-check this expression:
246 |
247 | ```hs
248 | p_ = Structure . el "p"
249 | ```
250 |
251 | First, we write down the type of the outer-most function. In
252 | our case, this is the operator `.` which has the type:
253 |
254 | ```hs
255 | (.) :: (b -> c) -> (a -> b) -> (a -> c)
256 | ```
257 |
258 | After that, we can try to **match** the type of the arguments we
259 | apply to this function with the type of the arguments from the type signature.
260 |
261 | In this case, we try to apply two arguments to `.`:
262 |
263 | 1. `Structure :: String -> Structure`
264 | 2. `el "p" :: String -> String`
265 |
266 | And luckily, `.` expects two arguments with the types:
267 |
268 | 1. `b -> c`
269 | 2. `a -> b`
270 |
271 | > Note: Applying a function with more arguments than it expects is a type error.
272 |
273 | Since the `.` operator takes at least the number of arguments we supply, we continue
274 | to the next phase of type-checking: matching the types of the inputs with the types
275 | of the expected inputs (from the type signature of the operator).
276 |
277 | When we match two types, we check for *equivalence* between them. There are a few
278 | possible scenarios here:
279 |
280 | 1. When the two types are **concrete** (as opposed to type variables)
281 | and **simple**, like `Int` and `Bool`,
282 | we check if they are the same. If they are, they type check, and we continue.
283 | If they aren't, they don't type check, and we throw an error.
284 | 2. When the two types we match are more **complex** (for example, both are functions),
285 | we try to match their inputs and outputs (in the case of functions). If the inputs and outputs
286 | match, then the two types match.
287 | 3. There is a special case when one of the types is a **type variable** -
288 | in this case, we treat the matching process like an equation and write it down somewhere.
289 | The next time we see this type variable, we *replace it with its match in the equation*.
290 | Think about this like *assigning* a type *variable* with a *value*.
291 |
292 | In our case, we want to match (or check the equivalence of) these types:
293 |
294 | 1. `String -> Structure` with `b -> c`
295 | 2. `String -> String` with `a -> b`
296 |
297 | Let's do this one by one, starting with (1) - matching `String -> Structure` and `b -> c`:
298 |
299 | 1. Because the two types are complex, we check that they are both functions, match their
300 | inputs and outputs: `String` with `b`, and `Structure` with `c`.
301 | 2. Because `b` is a *type variable*, we mark down somewhere that `b` should
302 | be equivalent to `String`.
303 | We write `b ~ String` (we use `~` to denote equivalence).
304 | 3. We match `Structure` and `c`, same as before, we write down that `c ~ Structure`.
305 |
306 | No problem so far; let's try matching `String -> String` with `a -> b`:
307 |
308 | 1. The two types are complex; we see that both are functions, so we match
309 | their inputs and outputs.
310 | 2. Matching `String` with `a` - we write down that `a ~ String`.
311 | 3. Matching `String` with `b` - we remember that we have already written
312 | about `b` - looking back, we see that we already noted that `b ~ String`.
313 | We need to replace `b` with the type that we wrote down before and
314 | check it against this type, so we match `String` with `String`
315 | which, fortunately, type-check because they are the same.
316 |
317 | So far, so good. We've type-checked the expression and discovered the following equivalences
318 | about the type variables in it:
319 |
320 | 1. `a ~ String`
321 | 2. `b ~ String`
322 | 3. `c ~ Structure`
323 |
324 | Now, when asking what is the type of the expression:
325 |
326 | ```hs
327 | p_ = Structure . el "p"
328 | ```
329 |
330 | We say that it is the type of `.` after *replacing* the type variables using the equations, we found
331 | and *removing* the inputs we applied to it, so we started with:
332 |
333 | ```hs
334 | (.) :: (b -> c) -> (a -> b) -> (a -> c)
335 | ```
336 |
337 | Then we replaced the type variables:
338 |
339 | ```hs
340 | (.) :: (String -> Structure) -> (String -> String) -> (String -> Structure)
341 | ```
342 |
343 | And removed the two arguments when we applied the function:
344 |
345 | ```hs
346 | Structure . el "p" :: String -> Structure
347 | ```
348 |
349 | And we got the type of expression!
350 |
351 | Fortunately, Haskell can do this process for us. But when Haskell complains
352 | that our types fail to type-check, and we don't understand exactly why, going through this process
353 | can help us understand where the types do not match, and then we can figure out how to solve it.
354 |
355 |
356 | > **Note**: If we use a *parametrically polymorphic* function more than once,
357 | > or use different functions that have similar type variable names,
358 | > the type variables don't have to match in all instances simply because they share a name.
359 | > Each instance has its own unique set of type variables.
360 | > For example, consider the following snippet:
361 | >
362 | > ```hs
363 | > incrementChar :: Char -> Char
364 | > incrementChar c = chr (ord (id c) + id 1)
365 | > ```
366 | > where the types for the functions we use are:
367 | >
368 | > ```hs
369 | > id :: a -> a
370 | > ord :: Char -> Int
371 | > chr :: Int -> Char
372 | > ```
373 | >
374 | > In the snippet above, we use `id` twice (for no good reason other than for demonstration purposes).
375 | > The first `id` takes a `Char` as argument, and its `a` is equivalent to `Char`.
376 | > The second `id` takes an `Int` as argument, and its *distinct* `a` is equivalent to `Int`.
377 | >
378 | > This, unfortunately, only applies to functions defined at the top-level. If we'd define a local function
379 | > to be passed as an argument to `incrementChar` with the same type signature as `id`,
380 | > the types must match in all uses. So this code:
381 | >
382 | > ```hs
383 | > incrementChar :: (a -> a) -> Char -> Char
384 | > incrementChar func c = chr (ord (func c) + func 1)
385 | > ```
386 | >
387 | > Will not type check. Try it!
388 |
389 | ## Appending Structure
390 |
391 | Before, when we wanted to create richer HTML content and appended
392 | nodes to one another, we used the append (`<>`) operator.
393 | Since we are now not using `String` anymore, we need another way
394 | to do it.
395 |
396 | While it is possible to overload `<>` using a feature in
397 | Haskell called type classes, we will instead create a new function
398 | and call it `append_`, and cover type classes later.
399 |
400 | `append_` should take two `Structure`s, and return a third `Structure`,
401 | appending the inner `String` in the first `Structure` to the second and wrapping the result back in `Structure`.
402 |
403 | ---
404 |
405 | Try implementing `append_`.
406 |
407 |
408 | Solution
409 |
410 | ```hs
411 | append_ :: Structure -> Structure -> Structure
412 | append_ (Structure a) (Structure b) =
413 | Structure (a <> b)
414 | ```
415 |
416 |
417 |
418 | ---
419 |
420 | ## Converting back `Html` to `String`
421 |
422 | After constructing a valid `Html` value, we want to be able to
423 | print it to the output so we can display it in our browser.
424 | For that, we need a function that takes an `Html` and converts it to a `String`, which we can then pass to `putStrLn`.
425 |
426 | ---
427 |
428 | Implement the `render` function.
429 |
430 |
431 | Solution
432 |
433 | ```hs
434 | render :: Html -> String
435 | render html =
436 | case html of
437 | Html str -> str
438 | ```
439 |
440 |
441 |
442 | ---
443 |
444 | ## `type`
445 |
446 | Let's look at one more way to give new names to types.
447 |
448 | A `type` definition looks really similar to a `newtype` definition - the only
449 | difference is that we reference the type name directly without a constructor:
450 |
451 | ```
452 | type =
453 | ```
454 |
455 | For example, in our case, we can write:
456 |
457 | ```hs
458 | type Title = String
459 | ```
460 |
461 | `type`, in contrast with `newtype`, is just a type name alias.
462 | When we declare `Title` as a *type alias* of `String`,
463 | we mean that `Title` and `String` are interchangeable,
464 | and we can use one or the other whenever we want:
465 |
466 | ```hs
467 | "hello" :: Title
468 |
469 | "hello" :: String
470 | ```
471 |
472 | Both are valid in this case.
473 |
474 | We can sometimes use `type`s to give a bit more clarity to our code,
475 | but they are much less useful than `newtype`s which allow us to
476 | *distinguish* two types with the same type representation.
477 |
478 | ## The rest of the owl
479 |
480 | ---
481 |
482 | Try changing the code we wrote in previous chapters to use the new types we created.
483 |
484 | > **Tips**
485 | >
486 | > We can combine `makeHtml` and `html_`, and remove `body_` `head_` and `title_`
487 | > by calling `el` directly in `html_`, which can now have the type
488 | > `Title -> Structure -> Html`.
489 | > This will make our HTML EDSL less flexible but more compact.
490 | >
491 | > Alternatively, we could create `newtype`s for `HtmlHead` and `HtmlBody` and
492 | > pass those to `html_`, and we might do that in later chapters, but I've chosen
493 | > to keep the API a bit simple for now, we can always refactor later!
494 |
495 |
496 | Solution
497 |
498 | ```hs
499 | -- hello.hs
500 |
501 | main :: IO ()
502 | main = putStrLn (render myhtml)
503 |
504 | myhtml :: Html
505 | myhtml =
506 | html_
507 | "My title"
508 | ( append_
509 | (h1_ "Heading")
510 | ( append_
511 | (p_ "Paragraph #1")
512 | (p_ "Paragraph #2")
513 | )
514 | )
515 |
516 | newtype Html
517 | = Html String
518 |
519 | newtype Structure
520 | = Structure String
521 |
522 | type Title
523 | = String
524 |
525 | html_ :: Title -> Structure -> Html
526 | html_ title content =
527 | Html
528 | ( el "html"
529 | ( el "head" (el "title" title)
530 | <> el "body" (getStructureString content)
531 | )
532 | )
533 |
534 | p_ :: String -> Structure
535 | p_ = Structure . el "p"
536 |
537 | h1_ :: String -> Structure
538 | h1_ = Structure . el "h1"
539 |
540 | el :: String -> String -> String
541 | el tag content =
542 | "<" <> tag <> ">" <> content <> "" <> tag <> ">"
543 |
544 | append_ :: Structure -> Structure -> Structure
545 | append_ c1 c2 =
546 | Structure (getStructureString c1 <> getStructureString c2)
547 |
548 | getStructureString :: Structure -> String
549 | getStructureString content =
550 | case content of
551 | Structure str -> str
552 |
553 | render :: Html -> String
554 | render html =
555 | case html of
556 | Html str -> str
557 | ```
558 |
559 |
560 |
561 | ---
562 |
563 | ## Are we safe yet?
564 |
565 | We have made some progress - now we can't write `"Hello"`
566 | where we'd expect either a paragraph or a heading, but we can still
567 | write `Structure "hello"` and get something that isn't a
568 | paragraph or a heading. So while we made it harder for the user
569 | to make mistakes by accident, we haven't really been able to **enforce
570 | the invariants** we wanted to enforce in our library.
571 |
572 | Next, we'll see how we can make expressions such as `Structure "hello"` illegal
573 | as well using *modules* and *smart constructors*.
574 |
--------------------------------------------------------------------------------