Haskell where clause syntax inside a do block
Solution 1
There are three similar (but distinct) ways of defining stuff here:
You can attach
where
clauses after certain definitions--mostly equation-style bindings. So you could put one at the end of your function, or after something defined withlet
or a surroundingwhere
clause.On the other hand,
let x = ... in ...
is an expression that evaluates to the part afterin
, which is the only place the stuff afterlet
is visible.Inside a
do
block, because there's already an implicit nesting of scope (things are visible after they're first defined), you can use justlet x = ...
alone. This is really the same thing as the previous form--the rest of thedo
block after thelet
is effectively thein ...
portion.
If you want a local definition that uses something defined within the do
block, your only choice is the third (or passing the other value(s) as argument(s)). For an independent helper functions like your example, however, any style works. Here's your example, to demonstrate each:
The first style, where func
is visible anywhere in foo
, including anything else defined in the where
clause:
foo = do ...
mapM_ func aList
...
return aValue
where func x = x + 1
The second style, where func
is only visible inside the let
expression, which in this case is the entire do
block:
foo = let func x = x + 1
in do
...
mapM_ func aList
...
return aValue
And the third style, defining it inside the do
block. In this case, func
is only visible after the let
; in the first ...
it hasn't been defined yet.
foo = do ...
let func x = x + 1
mapM_ func aList
...
return aValue
Oh, and for good measure: Since let ... in ...
is an expression, you can also use it anywhere you have an expression, to name some local definitions. So here's another example:
foo = do ...
let func x = x + 1 in mapM_ func aList
...
return aValue
As before, func
is only visible inside the let
expression, which in this case is the single expression after it, nowhere else.
Solution 2
Another option is to use forM_
instead of mapM_
, which flips the order of the arguments. You can then use the $
operator with a trailing lambda expression like this:
do
forM_ aList $ \x -> do
...
return aValue
Solution 3
Shouldn't your where
be at the end of the function?
Like this:
function aList aValue = do
mapM_ func aList
return aValue
where func x = x + 1
Related videos on Youtube
Ralph
Physician, retired. Retired software engineer/developer (Master of Science in Computer Science). I currently program mostly in Go. I am particularly interested in functional programming. In past lives, I programmed in Java, Scala, Basic, Fortran, Pascal, Forth, C (extensively, at Bell Labs), C++, Groovy, and various assembly languages. Started programming in assembly language in 1976. I started a martial arts school in 1986 (Shojin Cuong Nhu in New Jersey) and currently teach at the Tallest Tree Dojo in Gainesville, Florida. I like to cook. I am an atheist. Email: user: grk, host: usa.net
Updated on June 05, 2022Comments
-
Ralph almost 2 years
I am trying to refactor a
mapM_
function call inside ado
block in Haskell. I would like to extract the lambda to a (locally) named function to make the code more readable.My code originally looks like this:
do -- ... mapM_ (\x -> x + 1) aList return aValue
I would like to change it to
do -- ... mapM_ func aList where func x = x + 1 return aValue
but I am getting a syntax error on the
return aValue
line. My actual lambda is more complicated :-), but I did try it with this same lambda to make sure it was not an issue in the lambda code.How can I rewrite this code? Should I use
let
...in
instead? -
Ralph over 11 yearsI'm not sure. I would like to find some way to extract the lambda into a very local function for improved readability. If I have to put it at the end of the top-level function, that makes it much less usable. I'm a Haskell noob.
-
Ralph over 11 yearsThanks. The third form looks like it will allow me to define the lambda function close enough to the
mapM_
to be useful. I'm only worried that it pollutes the top-level function namespace with the namefunc
defined in thelet
(minor issue). -
Ralph over 11 yearsI had not considered that alternative. I'll look at my code again to see if that is more readable. In other cases where I need a monadic list traversal, but there is no convenient flipped version like
forM_
, I can just useflip
to make a new function. -
C. A. McCann over 11 years@Ralph: Well, it's just a matter of what scope you want it visible in. If your
do
block is big enough that you need to worry about polluting the namespace inside it, you should probably consider breaking it into smaller pieces anyway. :] -
Ralph over 11 yearsYeah, that had occurred to me too. I'm translating some functional Scala code that some idiot wrote (:-)) with functions that are longer than they should be.