Skip to content

Commit 368e4ab

Browse files
committed
Part 1 Chapter 7 Exercise 6, part 4.
1 parent 94ce28d commit 368e4ab

File tree

1 file changed

+51
-10
lines changed

1 file changed

+51
-10
lines changed

part1_chapter7_exercise6/Main.hs

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@ main = do
1919
putStrLn . show $ chop8' [1, 2, 3, 4, 5, 6, 7, 8, 9]
2020

2121
putStrLn "map (+1) [1, 2, 3]"
22-
putStrLn . show $ map (+1) [1, 2, 3]
22+
putStrLn . show $ map show [1, 2, 3]
2323

2424
putStrLn "map' (+1) [1, 2, 3]"
2525
putStrLn . show $ map' (+1) [1, 2, 3]
2626

27+
putStrLn "take 5 $ iterate (+1) 2"
28+
putStrLn . show . take 5 $ iterate (+1) 2
29+
30+
putStrLn "take 5 $ iterate' (+1) 2"
31+
putStrLn . show . take 5 $ iterate' (+1) 2
32+
2733
unfold :: (a -> Bool) -> (a -> a) -> (a -> a) -> a -> [a]
2834
unfold predicate headFunction tailFunction x
2935
| predicate x = []
@@ -50,17 +56,24 @@ chop8' = unfold null (take 8) (drop 8)
5056
-- Now with that in mind, does it really makes sense to apply "unfold" here? Is the function not supposed to build a list
5157
-- using the starting value and the functions provided? If not, I'm not sure what really is required. If so, though,
5258
-- the type signature defined by the regular "map" is going to make:
53-
-- the implementation unnecessarily complicated
54-
-- map and map' are not going to be interchangable.
59+
-- * the implementation unnecessarily complicated
60+
-- * map and map' are not going to be interchangable.
61+
-- Additionally, "unfold" requires the function f to be of type (a -> a) while "map" requires the function f to be of type (a -> a)
62+
-- so we know already that we won't be able to create an exact, interchangeable replica of the original "map".
63+
-- E.g. we would not be able to use the "map'" like this:
64+
--
65+
-- map' show [1, 2, 3]
66+
--
67+
-- which would be perfectly fine with the original "map".
5568
--
5669
-- My initial thought was to implement function:
5770
--
58-
-- map' :: (a -> b) -> [a] -> [b]
71+
-- map' :: (a -> a) -> [a] -> [a]
5972
--
60-
-- with the signature identical to the regular "map" (just a different implementation) so that both can be used interchangeably.
73+
-- with the signature similar to the regular "map" (just a different implementation) so that both can be used interchangeably.
6174
-- It took me a while before I realized that it is probably more tricky than it should be.
6275
-- This is when I started looking for other solutions, just to get an idea how people interpret the task.
63-
-- What I found mademe even more confused. Examples:
76+
-- What I found made me even more confused. Examples:
6477
--
6578
-- 1. https://github.com/evturn's implementaion: https://github.com/evturn/programming-in-haskell/blob/master/07-higher-order-functions/07.9-exercises.hs
6679
--
@@ -70,20 +83,48 @@ chop8' = unfold null (take 8) (drop 8)
7083
--
7184
-- map' :: (a -> Bool) -> (a -> a) -> a -> [a]
7285
--
73-
-- So as you can see, it doesn't match the original "map"'s type and the input argument (second last "a") gets "unfolded" into [a].
86+
-- It doesn't match the original "map"'s type very precisely and the input argument (second last "a") gets "unfolded" into [a].
7487
--
7588
-- 2. Another example I found is the one by https://github.com/RoccoMathijn: https://github.com/RoccoMathijn/programming-in-haskell/blob/master/chapter07.hs
7689
--
7790
-- map' :: (a -> b) -> [a] -> [b]
7891
-- map' f = unfold (null) (f.head) (tail)
7992
--
8093
-- But this again is not intercangeable with the original "map" as b is simply [a], so the result is really [[a]].
94+
-- Furthermore, I'm not sure how it even builds as "unfold" operates on (a -> a) and not (a -> b) as the function f. I'm using GHC 8.6.3 and getting the following:
95+
--
96+
-- $ ghci
97+
-- GHCi, version 8.6.3: http://www.haskell.org/ghc/ :? for help
98+
-- Prelude> :load Main
99+
-- [1 of 1] Compiling Main ( Main.hs, interpreted )
100+
101+
-- Main.hs:95:26: error:
102+
-- * Couldn't match type `b' with `[a]'
103+
-- `b' is a rigid type variable bound by
104+
-- the type signature for:
105+
-- map'' :: forall a b. (a -> b) -> [a] -> [b]
106+
-- at Main.hs:94:1-31
107+
-- Expected type: [a] -> [a]
108+
-- Actual type: [a] -> b
109+
-- * In the second argument of `unfold', namely `(f . head)'
110+
-- In the expression: unfold (null) (f . head) (tail)
111+
-- In an equation for map'': map'' f = unfold (null) (f . head) (tail)
112+
-- * Relevant bindings include
113+
-- f :: a -> b (bound at Main.hs:95:7)
114+
-- map'' :: (a -> b) -> [a] -> [b] (bound at Main.hs:95:1)
115+
-- |
116+
-- 95 | map'' f = unfold (null) (f.head) (tail)
117+
-- | ^^^^^^
118+
-- Failed, no modules loaded.
81119
--
82-
-- After more time than I'd like to admit, I finally figured out the implementation which is interchangeable with the original "map".
120+
-- After more time than I'd like to admit, I finally figured out the implementation which is almost interchangeable with the original "map".
83121
-- Terribly inefficient, but I believe it answers the exercise's question precisely.
84122

85-
map' :: (a -> b) -> [a] -> [b]
123+
map' :: (a -> a) -> [a] -> [a]
86124
map' f = concat . unfold null (listify . f . head) (tail)
87125

88126
listify :: a -> [a]
89-
listify x = [x]
127+
listify x = [x]
128+
129+
iterate' :: (a -> a) -> a -> [a]
130+
iterate' = unfold (\x -> False) id

0 commit comments

Comments
 (0)