I am learning about the filterM function in the book "Learn You a Haskell for Great Good!" by Miran Lipovaca. For the following example:
keepSmall :: Int -> Writer [String] Bool
keepSmall x
| x < 4 = do
tell ["Keeping " ++ show x]
return True
| otherwise = do
tell [show x ++ " is too large, throwing it away"]
return False
The result obtained from using this function with filterM is the following:
> runWriter $ filterM keepSmall [9,1,5,2,10,3]
([1,2,3],["9 is too large, throwing it away","Keeping 1","5 is too large,
throwing it away","Keeping 2","10 is too large, throwing it away","Keeping 3"])
Regarding the type of the result of filterM, I know that filterM has the following type declaration:
filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
Since the monad used for this example is Writer [String], would the type of the list resulting from filterM be Writer [String] [Int]? If this is the case, is this why the result type is ([Int], [String]), since Writer w a is equivalent to the tuple (a,w)?
That's because of the type of runWriter
runWriter :: Writer w a -> (a, w)
from Hoogle, it literally just unwrap a writer computation as a (result, output) pair. That's why you got the result in a pair.
A little example, just to see how it works in other context:
runWriter (tell $ return "Hello" ())
=> ((),"Hello")
Related
Within this block of code here, I have a function tagsort that takes a FilePath and returns an IO String.
builddir xs = do
writeto <- lastest getArgs
let folderl b = searchable <$> (getPermissions b)
let filel c = ((lastlookup mlookup c) &&) <$> ((not <$> folderl c))
a <- listDirectory xs
listdirs <- filterM (folderl) (map ((xs ++ "/") ++) a)
filedirs <- filterM (filel) (map ((xs ++ "/") ++) a)
tagfiles <- tagsort <$> filedirs
--testprint to terminal
putStrLn $ concat listdirs
putStrLn $ concat tagfiles
tagsort :: Control.Monad.IO.Class.MonadIO m => FilePath -> m [Char]
tagsort xs = do
nsartist <- getTags xs artistGetter
nsalbum <- getTags xs albumGetter
let artist = init $ drop 8 $ show nsartist
let album = init $ drop 7 $ show nsalbum
pure (artist ++ " - " ++ album)
I'd like to take this function and map it over a list of directories. When run, I get this error.
• Couldn't match type ‘[]’ with ‘IO’
Expected type: IO (t0 [Char])
Actual type: [t0 [Char]]
• In a stmt of a 'do' block: tagfiles <- tagsort <$> filedirs
I believe I understand what is happening here. In order to bind in the way I desire to tagfiles, I would want an IO [String], but instead mapping tagsort to the list filedirs produces [IO String]. I am not totally sure how to circumvent this or if it is even able to be circumvented at all. Perhaps mapping is not the correct way to do this? Any help would be appreciated.
This is because the function tagsort is of type String -> IO String
Note: I am using IO for simplicity and String for both [Char] and FilePath.
However, when mapping it onto filedir :: [String] using
(<$>) = fmap :: Functor f => (a -> b) -> f a -> f b
a conflict occured between IO [String] and `[IO String] - the former is what the compiler expects an expression in a do block to be.
This isn't very useful on first glance. However, haskell has a function called sequence for this exact task. Its constraints needn't matter much now, as Foldable is something completely different. For now, know that it can be of type [IO a] -> IO [a]
Luckily again, there's a very useful predefined utility function, mapM just for sequence . map f.
The final code will be:
mapM tagSort fileDirs
I try to run snippets from chapter 8 about functional parsers in Graham Hutton's 'Programming in Haskell' both in ghci and frege-repl.
I'm not able to sequence parsers using do syntax.
I have following definitions in Frege (Haskell version differs only with simpler item definition that doesn't pack and unpack String and Char and is the same as in the book):
module Parser where
type Parser a = String -> [(a, String)]
return :: a -> Parser a
return v = \inp -> [(v, inp)]
-- this is Frege version
item :: Parser Char
item = \inp ->
let inp' = unpacked inp
in
case inp' of
[] -> []
(x:xs) -> [(x,packed xs)]
parse :: Parser a -> String -> [(a, String)]
parse p inp = p inp
-- sequencing
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
p >>= f = \inp -> case (parse p inp) of
[] -> []
[(v,out)] -> parse (f v) out
p :: Parser (Char, Char)
p = do x <- Parser.item
Parser.item
y <- Parser.item
Parser.return (x,y)
-- this works
p' :: Parser (Char, Char)
p' = item Parser.>>= \x ->
item Parser.>>= \_ ->
item Parser.>>= \y ->
Parser.return (x,y)
p' works both in ghci and frege-repl. However, when trying loading module I got those messages. First from ghci:
src/Parser.hs:38:8:
Couldn't match type ‘[(Char, String)]’ with ‘Char’
Expected type: String -> [((Char, Char), String)]
Actual type: Parser ([(Char, String)], [(Char, String)])
In a stmt of a 'do' block: Parser.return (x, y)
In the expression:
do { x <- item;
item;
y <- item;
Parser.return (x, y) }
Failed, modules loaded: none.
frege-repl is even less friendly because it simply kicks me out from repl with an error stack trace:
Exception in thread "main" frege.runtime.Undefined: returnTypeN: too many arguments
at frege.prelude.PreludeBase.error(PreludeBase.java:18011)
at frege.compiler.Utilities.returnTypeN(Utilities.java:1937)
at frege.compiler.Utilities.returnTypeN(Utilities.java:1928)
at frege.compiler.GenJava7$80.eval(GenJava7.java:11387)
at frege.compiler.GenJava7$80.eval(GenJava7.java:11327)
at frege.runtime.Fun1$1.eval(Fun1.java:63)
at frege.runtime.Delayed.call(Delayed.java:198)
at frege.runtime.Delayed.forced(Delayed.java:267)
at frege.compiler.GenJava7$78.eval(GenJava7.java:11275)
at frege.compiler.GenJava7$78.eval(GenJava7.java:11272)
at frege.runtime.Fun1$1.eval(Fun1.java:63)
at frege.runtime.Delayed.call(Delayed.java:200)
at frege.runtime.Delayed.forced(Delayed.java:267)
at frege.control.monad.State$IMonad_State$4.eval(State.java:1900)
at frege.control.monad.State$IMonad_State$4.eval(State.java:1897)
at frege.runtime.Fun1$1.eval(Fun1.java:63)
at frege.runtime.Delayed.call(Delayed.java:198)
at frege.runtime.Delayed.forced(Delayed.java:267)
at frege.control.monad.State$IMonad_State$4.eval
...
My intuition is that I need something apart >>= and return or there is something I should tell compilers. Or maybe I need to put p definition into State monad?
This is because String -> a is the monad that is being used in your do notation, since one of the instances of Monad in the Prelude is the function arrow.
Therefore, for example, the x in x <- Parser.item is an argument of type [(Char, String)].
You can get around this by making Parser a newtype and defining your own custom Monad instance for it.
The following works with Frege (and should work the same way with GHC language extension RebindableSyntax):
module P
where
type Parser a = String -> [(a, String)]
return :: a -> Parser a
return v = \inp -> [(v, inp)]
-- this is Frege version
item :: Parser Char
item = maybeToList . uncons
parse :: Parser a -> String -> [(a, String)]
parse p inp = p inp
-- sequencing
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
p >>= f = \inp -> case (parse p inp) of
[] -> []
[(v,out)] -> parse (f v) out
p :: Parser (Char, Char)
p = do
x <- item
item
y <- item
return (x,y)
main = println (p "Frege is cool")
It prints:
[(('F', 'r'), "ege is cool")]
The main difference to your version is a more efficient item function, but, as I said before, this is not the reason for the stack trace. And there was this small indentation problem with the do in your code.
So yes, you can use the do notation here, though some would call it "abuse".
I'm trying to get the code below working. It's a finite state machine where I pass in a function-as-next-state. The function is called with r and returns a list of results + the next function-as-next state. Keep on calling until the list runs out, and return the concatenation of the results. The monad is an error monad to allow me to throw an error if needed.
fsm f [] = return []
fsm f (r:rs) = do
(xs, f') <- f r
rest <- fsm f' rs
return $ xs ++ rest
The error is:
Occurs check: cannot construct the infinite type: t1 = t0 -> m0 ([a0], t1)
In the first argument of `fsm', namely f'
I've seen the infinite type error before and I understand the way around it is to wrap a type with a newtype. But I cannot figure out how to get this done.
Can someone point out the insight?
I think this is what you want:
newtype F m = F { unF :: String -> m ([String], F m) }
fsm :: (Monad m) => F m -> [String] -> m [String]
fsm f [] = return []
fsm f (r:rs) = do
(xs, f') <- unF f r
rest <- fsm f' rs
return $ xs ++ rest
You are right that you need to use a data or newtype any time you want a recursive type.
In reply to your comment, here's how you would implement your dup function:
dup :: (Monad m) => F m
dup = F dup' where dup' xs = return ([xs, xs], F dup')
... or you could split it up into two separate definitions if you prefer.
Note that if you are not sure what the type signature should be, just enable the NoMonomorphismRestriction extension and the compiler will not complain and correctly infer the top-level type for you.
my goal is to write Haskell function which reads N lines from input and joins them in one string. Below is the first attempt:
readNLines :: Int -> IO String
readNLines n = do
let rows = replicate n getLine
let rowsAsString = foldl ++ [] rows
return rowsAsString
Here haskell complaints on foldl:
Couldn't match expected type [a]'
against inferred type(a1 -> b -> a1)
-> a1 -> [b] -> a1'
As I understand type of rows is [IO String], is it possible some how join such list in a single IO String?
You're looking for sequence :: (Monad m) => [m a] -> m [a].
(Plus liftM :: Monad m => (a1 -> r) -> m a1 -> m r and unlines :: [String] -> String, probably.)
Besides what ephemient points out, I think you have a syntax issue: The way you're using the ++ operator makes it look like you are trying to invoke the ++ operator with operands foldl and []. Put the ++ operator in parentheses to make your intent clear:
foldl (++) [] rows
The functions you are looking for is is sequence, however it should be noted that
sequence (replicate n f)
is the same as
replicateM n f
And foldl (++) [] is equivalent to concat. So your function is:
readNLines n = liftM concat (replicateM n getLine)
Alternatively if you want to preserve line breaks:
readNLines n = liftM unlines (replicateM n getLine)
The shortest answer I can come up with is:
import Control.Applicative
import Control.Monad
readNLines :: Int -> IO String
readNLines n = concat <$> replicateM n getLine
replicate returns a list of IO String actions. In order to perform these actions, they need to be run in the IO monad. So you don't want to join an array of IO actions, but rather run them all in sequence and return the result.
Here's what I would do
readNLines :: Int -> IO String
readNLines n = do
lines <- replicateM n getLine
return $ concat lines
Or, in applicative style:
import Control.Applicative
readNLines :: Int -> IO String
readNLines n = concat <$> replicateM n getLine
Both of these use the monadic replicate (replicateM), which evaluates a list of monadic values in sequence, rather than simply returning a list of actions
I'm new to haskell, and i read through and digested Learn You A Haskell For Great Good, trying out a couple of things along the way. For my first project i wanted to try the classic: FizzBuzz. So i came up with the following code:
import System.IO
fizzBuzz :: (Integral a) => a -> String
fizzBuzz num
| fizz && buzz = "FizzBuzz"
| fizz = "Fizz"
| buzz = "Buzz"
| otherwise = show num
where fizz = num `mod` 3 == 0
buzz = num `mod` 5 == 0
main = print $ map fizzBuzz [1..100]
Worked great, except i got a rather dense looking list that was hard to read. So i tried this main function instead:
main = map putStrLn $ map fizzBuzz [1..100]
And that gives me the error Couldn't match expected type 'IO t' against inferred type '[IO ()]'. I tried half a dozen things and none of it seemed to help. What's the proper way to do what i'm trying to do?
map :: (a -> b) -> [a] -> [b]
putStrLn :: Show a => a -> IO ()
map putStrLn :: Show a => [a] -> [IO ()]
You've got a list of IO () actions.
main :: IO ()
You need to join them into a single IO () action.
What you want to do is to perform each of those IO () actions in sequence/sequence_:
sequence :: Monad m => [m a] -> m [a]
sequence_ :: Monad m => [m a] -> m ()
For convenience, mapM/mapM_ will map a function over a list and sequence the resulting monadic results.
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
So your fixed code would look like this:
main = mapM_ putStrLn $ map fizzBuzz [1..100]
Although I'd probably write it like this:
main = mapM_ (putStrLn . fizzBuzz) [1..100]
Or even this:
main = putStr $ unlines $ map fizzBuzz [1..100]
Let's write our own sequence. What do we want it to do?
sequence [] = return []
sequence (m:ms) = do
x <- m
xs <- sequence ms
return $ x:xs
If there's nothing left in the list, return (inject into the monad) an empty list of results.
Otherwise, within the monad,
Bind (for the IO monad, this means execute) the first result.
sequence the rest of the list; bind that list of results.
Return a cons of the first result and the list of other results.
GHC's library uses something more like foldr (liftM2 (:)) (return []) but that's harder to explain to a newcomer; for now, just take my word that they're equivalent.
sequence_ is easier, since it doesn't bother keeping track of the results. GHC's library implements it as sequence_ ms = foldr (>>) (return ()) ms. Let's just expand the definition of foldr:
sequence [a, b, c, d]
= foldr (>>) (return ()) [a, b, c, d]
= a >> (b >> (c >> (d >> return ())))
In other words, "do a, discard the result; do b; discard the result, … finally, return ()".
mapM f xs = sequence $ map f xs
mapM_ f xs = sequence_ $ map f xs
On the other hand, you don't even need to know monads at all with the alternate unlines solution.
What does unlines do? Well, lines "a\nb\nc\nd\n" = ["a", "b", "c", "d"], so of course unlines ["a", "b", "c", "d"] = "a\nb\nc\nd\n".
unlines $ map fizzBuzz [1..100] = unlines ["1", "2", "Fizz", ..] = "1\n2\nFizz\n..." and off it goes to putStr. Thanks to the magic of Haskell's laziness, the full string never needs to be constructed in memory, so this will happily go to [1..1000000] or higher :)