From b1cc107960cbcadaf9c6d2d31bb0403e37bb3e8b Mon Sep 17 00:00:00 2001 From: Joel Kaasinen Date: Mon, 11 Dec 2023 17:45:27 +0200 Subject: [PATCH] part 1: update for GHC 9.2.8 fixes #94 --- part1.html | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/part1.html b/part1.html index 59f525bd..09512391 100644 --- a/part1.html +++ b/part1.html @@ -291,7 +291,7 @@

1

1.5 Let’s Start!

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
 withdraw account amount bank = Map.adjust (\x -> x-amount) account 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

4.7 Reading Docs

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 @@ 

sortPersons Age Descending persons ==> [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 as, collected in a list. Here are some example values of NonEmpty Int: