Rabu, 26 Januari 2011

Perl diamond operator in Haskell

As a Perl enthusiastic system admin, i'm troubled to forget the following snippets:
while (<>) { print(uc); }

What nice about the construct is that the diamond operator <> will automatically handling read from stdin, whether you give it from a pipe, from the arguments, or wait from keyboard if none given.

As a Haskell newbie, below are my current attempt to mimicking that:
import System.IO
import System.Environment
import qualified Data.ByteString.Lazy.Char8 as BS
import Control.Monad (liftM)
import Data.Char (toUpper)

main = getArgs >>= grabContents >>= BS.putStr
where grabContents args =
if null args then BS.hGetContents stdin
else mapM BS.readFile args >>= return . BS.concat


Working on it further, i try to abstract it like so:
(<>) :: (BS.ByteString -> IO a) -> IO a
(<>) f = do
args <- getArgs
contents <- if null args
then BS.hGetContents stdin
else getArgs >>= mapM BS.readFile >>= return . BS.concat
f contents


So, we can write main function which quite nice in a imperative sense like below:
main = (<>) (\ str ->
do let str' = BS.map toUpper str
BS.putStr str')


Some note: First, I'm using ByteString here because based on my dirty inspection (just simply using something like "time myprog args ..."), ByteString is higher in performance than plain String or Data.Text. I still believe that String, ByteString, Text has their own goodness and still make a mind how to justify on any given situation, one of them is the best to pick.

Second note, it is also the lazy version of ByteString i'm using, because i notice that function getContents of that module behave lazy as i expected --I just expect it won't read the whole data file and store it to memory at once. Using the lazy version, i just confident as reading line by line as the perl <> operator do.

My further investigation would be whether using getContents does not break when i'm do for example treat "\n\n" as line/record field separator instead of standard "\n". Perl have this capability as simple as assign any line separator we want in $/ predefined global variable.

0 komentar:

Poskan Komentar