Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Post History
Recall that . (composition) is defined as: f . g = \x -> f (g x) That is, it composes two functions of one argument each. The result (another function of one argument) passes its argument x ...
Answer
#1: Initial revision
Recall that `.` (composition) is defined as: f . g = \x -> f (g x) That is, it composes two functions of one argument each. The result (another function of one argument) passes its argument `x` to `g`, and the result of that to `f`. Your first attempt was: map (subtract 1) . take -- inline the definition of (.) -- with f = map (subtract 1) -- and g = take \x -> map (subtract 1) (take x) This doesn't work because `map` expects a list as its second argument, but `take x` is not a list. (It is a function mapping lists to lists, `[a] -> [a]`.) Your second attempt: map (subtract 1) . take n -- inline the definition of (.) -- with f = map (subtract 1) -- and g = take n \x -> map (subtract 1) (take n x) This works: `take n x` is a list and `map` takes a list. Your third attempt: map (subtract 1) . take n l -- inline the definition of (.) -- with f = map (subtract 1) -- and g = take n l \x -> map (subtract 1) (take n l x) This fails because `take n l x` is a type error. It tries to apply `take n l` to `x`, which would require `take n l` to be a function, but it is a list. --- In summary, `.` composes two functions of a single argument each. `map foo . take` fails because `take` is a function of two arguments; `map foo . take n l` fails because `take n l` is not a function at all (it takes zero arguments). In the working version, `map foo . take n`, we partially apply `take` and `take n` is indeed a function of one argument. --- In order to compose a function of one argument with a function of two arguments, we could define another operator: f .: g = \x y -> f (g x y) Then we could write: dec_first = map (subtract 1) .: take Alternatively, we could write: dec_first = (map (subtract 1) .) . take -- which is the same as dec_first = (.) (map (subtract 1)) . take The reason this works is that the first `.` waits for one argument and passes it to `take`, then passes the result of that (a partially applied `take x`) to another `.`, which results in the `map (subtract 1) . take x` code that we already know works: -- inline the first (.) -- with f = (.) (map (subtract 1)) -- and g = take dec_first = \x -> (.) (map (subtract 1)) (take x) -- write (.) in infix notation dec_first = \x -> map (subtract 1) . take x We could even write our `.:` operator as a composition of compositions: (.:) = (.) . (.) -- inline the first layer of (.) (.:) = \x -> (.) ((.) x) -- eta-expand (.:) = \x y -> (.) ((.) x) y -- convert to infix notation (.:) = \x y -> ((.) x) . y -- inline the second layer of (.) (.:) = \x y z -> (.) x (y z) -- convert to infix notation (.:) = \x y z -> x . y z -- inline the third layer of (.) (.:) = \x y z w -> x (y z w) -- rename parameters (.:) = \f g x y -> f (g x y)