7

I tend to fall into the use of forM_ in Haskell which is quite like .each in Ruby or foreach in Scala.

import Control.Monad (forM_)
import Network.BSD (getHostByName, hostAddresses)
import Network.Socket (inet_ntoa)
import System.Environment (getArgs)

resolve address = do
  ent <- getHostByName address
  mapM inet_ntoa (hostAddresses ent)

main = do
  args <- getArgs
  args `forM_` (\address -> do
    ips <- resolve address
    ips `forM_` (\ip -> putStrLn $ address ++ "\t" ++ ip))

It doesn't seem to be idiomatic to me but using mapM_ seems clumsy. Is there an idiomatic way of rewriting this code?

Steven Shaw
  • 6,063
  • 3
  • 33
  • 44
  • 1
    Not a duplicate, but I feel I feel [my answer here](http://stackoverflow.com/questions/16726659/should-do-notation-be-avoided-in-haskell/16733658#16733658) adresses this quite well. I'd say _it's perfectly fine to use `forM_` like that_. (Except as phadej says, `for_` is the more modern alternative, but in practice there's usually no difference between these.) – leftaroundabout Aug 23 '16 at 12:03

2 Answers2

11

It is, though you'd probably better using just for_ :: (Foldable t, Applicative f) => t a -> (a -> f b) -> f () from Data.Foldable.

Also by using it as prefix (i.e. normal function), the code looks like an "ordinary" imperative code:

main = do
    args <- getArgs
    for_ args $ \address -> do
        ips <- resolve address
        for_ ips $ \ip -> putStrLn $ address ++ "\t" ++ ip

P.S. Applicative versions of Monadic "traversals":

  • mapM ~ traverse
  • mapM_ ~ traverse_
  • forM ~ for
  • forM_ ~ for_
phadej
  • 11,947
  • 41
  • 78
  • 1
    These operations are inherently `Applicative`. There are versions more naturally monadic. `traverseM :: (Monad m, TraversableM t) => (s -> a -> m (s, b)) -> t a -> m (t b)`. The latter can be forced into `Traversable` using `StateT`, but I'm pretty sure that stops working when you want a McBride-style indexed monad. – dfeuer Aug 23 '16 at 15:20
  • Nice answer! Exactly what I was looking for! – Steven Shaw Aug 26 '16 at 06:52
1
main = getArgs >>= mapM_ (\address ->
  resolve address >>= mapM_ (\ip ->
    putStrLn $ address ++ "\t" ++ ip))

And once we have ArgumentDo we won't have to bracket that lambda expression.

ListT also happens to be applicable:

main = void $ runListT $ do
  address <- ListT getArgs
  ip <- ListT $ resolve address
  liftIO $ putStrLn $ address ++ "\t" ++ ip
Gurkenglas
  • 2,317
  • 9
  • 17
  • `mapM_` probably. I'm not sure code-golfing is really helpful here though. – phadej Aug 23 '16 at 12:16
  • Where does the `address` come from? – Lee Duhem Aug 23 '16 at 15:04
  • Whoops, didn't notice that. Btw the golfing just emerged from the process of removing the fors. Editing. – Gurkenglas Aug 23 '16 at 17:17
  • This use of `transformers` `ListT` is probably tolerable in Stephen Shaws case but it courts disaster. It's a standard description of a space leak waiting to happen. – Michael Aug 23 '16 at 20:13
  • See http://lpaste.net/179912 which is slower at these numbers, on my computer, when it uses a 'ListT done right' -- because it is translating through list operations. But the transformers version nearly exhausts memory and will exhaust memory if either number is bumped up. The streaming ListT uses constant memory, though it has to construct lists all the time. – Michael Aug 23 '16 at 20:15