6

I create a lot of temporary variables in Haskell:

main = do
    let nums'' = [1..10]
    let nums' = a . bunch . of_ . functions $ nums''
    let nums = another . bunch . of_ . functions $ nums'
    print nums

That is, I don't want to write a long chain of functions like so:

let nums = another . bunch . of_ . functions . a . bunch . of_ . functions $ [1..10]

Because it becomes unreadable to me, so I try to group the functions according to what they do. In the process I end up creating a bunch of ugly temporary variables like nums'' and nums' (I could give them more meaningful names, but the point still stands...every new line means a new variable). This is a case where a shadowed variable would result in cleaner code. I'd like to do something like:

let nums = [1..10]
nums = a . bunch . of_ . functions $ nums
nums = another . bunch . of_ . functions $ nums

I.e. exactly the same as above but without the temporary variables. Is there any way to do this in Haskell? Maybe the whole thing could be wrapped in a "transaction":

atomically $ do
  (...this code...)
  return nums

Something that would let Haskell know that the code in this section contains shadowed variables, and it should only worry about the end result. Is this possible?

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
Vlad the Impala
  • 15,572
  • 16
  • 81
  • 124
  • You could also use the State monad if you want to pass a value along without mutation yet without creating a bunch of names – amindfv May 17 '12 at 19:58
  • 3
    I might argue that (a) if you have meaningful names to give the intermediate steps, there's no problem; and (b) if you don't have meaningful names to give the intermediate steps, then there's no benefit to breaking them up. And from (a) and (b) we can conclude that there's no problem. It depends on whether you accept the premises, of course. – Antal Spector-Zabusky May 17 '12 at 20:16
  • 4
    I don't agree that reusing the same name for different purposes necessarily makes it more readable. Now the reader has to figure out which variable binding each occurrence referes to. – augustss May 17 '12 at 20:22
  • I do not view shadowing as avoiding creating temporary variables. Rather you're creating the same number of temporary variables which all have the same name, which seems to be an *increase* in conceptual complexity rather than a decrease. I would also view a series of minor variations like `nums`, `nums'`, `nums''`, or `nums1`, `nums2`, `nums3`, or etc, as pretty much equivalent, since there's no *content* in any of the names, but you at least have the ability to refer to any of them, unlike with a series of shadowed variables. – Ben May 18 '12 at 01:25

4 Answers4

14

This style is very common:

let nums = another
         . bunch
         . of_
         . functions
         . a
         . bunch
         . of_
         . functions
         $ [1..10]

It clearly delineates the code; while . serves the place of the temporary variable name.

And it avoids the dangerous issues that can happen when you start shadowing variable names -- accidentally referring to the wrong x will get you into trouble sooner or later.

Don Stewart
  • 137,316
  • 36
  • 365
  • 468
10

Here's a suggestion nobody else has given which I use from time to time: you may like naming your functions rather than naming your values! For example, perhaps you might write:

let runningSum = a . bunch . of_ . functions
    weight     = another . bunch . of_ . functions
in weight . runningSum $ [1..10]
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
6

If you absolutely must, it's possible, but not idiomatic. Use what Don or luqui suggest instead.

main = do
    nums <- return [1..10]
    nums <- return $ a . bunch . of_ . functions $ nums
    nums <- return $ another . bunch . of_ . functions $ nums
    print nums

If you're not in a monad, you can always start a new do block in the identity monad.

augustss
  • 22,884
  • 5
  • 56
  • 93
6

Since your names (nums, nums', nums'', ...) convey no information about the grouping, you can just use line breaks to group the functionality and communicate the same thing:

main =
    let nums = a . bunch . of_ . functions
             . another . bunch . of_ . functions
             $ [1..10]
    in print nums

However, I would suggest that instead of doing this, you give the subcomputations names which do convey information, eg. normalizedNums, average, etc. Then there is no ugly shadowing issue because you are using distinct names.

luqui
  • 59,485
  • 12
  • 145
  • 204