Problem 1: ---------- There are numerous benefits to working without side effects! Some of the most important are: It makes code easier to understand, since a function's output can't be influenced by *other* functions. (If we allow global variables, then a function's output could depend on global variables, and those could be modified by other functions.) It also makes our code less error prone, since a function's output can't be affected by functions that executed before it. (A bug in one function, or a mis-sequencing of function calls, can't affect another function.) Since a function's value can't be influenced by other functions, it allows us to test a function in isolation. It will behave exactly the same way if we move it out of a large project, or migrate it to another project. The lack of state also allows the compiler to parallelize our code much more easily. Since the evaluation of one function can't influence the evaluation of another, they can be evaluated *concurrently* without any issues. Lack of state also makes lazy evaluation possible. It's hard to predict when -- or *if* -- an expression will be evaluated when lazy evaluation is being used. That's incompatible with a programming style where programmers always expect a computation to be performed, and in the order specified by the program. Problem 2: ---------- There are two potential concerns with this expression: head (map (10 `div`) [10,9..]) One is that the .. is a recipe for producing an infinite list, and our code won't terminate if we had to actually generate all of those values. The other is that there's a 0 hiding in that [10,9..], which means we're trying to divide by zero if we generate enough output values. The expression will evaluate to 1.0 without any issues though. Using lazy evaluation, the map expression won't actually produce all of its outputs immediately, and therefore won't force the evaluation of the infinite input list. In fact, the map expression will only be forced to produce its first output, since that's all that head needs. That first output value is 10/10, which won't cause any errors. Problem 3: ---------- Here's one way to define shift in terms of higher-order function: shift nums = map (+offset) nums // Add shift amount to each value where offset = 100-(maximum nums) // shift amount You could also write it recursively, but in that case you'd need a helper function to find the offset since it's a function of the entire list: shift nums = addOffset (largest nums)) nums largest [n] = n largest (n:ns) = max n (largest ns) addOffset _ [] = [] addOffset offset (n:ns) = (n+offset) : addOffset offset ns Problem 4: ---------- a) Here's one way to write it recursively: pattern _ [] = [] pattern test (c:cs) | test c = '-' : pattern test cs | otherwise = c : pattern test cs b) It's easier to write without explicit recursion: pattern test word = map transform word where transform c = if (test c) then '-' else c c) Haskell would report its type as: (Char -> Bool) -> [Char] -> [Char] It takes a function from Char to Bool as its first input, and a list of Char as its section, and it produces a list of Char as its output. Problem 5: ---------- 5a) group1 = ["ok","bad","good"] 5b) group2 = ["good","fine","great"] 5c) mystery would return True foldr (||) [True,False,False] --> True 5d) The mystery function takes a list of two-tuples, and returns True if any of the items in the second positions of the tuples are also found in the first.