Skip to content

Commit 7e3f55d

Browse files
authored
Added definition, exercise, and solution for Nat to Section 6.1 (#89)
* added definition, exercise, and solution for Nat to Section 6.1 * renamed type and data constructors for Nat introduction * cleaned up parenthetical remark
1 parent 5252173 commit 7e3f55d

2 files changed

Lines changed: 50 additions & 19 deletions

File tree

src/Solutions/Dependent.idr

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,34 @@ data Vect : (len : Nat) -> Type -> Type where
1111
(::) : a -> Vect n a -> Vect (S n) a
1212

1313
-- 1
14+
len : List a -> Nat
15+
len Nil = Z
16+
len (_ :: xs) = S (len xs)
17+
18+
-- 2
1419
head : Vect (S n) a -> a
1520
head (x :: _) = x
1621
head Nil impossible
1722

18-
-- 2
23+
-- 3
1924
tail : Vect (S n) a -> Vect n a
2025
tail (_ :: xs) = xs
2126
tail Nil impossible
2227

23-
-- 3
28+
-- 4
2429
zipWith3 : (a -> b -> c -> d) -> Vect n a -> Vect n b -> Vect n c -> Vect n d
2530
zipWith3 f [] [] [] = []
2631
zipWith3 f (x :: xs) (y :: ys) (z :: zs) = f x y z :: zipWith3 f xs ys zs
2732

28-
-- 4
33+
-- 5
2934
-- Since we only have a `Semigroup` constraint, we can't conjure
3035
-- a value of type `a` out of nothing in case of an empty list.
3136
-- We therefore have to return a `Nothing` in case of an empty list.
3237
foldSemi : Semigroup a => List a -> Maybe a
3338
foldSemi [] = Nothing
3439
foldSemi (x :: xs) = Just . maybe x (x <+>) $ foldSemi xs
3540

36-
-- 5
41+
-- 6
3742
-- the `Nil` case is impossible here, so unlike in Exercise 4,
3843
-- we don't need to wrap the result in a `Maybe`.
3944
-- However, we need to pattern match on the tail of the Vect to
@@ -42,24 +47,24 @@ foldSemiVect : Semigroup a => Vect (S n) a -> a
4247
foldSemiVect (x :: []) = x
4348
foldSemiVect (x :: t@(_ :: _)) = x <+> foldSemiVect t
4449

45-
-- 6
50+
-- 7
4651
iterate : (n : Nat) -> (a -> a) -> a -> Vect n a
4752
iterate 0 _ _ = Nil
4853
iterate (S k) f v = v :: iterate k f (f v)
4954

50-
-- 7
55+
-- 8
5156
generate : (n : Nat) -> (s -> (s,a)) -> s -> Vect n a
5257
generate 0 _ _ = Nil
5358
generate (S k) f v =
5459
let (v', va) = f v
5560
in va :: generate k f v'
5661

57-
-- 8
62+
-- 9
5863
fromList : (as : List a) -> Vect (length as) a
5964
fromList [] = []
6065
fromList (x :: xs) = x :: fromList xs
6166

62-
-- 9
67+
-- 10
6368
-- Lookup the type and implementation of functions `maybe` `const` and
6469
-- try figuring out, what's going on here. An alternative implementation
6570
-- would of course just pattern match on the argument.

src/Tutorial/Dependent.md

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,31 @@ How could we even describe such a precondition?
3636
## Length-Indexed Lists
3737

3838
The answer to the issues described above is of course: Dependent types.
39-
And the most common introductory example is the *vector*: A list indexed
40-
by its length:
39+
Before we proceed to our example, first consider how Idris recursively
40+
defines the natural numbers (here affixed with apostrophes to avoid introducing
41+
a conflict with the actual definition of `Nat`, which you can find
42+
[here](https://github.com/idris-lang/Idris-dev/blob/master/libs/prelude/Prelude/Nat.idr)
43+
for reference)):
44+
45+
```idris
46+
data Nat' : Type where
47+
Z' : Nat'
48+
S' : Nat' -> Nat'
49+
```
50+
51+
In this scheme, 0 is represented by `Z`, 1 is represented by `S Z`, 2 is
52+
represented by `S (S Z)`, and so on. Idris does this automatically so if you
53+
enter `Z` or `S Z` into the REPL, it will return `0` or `1`. Note that the
54+
only function inherently available to act on a value of type `Nat` is our data
55+
constructor `S`, which represents the successor function, i.e. adding 1.
56+
57+
Also note that in Idris, every `Nat` can be represented as either a `Z` or
58+
an `S n` where `n` is another `Nat`. Much as every `List a` can be represented
59+
as either a `Nil` or an `x :: xs` (where `x` is an `a` and `xs` is a `List a`),
60+
this informs our pattern matching when solving problems.
61+
62+
Now we can consider the textbook introductory example of dependent types,
63+
the *vector*, which is a list indexed by its length:
4164

4265
```idris
4366
data Vect : (len : Nat) -> (a : Type) -> Type where
@@ -443,7 +466,10 @@ this, which constructor(s) of the type family to use.
443466

444467
### Exercises part 1
445468

446-
1. Implement function `head` for non-empty vectors:
469+
1. Implement a function `len : List a -> Nat` for calculating the
470+
length of a `List`. For example, `len [1, 1, 1]` produces `3`.
471+
472+
2. Implement function `head` for non-empty vectors:
447473

448474
```idris
449475
head : Vect (S n) a -> a
@@ -455,27 +481,27 @@ this, which constructor(s) of the type family to use.
455481
a `Maybe`! Make sure to add an `impossible` clause for the `Nil`
456482
case (although this is not strictly necessary here).
457483

458-
2. Using `head` as a reference, declare and implement function `tail`
484+
3. Using `head` as a reference, declare and implement function `tail`
459485
for non-empty vectors. The types should reflect that the output
460486
is exactly one element shorter than the input.
461487

462-
3. Implement `zipWith3`. If possible, try to doing so without looking at
488+
4. Implement `zipWith3`. If possible, try to doing so without looking at
463489
the implementation of `zipWith`:
464490

465491
```idris
466492
zipWith3 : (a -> b -> c -> d) -> Vect n a -> Vect n b -> Vect n c -> Vect n d
467493
```
468494

469-
4. Declare and implement a function `foldSemi`
495+
5. Declare and implement a function `foldSemi`
470496
for accumulating the values stored
471497
in a `List` through `Semigroup`s append operator (`(<+>)`).
472498
(Make sure to only use a `Semigroup` constraint, as opposed to
473499
a `Monoid` constraint.)
474500

475-
5. Do the same as in Exercise 4, but for non-empty vectors. How
501+
6. Do the same as in Exercise 4, but for non-empty vectors. How
476502
does a vector's non-emptiness affect the output type?
477503

478-
6. Given an initial value of type `a` and a function `a -> a`,
504+
7. Given an initial value of type `a` and a function `a -> a`,
479505
we'd like to generate `Vect`s of `a`s, the first value of
480506
which is `a`, the second value being `f a`, the third
481507
being `f (f a)` and so on.
@@ -487,7 +513,7 @@ this, which constructor(s) of the type family to use.
487513
encapsulate this behavior. Get some inspiration from `replicate`
488514
if you don't know where to start.
489515

490-
7. Given an initial value of a state type `s` and
516+
8. Given an initial value of a state type `s` and
491517
a function `fun : s -> (s,a)`,
492518
we'd like to generate `Vect`s of `a`s. Declare and implement
493519
function `generate`, which should encapsulate this behavior. Make sure to use
@@ -501,7 +527,7 @@ this, which constructor(s) of the type family to use.
501527
[1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
502528
```
503529

504-
8. Implement function `fromList`, which converts a list of
530+
9. Implement function `fromList`, which converts a list of
505531
values to a `Vect` of the same length. Use holes if you
506532
get stuck:
507533

@@ -513,7 +539,7 @@ this, which constructor(s) of the type family to use.
513539
length of the resulting vector by passing the list argument
514540
to function *length*.
515541

516-
9. Consider the following declarations:
542+
10. Consider the following declarations:
517543

518544
```idris
519545
maybeSize : Maybe a -> Nat

0 commit comments

Comments
 (0)