How to change IO() into [String] in Haskell? - haskell

I am making a frame that print a character frame around a character as I show below.
This is example of frame. Example to understand what I actual frame is and What i am want from all these function right, left, up and down:
a is list of string that is A.
Main> showMatDownAttach '#' a
aaaaa
a a
a a
aaaaaaa
a a
a a
a a
#########
Main> showMatDownAttach '#' a
aaaaa
a a
a a
aaaaaaa
a a
a a
a a
#########
I also have for right and left. Now I want to make a function that can combine all of them in one function. How I can do this?
a = [" aaaaa "," a a "," a a "," aaaaaaa "," a a "," a a "," a a "]
--badar = putStr( concat (map (++ "\n")(letter 'a')))
--showMat :: Char -> IO()
--showMat ch = putStr (concat ( map(++ "\n") (letter 'a')))
replicateIt :: Int -> [Char] -> [Char]
replicateIt x ls=take x (cycle ls)
--repeatIt :: Int -> [Char] -> [[Char]]
repeatIt num []=[]
repeatIt num (x:sx)= replicateIt num [x]:(repeatIt num sx)
hStretchChar :: Int -> Char -> String
hStretchChar i ch = replicate i ch
hStretchString :: Int -> String -> String
hStretchString i sts = concat ( map ( hStretchChar i) sts)
hStretchListOfString :: Int -> [String] -> [String]
hStretchListOfString i stlist = map (hStretchString i ) stlist
vStretchString :: Int -> String -> String
vStretchString i str = concat (replicate i (str ++ "\n"))
vStretchListOfString :: Int -> [String] -> [String]
vStretchListOfString i strList = map (vStretchString i) strList
stretch :: Int -> Int -> [String] -> [String]
stretch i j strList = vStretchListOfString i (hStretchListOfString j strList)
showMat' :: [String] -> IO()
showMat' strList = putStr (concat (stretch 1 1 strList))
--Left Attach Character
leftattach :: Char -> [String] -> [String]
leftattach a strlist = map ( a: ) strlist
showMatCharAttachLeft :: Char -> [String] -> IO()
showMatCharAttachLeft a strList = putStr (concat (stretch 1 1 ( leftattach a strList)))
charToString :: Char -> String
charToString a = a:[]
--Right Attach Character
rightattach :: Char -> [String] -> [String]
rightattach a strlist = map (++(charToString a)) strlist
showMatCharAttachRight :: Char -> [String] -> IO()
showMatCharAttachRight a strList = putStr (concat (stretch 1 1 ( rightattach a strList)))
--Up Attach Character
upattach :: Char -> [String] -> [String]
upattach a strList = take (length (head strList)) (cycle (charToString a)) : strList
showMatUpAttach :: Char -> [String] -> IO()
showMatUpAttach a strList = putStr (concat (stretch 1 1 (upattach a strList)))
--Down Attach Character
downattach :: Char -> [String] -> [String]
downattach a strList = strList ++ listOfCharTolistOfString (take (length (head strList)) (cycle (charToString a)))
showMatDownAttach :: Char -> [String] -> IO()
showMatDownAttach a strList = putStr (concat (stretch 1 1 (downattach a strList)))
--test0 a strList = listOfCharTolistOfString (take (length (head strList)) (cycle (charToString a)))
listOfCharTolistOfString :: [Char] -> [String]
listOfCharTolistOfString a = a:[]

IO () is a very opaque type; there isn't much you can do with it, and there isn't any meaningful way to convert it to [String]. Once you're in IO, you can't get out of it.
Generally in Haskell you write most of your code without using IO. Here's a solution to the problem to demonstrate what I mean. Notice that all of the "frame" code is defined with pure functions, and IO doesn't get introduced until main at the very end.
import Data.Foldable (traverse_)
import Data.List (repeat)
frame1 :: a -> [a] -> [a]
frame1 f xs = [f] ++ xs ++ [f]
frame2 :: a -> [[a]] -> [[a]]
frame2 f grid = frame1 edge $ frame1 f <$> grid
where edge = take (width grid + 2) $ repeat f
width :: [[a]] -> Int
width [] = 0
width (x:_) = length x
a :: [[Char]]
a = [ " aaaaa "
, " a a "
, " a a "
, " aaaaaaa "
, " a a "
, " a a "
, " a a "
]
main :: IO ()
main = traverse_ putStrLn $ foldr frame2 a "* &"
Output:
***************
* *
* &&&&&&&&&&& *
* & aaaaa & *
* & a a & *
* & a a & *
* & aaaaaaa & *
* & a a & *
* & a a & *
* & a a & *
* &&&&&&&&&&& *
* *
***************

