diff --git a/part1.html b/part1.html index 59f525bd..09512391 100644 --- a/part1.html +++ b/part1.html @@ -291,7 +291,7 @@
GHCi is the interactive Haskell interpreter. Here’s an example session:
$ stack ghci
-GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help
+GHCi, version 9.2.8: http://www.haskell.org/ghc/ :? for help
Prelude> 1+1
2
Prelude> "asdf"
@@ -889,7 +889,7 @@ where len = collatz n
We can load the program in GHCi and play with it.
$ stack ghci
-GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help
+GHCi, version 9.2.8: http://www.haskell.org/ghc/ :? for help
Prelude> :load Collatz.hs
[1 of 1] Compiling Collatz ( Collatz.hs, interpreted )
Ok, one module loaded.
@@ -1120,7 +1120,7 @@ ==> 2*1 : (2*2 : (2*3 : []))
=== [2*1, 2*2, 2*3]
==> [2,4,6]
-Once you know pattern matching for lists, it’s straightforward to define map
and filter
. Actually, let’s just look at the GHC standard library implementations. Here’s map:
Once you know pattern matching for lists, it’s straightforward to define map
and filter
. Actually, let’s just look at the GHC standard library implementations. Here’s map:
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
and here’s filter:
+and here’s filter:
filter :: (a -> Bool) -> [a] -> [a]
filter _pred [] = []
filter pred (x:xs)
@@ -2763,7 +2763,7 @@ 4.6.1<
Data.Map
defines all sorts of useful higher-order functions for updating maps. We can rewrite the withdraw
function using Data.Map.adjust
:
withdraw :: String -> Int -> Map.Map String Int -> Map.Map String Int
= Map.adjust (\x -> x-amount) account bank withdraw account amount bank
-Note! There are separate Data.Map.Strict
and Data.Map.Lazy
implementations. When you import Data.Map
you get Data.Map.Lazy
. You can find the documentation for all the Data.Map
functions in the docs for Data.Map.Lazy
. We won’t go into their differences here, but mostly you should use Data.Map.Strict
in real code.
+Note! There are separate Data.Map.Strict
and Data.Map.Lazy
implementations. When you import Data.Map
you get Data.Map.Lazy
. You can find the documentation for all the Data.Map
functions in the docs for Data.Map.Lazy
. We won’t go into their differences here, but mostly you should use Data.Map.Strict
in real code.
4.6.2 Data.Array
Another type that works kind of like a list but is more efficient for some operations is the array. Arrays are familiar from many other programming languages, but Haskell arrays are a bit different.
Unlike the Data.Map
module, the Data.Array
can just be imported normally:
@@ -2803,7 +2803,7 @@ ==> 10
Haskell libraries tend to have pretty good docs. We’ve linked to docs via Hackage (https://hackage.haskell.org) previously, but it’s important to know how to find the docs by yourself too. The tool for generating Haskell documentation is called Haddock so sometimes Haskell docs are referred to as haddocks.
-Hackage is the Haskell package repository (just like PyPI for Python, Maven Central for Java or NPM for Javascript). In addition to the actual packages, it hosts documentation for them. Most of the modules that we use on this course are in the package called base
. You can browse the docs for the base package at https://hackage.haskell.org/package/base-4.14.1.0/.
Hackage is the Haskell package repository (just like PyPI for Python, Maven Central for Java or NPM for Javascript). In addition to the actual packages, it hosts documentation for them. Most of the modules that we use on this course are in the package called base
. You can browse the docs for the base package at https://hackage.haskell.org/package/base-4.16.4.0/.
When you’re not quite sure where the function you’re looking for is, Hoogle (https://hoogle.haskell.org/) can help. Hoogle is a search engine for Haskell documentation. It is a great resource when you need to check what was the type of foldr
or which packages contain a function named reverse
.
Finally, since this course is using the stack
tool, you can also browse the documentation for the libraries stack has installed for you with the commands
stack haddock --open
@@ -3436,7 +3436,7 @@ (/=) :: a -> a -> Bool
x /= y = not (x == y)
Note how both operations have a default implementation in terms of the other. This means we could define an Eq
instance with no content at all, but the resulting functions would just recurse forever. In practice, we want to define at least one of ==
and /=
.
When there are lots of default implementations, it can be hard to know which functions you need to implement yourself. For this reason class documentation usually mentions the minimal complete definition. For Eq
, the docs say “Minimal complete definition: either == or /=.”
When there are lots of default implementations, it can be hard to know which functions you need to implement yourself. For this reason class documentation usually mentions the minimal complete definition. For Eq
, the docs say “Minimal complete definition: either == or /=.”
Let’s look at Ord
next. Ord
has 7 operations, all with default implementations in terms of each other. By the way, note the quirky way of defining multiple type signatures at once. It’s okay, it’s a feature of Haskell, this is how Ord
is defined in the standard. (We’ll get back to what the (Eq a) =>
part means soon.)
class (Eq a) => Ord a where
compare :: a -> a -> Ordering
@@ -3456,7 +3456,7 @@ | otherwise = x
min x y | x <= y = x
| otherwise = y
With this definition it’s really hard to know what the minimal complete definition is. Luckily the docs tell us “Minimal complete definition: either compare or <=.”
+With this definition it’s really hard to know what the minimal complete definition is. Luckily the docs tell us “Minimal complete definition: either compare or <=.”
As a final word on default implementations, if there is never a need to override the default definition, the function can be moved out of the class for simplicity. Consider a class like Combine
below:
class Combine a where
combine :: a -> a -> a
@@ -3747,7 +3747,7 @@ Age Descending persons
sortPersons ==> [Person {name = "Fridolf", age = 73},Person {name = "Hans", age = 65},Person {name = "Greta", age = 60}]
Note how you can’t accidentally typo the field name (unlike with strings), and how you don’t need to remember whether true
refers to ascending or descending order.
Let’s move on to the next example. Many Haskell functions don’t work with empty lists (consider head []
). If you’re writing code that needs to track whether lists are possibly empty or guaranteed to not be empty, you can use the NonEmpty
type from the Data.List.NonEmpty module.
Let’s move on to the next example. Many Haskell functions don’t work with empty lists (consider head []
). If you’re writing code that needs to track whether lists are possibly empty or guaranteed to not be empty, you can use the NonEmpty
type from the Data.List.NonEmpty module.
Consider the definition of NonEmpty
:
data NonEmpty a = a :| [a]
Here the type represents a lack of cases. The type NonEmpty a
will always consist of a value of type a
, and some further a
s, collected in a list. Here are some example values of NonEmpty Int
: