Skip to content

Commit

Permalink
Part 1 Chapter 7 Exercise 6, part 4.
Browse files Browse the repository at this point in the history
  • Loading branch information
PiotrJustyna committed Mar 20, 2019
1 parent 94ce28d commit 368e4ab
Showing 1 changed file with 51 additions and 10 deletions.
61 changes: 51 additions & 10 deletions part1_chapter7_exercise6/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@ main = do
putStrLn . show $ chop8' [1, 2, 3, 4, 5, 6, 7, 8, 9]

putStrLn "map (+1) [1, 2, 3]"
putStrLn . show $ map (+1) [1, 2, 3]
putStrLn . show $ map show [1, 2, 3]

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

putStrLn "take 5 $ iterate (+1) 2"
putStrLn . show . take 5 $ iterate (+1) 2

putStrLn "take 5 $ iterate' (+1) 2"
putStrLn . show . take 5 $ iterate' (+1) 2

unfold :: (a -> Bool) -> (a -> a) -> (a -> a) -> a -> [a]
unfold predicate headFunction tailFunction x
| predicate x = []
Expand All @@ -50,17 +56,24 @@ chop8' = unfold null (take 8) (drop 8)
-- Now with that in mind, does it really makes sense to apply "unfold" here? Is the function not supposed to build a list
-- using the starting value and the functions provided? If not, I'm not sure what really is required. If so, though,
-- the type signature defined by the regular "map" is going to make:
-- the implementation unnecessarily complicated
-- map and map' are not going to be interchangable.
-- * the implementation unnecessarily complicated
-- * map and map' are not going to be interchangable.
-- Additionally, "unfold" requires the function f to be of type (a -> a) while "map" requires the function f to be of type (a -> a)
-- so we know already that we won't be able to create an exact, interchangeable replica of the original "map".
-- E.g. we would not be able to use the "map'" like this:
--
-- map' show [1, 2, 3]
--
-- which would be perfectly fine with the original "map".
--
-- My initial thought was to implement function:
--
-- map' :: (a -> b) -> [a] -> [b]
-- map' :: (a -> a) -> [a] -> [a]
--
-- with the signature identical to the regular "map" (just a different implementation) so that both can be used interchangeably.
-- with the signature similar to the regular "map" (just a different implementation) so that both can be used interchangeably.
-- It took me a while before I realized that it is probably more tricky than it should be.
-- This is when I started looking for other solutions, just to get an idea how people interpret the task.
-- What I found mademe even more confused. Examples:
-- What I found made me even more confused. Examples:
--
-- 1. https://github.com/evturn's implementaion: https://github.com/evturn/programming-in-haskell/blob/master/07-higher-order-functions/07.9-exercises.hs
--
Expand All @@ -70,20 +83,48 @@ chop8' = unfold null (take 8) (drop 8)
--
-- map' :: (a -> Bool) -> (a -> a) -> a -> [a]
--
-- 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].
-- It doesn't match the original "map"'s type very precisely and the input argument (second last "a") gets "unfolded" into [a].
--
-- 2. Another example I found is the one by https://github.com/RoccoMathijn: https://github.com/RoccoMathijn/programming-in-haskell/blob/master/chapter07.hs
--
-- map' :: (a -> b) -> [a] -> [b]
-- map' f = unfold (null) (f.head) (tail)
--
-- But this again is not intercangeable with the original "map" as b is simply [a], so the result is really [[a]].
-- 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:
--
-- $ ghci
-- GHCi, version 8.6.3: http://www.haskell.org/ghc/ :? for help
-- Prelude> :load Main
-- [1 of 1] Compiling Main ( Main.hs, interpreted )

-- Main.hs:95:26: error:
-- * Couldn't match type `b' with `[a]'
-- `b' is a rigid type variable bound by
-- the type signature for:
-- map'' :: forall a b. (a -> b) -> [a] -> [b]
-- at Main.hs:94:1-31
-- Expected type: [a] -> [a]
-- Actual type: [a] -> b
-- * In the second argument of `unfold', namely `(f . head)'
-- In the expression: unfold (null) (f . head) (tail)
-- In an equation for map'': map'' f = unfold (null) (f . head) (tail)
-- * Relevant bindings include
-- f :: a -> b (bound at Main.hs:95:7)
-- map'' :: (a -> b) -> [a] -> [b] (bound at Main.hs:95:1)
-- |
-- 95 | map'' f = unfold (null) (f.head) (tail)
-- | ^^^^^^
-- Failed, no modules loaded.
--
-- After more time than I'd like to admit, I finally figured out the implementation which is interchangeable with the original "map".
-- After more time than I'd like to admit, I finally figured out the implementation which is almost interchangeable with the original "map".
-- Terribly inefficient, but I believe it answers the exercise's question precisely.

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

listify :: a -> [a]
listify x = [x]
listify x = [x]

iterate' :: (a -> a) -> a -> [a]
iterate' = unfold (\x -> False) id

0 comments on commit 368e4ab

Please sign in to comment.