So, IO () encapsulates all side-effects and is not just about console output. So pedantically there is no way to "change IO () to [String]"
With that said, I believe System.Posix.Redirect is what you are looking for. You just want to call showMatDownAttach to capture stdout to get the [String] you want.
A quick Google search reveals a couple more packages to do this as well:
https://hackage.haskell.org/package/io-capture
https://hackage.haskell.org/package/silently
Now doing this is not very idiomatic Haskell and if you have access to showMatDownAttach you'll want to change it to something like what https://stackoverflow.com/a/40701235/111021 suggests. But since you ask this question I believe you already considered that and somehow that's not an option.

Related

Haskell -- How to draw lists of characters from a string?

How to draw lists of characters from a string?
For example, for a string "abcdefghi", return three strings "adg" "deh" "cfi".
for the first string, it's the 1st, n+1th and n+2th characters from the original string, n is the number of strings to generate.
Two solutions:
First: cryptic and short
drawlist :: Int -> String -> [String]
drawlist n s = [map fst $ filter snd $ zip str [mod c n == i | c<-[0..] ] | i <- [0..(n-1)]]
Second: long and maybe clearer
initlist :: Int -> [String]
initlist 0 = []
initlist n = "":(initlist (n-1))
addtonth :: [String] -> Int -> Char -> [String]
addtonth (x:xc) 1 c = (x++[c]):(addtonth xc 0 c)
addtonth (x:xc) i c = x:(addtonth xc (i-1) c)
addtonth [] i c = []
drawlist' :: Int -> String -> Int -> [String] -> [String]
drawlist' n (x:xs) i s = drawlist' n xs (mod (i+1) n) $ addtonth s (i+1) x
drawlist' n [] i s = s
drawlist :: Int -> String -> [String]
drawlist n s = drawlist n s 0 $ initlist n
Test:
Prelude> str = "abcdefghi"
Prelude> drawlist 2 str
["acegi","bdfh"]
Prelude> drawlist 3 str
["adg","beh","cfi"]
Prelude> drawlist 4 str
["aei","bf","cg","dh"]
Prelude> drawlist 5 str
["af","bg","ch","di","e"]

How to foldl an io operation on a list generated by a zip in order to print 2 columns?

I have seen several questions regard to use of foldl and IO but none of them seem to provide a solution to my case.
I am simply trying to output two columns of numbers.
0 256
1 256
2 256
...
256 256
0 255
1 255
2 255
3 255
4 255
I have tried the following but I was only able to print a single line and not an entire column:
zipMerge :: [a] -> [b] -> [(a,b)]
zipMerge [] _ = []
zipMerge _ [] = error "second list cannot be empty"
zipMerge (a:as) bs = (zip (replicate (length bs) a) bs) ++ (zipMerge as bs)
-- foldl :: (a -> b -> a) -> a -> [b] -> a
printPixel :: IO() -> (Int, Int) -> IO()
printPixel _ (i, j) = putStrLn (show i) ++ " " ++ (show j)
printPixels :: [(Int, Int)] -> IO()
printPixels colors = foldl printPixel (return ()) colors
printGradient :: IO()
printGradient = do
putStrLn "255"
let is = [0..256]
js = [256,255..0]
printPixels (zipMerge js is)
What am I doing wrong ?
A fold is definitely overkill here. The idea of fold is to keep some sort of state as you traverse the list, but in your case there is no state to keep: you just need to perform an effect for every element in order, and independently of other elements.
To do this, use mapM_:
printPixel :: (Int, Int) -> IO()
printPixel (i, j) = putStrLn $ (show i) ++ " " ++ (show j)
printPixels :: [(Int, Int)] -> IO()
printPixels colors = mapM_ printPixel colors
Or its twin for_, which is the same thing, but with arguments in reverse order (and weaker constraints), which lets you provide the effect as a lambda-expression, but without parentheses, making it look almost like the for constructs in C-like languages:
printPixels :: [(Int, Int)] -> IO()
printPixels colors = for_ colors $ \(i, j) -> putStrLn $ (show i) ++ " " ++ (show j)

Concatenate to a list of string another string

