0

I want to build function below using only prelude built in function without importing Data.Bool. I want to replace bool function to something else so I don't have to import Data.Bool and function prints same output as below function. How can I do this so it returns same output?

increment :: [Bool] -> [Bool]
increment x = case x of
  [] -> [True]
  (y : ys) -> not y : bool id increment y ys
sypy2323
  • 23
  • 5
  • 2
    Really, I think you should throw away this code (it's disgusting), and think about how *you* would write an `increment` function. – dfeuer Dec 10 '20 at 05:56
  • 3
    just substitute the source code for `bool` into your code, simplify, and make the two case explicit in the pattern . – Will Ness Dec 10 '20 at 07:00
  • @dfeuer What's disgusting about it? This looks like a pretty reasonable way to do it to me. Probably the only thing I'd really want to change would be to move the patterns to the left of the equals sign, but that's a completely aesthetic syntactical thing; in the big picture this looks perfectly cromulent to me. – Daniel Wagner Dec 10 '20 at 18:24
  • @DanielWagner, my objection is mostly an aesthetic one, but I don't like the separate calculation of bit and carry. One case match on `y` will be sufficient. Less problematic for an exercise, but problematic in practice: the result will be a list of thunks instead of a list of calculated bits. – dfeuer Dec 10 '20 at 18:30

2 Answers2

5

bool from Data.Bool is doing exactly the same thing as a if statement, so it can be a way to implement it:

    bool x y b = if b then y else x
Renegatto
  • 51
  • 1
1

@dfeuer suggested in a comment that you should throw away this code because it's disgusting, and instead try to write it yourself. This might be distressing to you if you're the one that wrote the code in the first place and can't see why it's disgusting, so allow me to elaborate.

In fact, "disgusting" is too strong a word. However, the code is unnecessarily complex and difficult to understand. A more straightforward implementation does all the processing using pattern matching on the function argument:

increment :: [Bool] -> [Bool]
increment [] = [True]
increment (False : rest) = True  : rest
increment (True  : rest) = False : increment rest

This code is easier to read for most people, because all of the decision logic is at the same "level" and implemented the same way -- by inspecting the three patterns on the left-hand side of the definitions, you can see exactly how the three, mutually exclusive cases are handled at a glance.

In contrast, the original code requires the reader to consider the pattern match against an empty versus not empty list, the effect of the "not" computation on the first boolean, the bool call based on that same boolean, and the application of either the function id or the recursive increment on the rest of the boolean list. For any given input, you need to consider all four conceptually distinct processing steps to understand what the function is doing, and at the end, you'll probably still be uncertain about which steps were triggered by which aspects of the input.

Now, ideally, GHC with -O2 would compile both of these version to exactly the same code internally. It almost does. But, it turns out that due to an apparent optimization bug, the original code ends up being slightly less efficient than this rewritten version because it unnecessarily checks y == True twice.

K. A. Buhr
  • 45,621
  • 3
  • 45
  • 71