Currying subtraction
Asked Answered
S

5

56

If we want to map a function that increases every element of a range by 1, we could write

map (\x -> x + 1) [1..5]

but I guess most people would just go for

map (+1) [1..5]

instead. But this obviously doesn't work with (-1) since that's negative one.

So the first thing that came to mind was

map (+(-1)) [1..5]

which would make sense considering how subtraction is defined in the Prelude (x - y = x + negate y), but looks a bit odd to me. I then I came up with

map (flip (-) 1) [1..5]

This somehow looks better to me, but is maybe a bit too complicated.

Now I know this no big deal, but I'm wondering if I'm missing a more obvious way to write this? If not, which of the 2 ways would you prefer? I'm really just asking because often it's small details like this that make your code more idiomatic and hence pleasant for other developers who have to read it.

Solution

Now that I got a couple of answers, I think my personal favorite is

map (subtract 1) [1..5]

followed by

map pred [1..5]

mostly because the first one is really explicit and nobody needs to guess/look up what pred means (predecessor).

Sandasandakan answered 15/12, 2010 at 20:29 Comment(5)
A small note: the problem isn't currying, it's the operator section syntax. (-) 1 is a (syntactically valid) way to partially apply the curried - function.Deer
I was struggling with a title for this and couldn't come up with anything better. Also map ((-) 1) [1..5] doesn't work, hence the version with flip.Sandasandakan
If they are Integral, and you just want to subtract 1, why not map predGaekwar
@Grazer Because I didn't know about it, hence the question if I'm missing something obvious.Sandasandakan
See also haskell.org/haskellwiki/Unary_operatorEstafette
A
50

You can use the subtract function instead of - if you want to right-section subtraction:

map (subtract 1) [1..5]
Acierate answered 15/12, 2010 at 20:36 Comment(0)
U
9

Since - is both the infix subtract and the prefix negate, you can't use the (*x) (where * is an infix operator and x a value) syntax for -. Luckily Prelude comes with negate and subtract, which is \x -> -x and \x y -> y-x respectively, so that you may use those where you need to differentiate between the two.

Unthankful answered 15/12, 2010 at 23:18 Comment(0)
R
3

After many years since this question was asked, in GHC 9 we now have the LexicalNegation extension which allows the section (- 1), as long we use whitespace to separate the minus sign from the number.

Indeed, after enabling the extension, we have:

> map (subtract 1) [1..5]       -- still works, of course
[0, 1, 2, 3, 4]

> map (- 1) [1..5]              -- with whitespace
[0, 1, 2, 3, 4]                 -- (- 1) is now a section

> map (-1) [1..5]               -- no whitespace
*error*                         -- (-1) is now a negative literal
Rockhampton answered 30/8, 2021 at 10:52 Comment(0)
S
2

I think map (\x -> x - 1) [1..5] transmits the programmer's intention better, since there's no doubt about what is being subtracted from what. I also find your first solution, map (+(-1)) [1..5], easy to read too.

Shawntashawwal answered 15/12, 2010 at 22:42 Comment(0)
E
2

I don't like subtract because it's confusingly backwards. I'd suggest

minus :: Num n => n -> n -> n
minus = (-)
infixl 6 `minus`

Then you can write

map (`minus` 1) [1..5]
Eb answered 25/8, 2014 at 14:14 Comment(1)
I prefer just (+(-1)). The trick with the new name is nice, but it takes up a valuable name. Too bad (`-` 1) is invalid syntax.Kaplan

© 2022 - 2024 — McMap. All rights reserved.