The below code gives back a list of String but I want it work on multiple cases. The problem is that I can't create the same exact result with recursion.
The program gives back the following result:
replaceTabs 6 ["\thello world"]
=> [" hello world"]
Now this should work with a longer list like:
replaceTabs 6 ["asd dsa","\thello world"]
=> ["asd dsa"," hello world"]
Simple concat doesn't work, because it will give back undefined pattern.
replaceTab' :: Int -> [[Char]] -> [Char]
replaceTab' n [[x]] =
if x == '\t' then replicate n ' '
else [x]
replaceTabs :: Int -> [String]-> [String]
replaceTabs n [""] = [""]
replaceTabs n (x:xs) = (return . concat $ [replaceTab' n [a] | a <- (map (:[]) (x))])
This
replaceTab' :: Int -> [[Char]] -> [Char]
is the same as,
replaceTab' :: Int -> [String] -> String
What you should focus on is implementing a function,
replaceTab :: Int -> String -> String
which "fixes" a single String. Then replaceTabs is simply,
replaceTabs :: Int -> [String] -> [String]
replaceTabs n = map (replaceTab n)

Switching to ByteStrings

EDIT: I followed Yuras and Dave4420's advices (Thanks). I still have some errors. Updated the question. Finally I will use meiersi's version (Thanks) but I still want to find my errors...
I have a simple script that goes like this:
import System.Environment
getRow :: Int -> String -> String
getRow n = (!!n) . lines
getField :: Int -> String -> String
getField n = (!!n) . words'
words' :: String -> [String]
words' str = case str of
[] -> []
_ -> (takeHead " ; " str) : (words' (takeTail " ; " str))
takeHead :: String -> String -> String
takeHead st1 st2 = case st2 of
[] -> []
_ -> if st1 == (nHead (length st1) st2) then [] else (head st2):(takeHead st1 (tail st2))
takeTail :: String -> String -> String
takeTail st1 st2 = case st2 of
[] -> []
_ -> if st1 == (nHead (length st1) st2) then nTail (length st1) st2 else takeTail st1 (tail st2)
nTail :: Int -> String -> String
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (tail str)
in if (length str) < n then str else rec n str
nHead :: Int -> String -> String
nHead n str = let rec n str = if n == 0 then [] else (head str):(rec (n - 1) (tail str))
in if (length str) < n then str else rec n str
getValue :: String -> String -> String -> String
getValue row field src = getField (read field) $ getRow (read row) src
main :: IO ()
main = do
args <- getArgs
case args of
(path: opt1: opt2: _) -> do
src <- readFile path
putStrLn $ getValue opt1 opt2 src
(path: _) -> do
src <- readFile path
putStrLn $ show $ length $ lines src
It compiles and works. Then I wanted to switch to ByteStrings. Here is my attempt:
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.Char8 as Bc (cons, empty,unpack)
import qualified Data.ByteString.Lazy.UTF8 as Bu (lines)
import qualified System.Posix.Env.ByteString as Bg (getArgs)
separator :: B.ByteString
separator = (Bc.cons ' ' (Bc.cons ';' (Bc.cons ' ' Bc.empty)))
getRow :: Int -> B.ByteString -> B.ByteString
getRow n = (`B.index` n) $ Bu.lines
getCol :: Int -> B.ByteString -> B.ByteString
getCol n = (`B.index` n) $ wordsWithSeparator
wordsWithSeparator :: B.ByteString -> [B.ByteString]
wordsWithSeparator str = if B.null str then [] else (takeHead separator str):(wordsWithSeparator (takeTail separator str))
takeHead :: B.ByteString -> B.ByteString -> B.ByteString
takeHead st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then B.empty else B.cons (B.head st2) (takeHead st1 (B.tail st2))
takeTail :: B.ByteString -> B.ByteString -> B.ByteString
takeTail st1 st2 = if B.null st2 then B.empty else if st1 == (nHead (toInteger (B.length st1)) st2) then nTail (toInteger (B.length st1)) st2 else takeTail st1 (B.tail st2)
nTail :: Integer -> B.ByteString -> B.ByteString
nTail n str = let rec n str = if n == 0 then str else rec (n - 1) (B.tail str)
in if (toInteger (B.length str)) < n then str else rec n str
nHead :: Integer -> B.ByteString -> B.ByteString
nHead n str = let rec n str = if n == 0 then B.empty else B.cons (B.head str)(rec (n - 1) (B.tail str))
in if (toInteger (B.length str)) < n then str else rec n str
getValue :: B.ByteString -> B.ByteString -> B.ByteString -> B.ByteString
getValue row field = getCol (read (Bc.unpack field)) . getRow (read (Bc.unpack row))
main = do args <- Bg.getArgs
case (map (B.fromChunks . return) args) of
(path:opt1:opt2:_) -> do src <- B.readFile (Bc.unpack path)
B.putStrLn $ getValue opt1 opt2 src
(path:_) -> do src <- B.readFile (Bc.unpack path)
putStrLn $ show $ length $ Bu.lines src
It doesn't work. I could not debug it. Here is what GHC tells me:
BETA_getlow2.hs:10:23:
Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
In the second argument of `B.index', namely `n'
In the expression: (`B.index` n)
In the expression: (`B.index` n) $ Bu.lines
BETA_getlow2.hs:13:23:
Couldn't match expected type `GHC.Int.Int64' with actual type `Int'
In the second argument of `B.index', namely `n'
In the expression: (`B.index` n)
In the expression: (`B.index` n) $ wordsWithSeparator
Any tips would be appreciated.
getRow n = (!!n) . lines
Compare with
getRow n = B.index . Bu.lines
In the second version you don't use n at all, so it is the same as
getRow _ = B.index . Bu.lines
In the fist example you use n as an argument to the (!!) operator. You need to do the same in the second version.
Looks like it is not the only issue in your code, but I hope it is a good point to start ;)
I'm taking the liberty to interpret the following two sub-questions into your original question.
What Haskell code would one typically write for a script like the one you posted.
What are the right data structures to efficiently perform the desired functionality.
The following code gives one answer to these two sub-questions. It uses the text library to represent sequences of Unicode characters. Moreover, it exploits the text library's high-level API to implement the desired functionality. This makes the code easier to grasp and thereby avoids potential mistakes in the implementation of low-level functions.
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text as T
import qualified Data.Text.IO as T
import System.Environment (getArgs)
type Table a = [[a]]
-- | Split a text value into a text table.
toTable :: T.Text -> Table T.Text
toTable = map (T.splitOn " ; ") . T.lines
-- | Retrieve a cell from a table.
cell :: Int -> Int -> Table a -> a
cell row col = (!! col) . (!! row)
main :: IO ()
main = do
(path:rest) <- getArgs
src <- T.readFile path
case rest of
row : col : _ -> T.putStrLn $ cell (read row) (read col) $ toTable src
_ -> putStrLn $ show $ length $ T.lines src
The first two errors Yuras has resolved for you, I think.
Re the 3rd error:
words' :: B.ByteString -> [B.ByteString]
words' str = if B.null str then B.empty else ...
The B.empty should be []. B.empty :: B.ByteString, but the result is supposed to have type [B.ByteString].
Re the 4th-7th errors:
length :: [a] -> Int
B.length :: B.ByteString -> Int64
In this case I would change the type signatures of nTail and nHead to use Int64 instead of Int. If that didn't work, I'd use Integer on all Integral types, using toInteger to do the conversion.
Re the 8th error:
The input to read must be a String. There's no getting round that. You'll have to convert the B.ByteString to a String and pass that to read.
(Incidently, are you sure you want to switch to ByteString and not Text?)
Re the 9th (final) error:
args :: [Data.ByteString.ByteString] (n.b. a list of strict bytestrings, not the lazy bytestrings you use elsewhere) but in the pattern match you expect args :: B.ByteString for some reason.
You should pattern match on a [ByteString] the same way you pattern match on a [String]: they are both lists.
Convert args to something of type [B.ByteString] with map (B.fromChunks . return) args.

How can I avoid writing boilerplate code for functions performing pattern matching?

In this response to another question, a little Haskell code sketch was given which uses wrapper functions to factor out some code for doing syntax checking on command line arguments. Here's the part of the code which I'm trying to simplify:
takesSingleArg :: (String -> IO ()) -> [String] -> IO ()
takesSingleArg act [arg] = act arg
takesSingleArg _ _ = showUsageMessage
takesTwoArgs :: (String -> String -> IO ()) -> [String] -> IO ()
takesTwoArgs act [arg1, arg2] = act arg1 arg2
takesTwoArgs _ _ = showUsageMessage
Is there a way (maybe using Template Haskell?) to avoid having to write extra functions for each number of arguments? Ideally, I'd like to be able to write something like (I'm making this syntax up)
generateArgumentWrapper<2, showUsageMessage>
And that expands to
\fn args -> case args of
[a, b] -> fn a b
_ -> showUsageMessage
Ideally, I could even have a variable number of arguments to the generateArgumentWrapper meta-function, so that I could do
generateArgumentWrapper<2, asInt, asFilePath, showUsageMessage>
And that expands to
\fn args -> case args of
[a, b] -> fn (asInt a) (asFilePath b)
_ -> showUsageMessage
Is anybody aware of a way to achieve this? It would be a really easy way to bind command line arguments ([String]) to arbitrary functions. Or is there maybe a totally different, better approach?
Haskell has polyvariadic functions. Imagine you had a type like
data Act = Run (String -> Act) | Res (IO ())
with some functions to do what you want
runAct (Run f) x = f x
runAct (Res _) x = error "wrong function type"
takeNargs' 0 (Res b) _ = b
takeNargs' 0 (Run _) _ = error "wrong function type"
takeNargs' n act (x:xs) = takeNargs' (n-1) (runAct act x) xs
takeNargs' _ _ [] = error "not long enough list"
now, all you you need is to marshal functions into this Act type. You need some extensions
{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
and then you can define
class Actable a where
makeAct :: a -> Act
numberOfArgs :: a -> Int
instance Actable (String -> IO ()) where
makeAct f = Run $ Res . f
numberOfArgs _ = 1
instance Actable (b -> c) => Actable (String -> (b -> c)) where
makeAct f = Run $ makeAct . f
numberOfArgs f = 1 + numberOfArgs (f "")
now you can define
takeNArgs n act = takeNargs' n (makeAct act)
which makes it easier to define your original functions
takesSingleArg :: (String -> IO ()) -> [String] -> IO ()
takesSingleArg = takeNArgs 1
takesTwoArgs :: (String -> String -> IO ()) -> [String] -> IO ()
takesTwoArgs = takeNArgs 2
But we can do even better
takeTheRightNumArgs f = takeNArgs (numberOfArgs f) f
Amazingly, this works (GHCI)
*Main> takeTheRightNumArgs putStrLn ["hello","world"]
hello
*Main> takeTheRightNumArgs (\x y -> putStrLn x >> putStrLn y) ["hello","world"]
hello
world
Edit: The code above is much more complicated than it needs to be. Really, all you want is
class TakeArgs a where
takeArgs :: a -> [String] -> IO ()
instance TakeArgs (IO ()) where
takeArgs a _ = a
instance TakeArgs a => TakeArgs (String -> a) where
takeArgs f (x:xs) = takeArgs (f x) xs
takeArgs f [] = error "end of list"
You might want to make use of existing libraries to deal with command line arguments. I believe the de-facto standard right now is cmdargs, but other options exist, such as ReadArgs and console-program.
Combinators are your friend. Try this:
take1 :: [String] -> Maybe String
take1 [x] = Just x
take1 _ = Nothing
take2 :: [String] -> Maybe (String,String)
take2 [x,y] = Just (x,y)
take2 _ = Nothing
take3 :: [String] -> Maybe ((String,String),String)
take3 [x,y,z] = Just ((x,y),z)
take3 _ = Nothing
type ErrorMsg = String
with1 :: (String -> IO ()) -> ErrorMsg -> [String] -> IO ()
with1 f msg = maybe (fail msg) f . take1
with2 :: (String -> String -> IO ()) -> ErrorMsg -> [String] -> IO ()
with2 f msg = maybe (fail msg) (uncurry f) . take2
with3 :: (String -> String -> String -> IO ()) -> ErrorMsg -> [String] -> IO ()
with3 f msg = maybe (fail msg) (uncurry . uncurry $ f) . take3
foo a b c = putStrLn $ a ++ " :: " ++ b ++ " = " ++ c
bar = with3 foo "You must send foo a name, type, definition"
main = do
bar [ "xs", "[Int]", "[1..3]" ]
bar [ "xs", "[Int]", "[1..3]", "What am I doing here?" ]
And if you like overpowered language extensions:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, FlexibleContexts, UndecidableInstances #-}
foo a b c = putStrLn $ a ++ " :: " ++ b ++ " = " ++ c
foo_msg = "You must send foo a name, type, definition"
class ApplyArg a b | a -> b where
appArg :: ErrorMsg -> a -> [String] -> IO b
instance ApplyArg (IO b) b where
appArg _msg todo [] = todo
appArg msg _todo _ = fail msg
instance ApplyArg v q => ApplyArg (String -> v) q where
appArg msg todo (x:xs) = appArg msg (todo x) xs
appArg msg _todo _ = fail msg
quux :: [String] -> IO ()
quux xs = appArg foo_msg foo xs
main = do
quux [ "xs", "[int]", "[1..3]" ]
quux [ "xs", "[int]", "[1..3]", "what am i doing here?" ]

Resources