File: part11.lhs

package info (click to toggle)
haskell98-tutorial 200006-2-1.1
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd, squeeze, wheezy
  • size: 864 kB
  • ctags: 59
  • sloc: haskell: 2,125; makefile: 66; sh: 9
file content (122 lines) | stat: -rw-r--r-- 3,316 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
Gentle Introduction to Haskell 98, Online Supplement 
Part 11
Covers Sections 4.3, 4.4, 4.5, 4.6

> module Part11() where

> import Prelude hiding (take)

Section: 4.3 Case Expressions

The function `take' using a case statement instead of multiple equations:

> take :: Int -> [a] -> [a]
> take m ys = case (m,ys) of
>              (0  ,_)    -> []
>              (_  ,[])   -> []
>              (n+1,x:xs) -> x : take n xs

The function take using if then else.  We can also eliminate the n+k
pattern just for fun.  The original version of take is much easier to read!

> take' :: Int -> [a] -> [a]
> take' m ys = if m == 0 then [] else
>               if null ys then [] else
>                if m > 0 then head ys : take (m-1) (tail ys)
>                 else error "m < 0"

Section: 4.4  Lazy Patterns

Before the client-server example, here is a contrived example of lazy
patterns.  The first version will fail to pattern match whenever the
the first argument is [].  The second version will always pattern
match initially but x will fail if used when the list is [].

> nonlazy :: [Int] -> Bool -> [Int]
> nonlazy (x:xs) isNull  = if isNull then [] else [x]

> e1 = nonlazy [1,2] False
> e2 = nonlazy [] True
> e3 = nonlazy [] False

This version will never fail the initial pattern match

> lazy :: [Int] -> Bool -> [Int]
> lazy ~(x:xs) isNull  = if isNull then [] else [x]

> e4 = lazy [1,2] False
> e5 = lazy [] True
> e6 = lazy [] False

The server - client example is a little hard to demonstrate.  We'll avoid
the initial version which loops.  Here is the version with irrefutable
patterns.

> type Response = Int
> type Request = Int

> client :: Request -> [Response] -> [Request]
> client init ~(resp:resps) = init : client (next resp) resps

> server :: [Request] -> [Response]
> server (req : reqs) = process req : server reqs

Next maps the response from the previous request onto the next request

> next :: Response -> Request 
> next resp = resp

Process maps a request to a response

> process :: Request -> Response
> process req = req+1

> requests :: [Request]
> requests = client 0 responses

> responses :: [Response]
> responses = server requests

> e7 = take 5 responses

The lists of requests and responses are infinite - there is no need to
check for [] in this program.  These lists correspond to streams in other
languages.

Here is fib again:

> fib :: [Int]
> fib@(_:tfib) = 1 : 1 : [ a+b | (a,b) <- zip fib tfib]

> e8 = take 10 fib

Section: 4.5  Lexical Scoping and Nested Forms

One thing that is important to note is that the order of the
definitions in a program, let expression, or where clauses is
completely arbitrary.  Definitions can be arranged 'top down'
or `bottom up' without changing the program.

> e9 = let y = 2 :: Float
>          f x = (x+y)/y
>      in f 1 + f 2

> f :: Int -> Int -> String
> f x y | y > z  = "y > x^2"
>       | y == z = "y = x^2"
>       | y < z  = "y < x^2"
>   where
>     z = x*x
 
> e10 = f 2 5
> e11 = f 2 4

Section: 4.6  Layout

There's nothing much to demonstrate here.  We have been using layout all
through the tutorial.  The main thing is to be careful line up the
first character of each definition.  For example, if you
change the indentation of the definition of f in e9 you will get a
parse error.

Continued in part12.lhs