diff --git a/part1.html b/part1.html index 89831f4..5676bc4 100644 --- a/part1.html +++ b/part1.html @@ -533,8 +533,8 @@ Leaving GHCi.
polynomial :: Double -> Double
 polynomial x = x^2 - x - 1

you should usually play around with it. Start by running it. There are a couple of ways to do this.

-

If a definition fits on one line, you can just define it in GHCi using let:

-
Prelude> let polynomial x = x^2 - x - 1
+

If a definition fits on one line, you can just define it in GHCi:

+
Prelude> polynomial x = x^2 - x - 1
 Prelude> polynomial 3.0
 5.0

For a multi-line definition, you can either use ; to separate lines, or use the special :{ :} syntax to paste a block of code into GHCi:

@@ -1298,7 +1298,7 @@ Ok, two modules loaded.
reverse [1..4] ==> [4,3,2,1]

2.4 A Word About Immutability

Because Haskell is pure, it also means that functions can’t mutate (change) their inputs. Mutation is a side effect, and Haskell functions are only allowed output via their return value. This means that Haskell list functions always return a new list. In practice:

-
Prelude> let list = [1,2,3,4]
+
Prelude> list = [1,2,3,4]
 Prelude> reverse list
 [4,3,2,1]
 Prelude> list
@@ -1717,10 +1717,10 @@ Because Nothing is a constructor
 

3.2 Partial Application

When using higher-order functions you can find yourself defining lots of small helper functions, like addThree or shorten in the previous examples. This is a bit of a chore in the long run, but luckily Haskell’s functions behave a bit weirdly…

Let’s start in GHCi:

-
Prelude> let add a b = a+b
+
Prelude> add a b = a+b
 Prelude> add 1 5
 6
-Prelude> let addThree = add 3
+Prelude> addThree = add 3
 Prelude> addThree 2
 5

So, we’ve defined add, a function of two arguments, and only given it one argument. The result is not a type error but a new function. The new function just stores (or remembers) the given argument, waits for another argument, and then gives both to add.

@@ -2451,7 +2451,7 @@ unspecified
f :: (Eq a) => (a -> a) -> a -> Bool
 f g x = x == g x

If you don’t have a type annotation, type inference can provide the constraints!

-
Prelude> let f g x = x == g x
+
Prelude> f g x = x == g x
 Prelude> :type f
 f :: (Eq a) => (a -> a) -> a -> Bool

You can also have multiple constraints:

@@ -2605,7 +2605,7 @@ unspecified

The Ord constraint for the key type of the map is needed because maps are implemented as ordered binary search trees.

Note that like all Haskell values, maps are immutable meaning you can’t change a map once you define it. However, map operations like insert produce a new map. To perform multiple map operations you need to reuse the return value. Here’s a GHCi session operating on a map.

Prelude> import qualified Data.Map as Map
-Prelude Map> let values = Map.fromList [("z",3),("w",4)]
+Prelude Map> values = Map.fromList [("z",3),("w",4)]
 Prelude Map> Map.lookup "z" values
 Just 3
 Prelude Map> Map.lookup "banana" values
@@ -2624,7 +2624,7 @@ unspecified
     Nothing  -> bank                                   -- account not found, no change
     Just sum -> Map.insert account (sum-amount) bank   -- set new balance

Here’s how you might use the withdraw function in GHCi. Note how the maps get printed as fromList invocations. Also note how calling withdraw ... bank returns a new bank and doesn’t change the existing bank.

-
GHCi> let bank = Map.fromList [("Bob",100),("Mike",50)]
+
GHCi> bank = Map.fromList [("Bob",100),("Mike",50)]
 GHCi> withdraw "Bob" 80 bank
 fromList [("Bob",20),("Mike",50)]
 GHCi> bank                         -- note immutability
