Haskell idiomatic syntax without do blocks or copy brackets?

I'm new to Haskell and trying to find a way to pass multiple IO-bound values ​​to a function to work with the C library. Most people seem to use the <- operator inside the do block, for example:

gxy = x ++ y interactiveConcat1 = do {x <- getLine; y <- getLine; putStrLn (gxy); return ()} 

This makes me feel like I'm doing C, except that emacs cannot automatically back off. I tried to write this in a more Lispy style:

 interactiveConcat2 = getLine >>= (\x -> getLine >>= (\y -> putStrLn (gxy) >> return () )) 

It looks like a mess and contains a string of closed parentheses that you should count at the end (in addition, emacs can reliably help with this task in Lisp, but not in Haskell). Another way to say

 import Control.Applicative interactiveConcat3 = return g <*> getLine <*> getLine >>= putStrLn 

which looks pretty neat but is not part of the base language.

Are there any less time consuming notations for peeling values ​​from IO cells? Perhaps there is a cleaner way to use the elevator * or fmap? I hope it’s not too subjective to ask what is considered “idiomatic”?

Also, any advice on creating emacs would be better than (Haskell Ind). Thanks!

John

Edit: I came across https://wiki.haskell.org/Do_notation_considered_harmful and realized that the nested parentheses in the lambda chain that I wrote were not needed. However, it seems that the community (and the ghc developers) have adopted an application-based style using <*> etc., which seems to make the code easier to read, despite the headaches in determining operator priority.

+6
source share
2 answers

Note: This post is written by a competent Haskell. You can save it as Main.lhs and try it in your GHCi.


First a quick note: you can get rid of semicolons and curly braces in do . In addition, putStrLn is of type IO () , so you do not need return () :

 interactiveConcat1 = do x <- getLine y <- getLine putStrLn $ gxy 

We will work with IO , so importing Control.Applicative or Control.Monad will come in handy:

 > module Main where > import Control.Applicative > -- Repeat your definition for completeness > g :: [a] -> [a] -> [a] > g = (++) 

You are looking for something like this:

 > interactiveConcat :: IO () > interactiveConcat = magic g getLine getLine >>= putStrLn 

What type of magic needed? It returns an IO String , takes a function that returns a String and takes a regular String s, and takes two IO String s:

 magic :: (String -> String -> String) -> IO String -> IO String -> IO String 

We can probably generalize this type to

 > magic :: (a -> b -> c) -> IO a -> IO b -> IO c 

A quick hoogle search reveals that there are already two functions of almost this type: liftA2 from Control.Applicative and liftM2 from Control.Monad . They are defined for each Applicative and - in the case of liftM2 - Monad . Since IO is an instance of both, you can choose one of them:

 > magic = liftA2 

If you are using GHC 7.10 or later, you can also use <$> and <*> without importing and recording interactiveConcat as

 interactiveConcat = g <$> getLine <*> getLine >>= putStrLn 

For completeness, let's add main so that we can easily test this functionality with runhaskell Main.lhs :

 > main :: IO () > main = interactiveConcat 

A simple check shows that it works as intended:

  $ echo "Hello \ nWorld" |  runhaskell Main.lhs
 HelloWorld 

References

+14
source

You can use liftA2 (or liftM2 from Control.Monad ):

 import Control.Applicative (liftA2) liftA2 g getLine getLine >>= putStrLn 
+4
source

All Articles