@@ -2657,7 +2657,7 @@ unspecified
 (//) :: Ix i => Array i e -> [(i, e)] -> Array i e

Here’s an example GHCi session:

Prelude> import Data.Array
-Prelude Data.Array> let myArray = listArray (7,11) ["seven", "eight", "nine", "ten", "ELEVEN"]
+Prelude Data.Array> myArray = listArray (7,11) ["seven", "eight", "nine", "ten", "ELEVEN"]
 Prelude Data.Array> myArray
 array (7,11) [(7,"seven"),(8,"eight"),(9,"nine"),(10,"ten"),(11,"ELEVEN")]
 Prelude Data.Array> myArray ! 8
@@ -3234,7 +3234,7 @@ Because Nothing is a constructor.
 size :: Size a => a -> Int
 Prelude> size [True,False]
 2
-Prelude> let sizeBoth a b = [size a, size b]
+Prelude> sizeBoth a b = [size a, size b]
 Prelude> :t sizeBoth
 sizeBoth :: (Size a1, Size a2) => a1 -> a2 -> [Int]

A class can contain multiple functions, and even constants. Here we define a new version of the Size class with more content.

diff --git a/part2.html b/part2.html index f5b5b34..4659807 100644 --- a/part2.html +++ b/part2.html @@ -1548,7 +1548,7 @@ pit----->|Room "You fall..." []| treasure--->|Room "A gr -- and so on

A ha! The problem is that our recursion base case of sumEverySecond 0 is not enough to stop the recursion.

Finally, a word of caution. Using trace, and especially traceShowId, can cause things that would not otherwise get evaluated to get evaluated. For example:

-
Prelude Debug.Trace> let traceHead xs = head (traceShowId xs)
+
Prelude Debug.Trace> traceHead xs = head (traceShowId xs)
 Prelude Debug.Trace> traceHead [0..]
 -- never terminates since it's trying to show an infinite list

So feel free to use Debug.Trace when working on the exercises, but try to leave trace calls out of your final answers. Some exercise sets check your imports and disallow Debug.Trace.

@@ -2089,10 +2089,10 @@ Prelude> final

11.8 What Does It All Mean?

Let’s return to the functional world. How can we reconcile IO operations with Haskell being a pure and lazy language? Something like putStrLn :: String -> IO () is a pure function that returns an operation. How is it pure? putStrLn x is the same when x is the same. In other words: an operation is a pure description of a chain of side effects. Only executing the operation causes those side effects. When a Haskell program is run, only one operation is executed - it’s called main :: IO (). Other operations can be run only by linking them up to main.

When in GHCi, if an expression you type in evaluates to an operation, GHCi runs that operation for you. Here’s a demonstration of the purity of print:

-
Prelude> let x = print 1   -- creates operation, doesn't run it
-Prelude> x                 -- runs the operation
+
Prelude> x = print 1   -- creates operation, doesn't run it
+Prelude> x             -- runs the operation
 1
-Prelude> x                 -- runs it again!
+Prelude> x             -- runs it again!
 1

Operations are values just like numbers, lists and functions. We can write code that operates on operations. This function takes two operations, a and b, and returns an operation that asks the user which one he’d like to run.

choice :: IO x -> IO x -> IO x
@@ -2910,7 +2910,7 @@ this topic connects to equational reasoning discussed in chapter 10.
 withdrawOp :: String -> Int -> BankOp Int
 withdrawOp accountName amount = BankOp (withdraw accountName amount)

Let’s see how chaining works with these bank operations.

-
Prelude> let bank = Bank (Map.fromList [("edsger",10),("grace",50)])
+
Prelude> bank = Bank (Map.fromList [("edsger",10),("grace",50)])
 
 -- Running a number of operations using +>>
 
@@ -3580,7 +3580,7 @@ None of the above
 
Prelude> import qualified Data.Text as T
 Prelude T> :t T.pack
 T.pack :: String -> T.Text
-Prelude T> let phrase = T.pack "brevity is the soul of wit"
+Prelude T> phrase = T.pack "brevity is the soul of wit"
 Prelude T> :t phrase
 phrase :: T.Text
 Prelude T> phrase
@@ -3626,7 +3626,7 @@ None of the above
 Prelude T TL> TL.head (TL.pack (repeat 'x'))
 'x'

Another practical problem that sometimes crops is that you can end up with a mismatch between strict and lazy Texts when using libraries. You can usually fix this by using toStrict or fromStrict as needed.

-
Prelude T TL> let lazyPhrase = TL.pack "brevity is the soul of wit"
+
Prelude T TL> lazyPhrase = TL.pack "brevity is the soul of wit"
 Prelude T TL> :t lazyPhrase
 lazyPhrase :: TL.Text
 Prelude T TL> :t phrase
@@ -3655,7 +3655,7 @@ None of the above
 

We can walk through pretty much the same GHCi session using ByteString instead of Text. However, note how the ByteString is built up from Word8 values and not Char values. A Char can represent an arbitrary unicode codepoint like for a character like 'Å', but a Word8 represents a byte: a number from 0 to 255. Unfortunately and somewhat confusingly, ByteString values get printed like Strings.

Prelude> import Data.Word
 Prelude Data.Word> import qualified Data.ByteString as B
-Prelude Data.Word B> let binary = B.pack [99,111,102,102,101,101]
+Prelude Data.Word B> binary = B.pack [99,111,102,102,101,101]
 Prelude Data.Word B> :t binary
 binary :: B.ByteString
 Prelude Data.Word B> :t B.pack
@@ -3825,7 +3825,7 @@ None of the above
     -- Defined in ‘Database.SQLite.Simple.Types’
     -- ... rest of output omitted
 Prelude Database.SQLite.Simple> import qualified Data.Text as T
-Prelude Database.SQLite.Simple T> let q = Query (T.pack "SELECT 1;")
+Prelude Database.SQLite.Simple T> q = Query (T.pack "SELECT 1;")
 Prelude Database.SQLite.Simple T> res <- query_ db q :: IO [[Int]]
 Prelude Database.SQLite.Simple T> res
 [[1]]
@@ -3847,7 +3847,7 @@ None of the above instance (FromField a, FromField b) => FromRow (a,b) instance (FromField a, FromField b, FromField c) => FromRow (a,b,c)

In essence, basic Haskell datatypes fulfill the FromField class, and various Haskell collections fulfill the FromRow class. Our earlier example was using the FromRow [a] and FromField Int instances to get a [[Int]] out of query_. Here’s a simple query that uses some other datatypes:

-
Prelude Database.SQLite.Simple T> let q = Query (T.pack "SELECT 1, true, 'string';")
+
Prelude Database.SQLite.Simple T> q = Query (T.pack "SELECT 1, true, 'string';")
 Prelude Database.SQLite.Simple T> query_ db q :: IO [(Int,Bool,String)]
 [(1,True,"string")]

What happens if the SQL and Haskell types don’t match? Well, you get a runtime error, just like if you try to invoke read "True" :: Int.

@@ -3867,8 +3867,8 @@ None of the above instance (ToField a, ToField b, ToField c) => ToRow (a, b, c)

Parameterized queries use a ? character to denote slots where parameters can be passed in. Here’s a simple example:

-
Prelude Database.SQLite.Simple T> let input = (1,"hello") :: (Int,String)
-Prelude Database.SQLite.Simple T> let parameterized = Query (T.pack "SELECT ?+1, true, ?;")
+
Prelude Database.SQLite.Simple T> input = (1,"hello") :: (Int,String)
+Prelude Database.SQLite.Simple T> parameterized = Query (T.pack "SELECT ?+1, true, ?;")
 Prelude Database.SQLite.Simple T> query db parameterized input :: IO [(Int,Bool,String)]
 [(2,True,"hello")]

That’s pretty much all you need to know about sqlite-simple: open, query_, query, FromRow, ToRow. Oh right, one more thing. There are the functions execute and execute_ if you don’t need the result of the query. They’re useful for inserting things into the database, for example.