I have several data types in an IO context like:
a :: IO String
b :: IO FilePath
c :: String -> IO String
I want to put them all together in one data object like:
data Configdata = Configdata String FilePath (String -> String)
So I don't have to get each value for its own out of the IO context, but just out of IO Configdata.
The critical point where I don't have a solution is how I can transform String -> IO String to IO (String -> String).
Hoogle doesn't give me any functions which are capable of doing this.
I am not sure if it's maybe even not possible, since the input of the function is possibly infinite.
Does someone have a solution or an explanation why it's not possible?
I know that using a list instead of a function is an option, but I would prefer using a function if possible.
Indeed this is not possible. Consider the function:
import Acme.Missiles
boo :: String -> IO String
boo "cute" = return "Who's a nice kitty?"
boo "evil" = launchMissiles >> return "HTML tags lea͠ki̧n͘g fr̶ǫm ̡yo͟ur eye͢s̸ ̛l̕ik͏e liquid pain"
Now, if it were possible to transform this to IO (String -> String), it would have to execute all possible IO actions for any input before returning the pure String -> String function. IOW, even if you only planned to use the function for kitten-watching purposes, it would entail nuclear holocaust.
Nevertheless, it may well be possible to do this for your specific application. In particular, if you know the function will only ever be called for a predetermined set of strings, you can pre-query them from IO and store the results in a map, which can then be indexed purely.
import qualified Data.Map as Map
puh :: IO (String -> String)
puh = fmap ((Map.!) . Map.fromList) . forM ["cute"] $ \q -> do
res <- boo q
return (q, res)
Of course, this may not be feasible performance-wise.
Related
I wrote a bunch of code in Haskell to create an index of a text. The top function looks like this:
index :: String -> [(String, [Integer])]
index a = [...]
Now I want to give this function a String read from a file:
index readFile "input.txt"
Which won't work because readFile is of type FilePath -> IO String.
Couldn't match expected type 'String'
against inferred type 'IO String'
I see the error, but I can't find any function with type:
IO String -> String
I guess the key to success lies somewhere under some Monads, but I could not find a way to solve my problem.
You can easily enough write a function that calls the readFile action, and passes the result to your index function.
readAndIndex fileName = do
text <- readFile fileName
return $ index text
However, the IO monad taints everything that uses it, so this function has the type:
readAndIndex :: FilePath -> IO [(String, [Integer])]
There is a very good reason why there is no such function.
Haskell has the notion of functional purity. This means that a function will always return the same result when called with the same parameters. The only place where IO is allowed is inside the IO monad.
If there was* a function
index :: IO String -> String
then we could suddenly do IO actions anywhere by calling, for example:
index (launchMissiles >> deleteRoot >> return "PWNd!")
Functional purity is a very useful feature that we don't want to lose, since it allows the compiler to reorder and inline functions much more freely, they can be sparked off to different cores without changing the semantics and it also gives the programmers a sense of security since if you can know what a function can and can't do from it's type.
* Actually there is such a function. It's called unsafePerformIO and it's called that for very, very good reasons. Do not use it unless you're 100% sure of what you are doing!
Well you cannot get rid of the IO monad part of IO String. That means you will have to make your function return IO [(String, [Integer])].
I recommend learning more about monads, but for now you can get away with the liftM function:
liftM index (readFile "input.txt")
liftM has this signature:
liftM :: Monad m => (a -> b) -> m a -> m b
It takes a non-monadic function and transforms it into a monadic function.
fmap index $ readFile "input.txt"
or
readFile "input.txt" >>= return . index
You may want to look into monad and functors
I have a function that returns the type IO Bool. I'd like to use this function as an argument to filterM, but what I actually want to do is to invert its output. I've tried something to the effect of (not . f), but not isn't hip to the IO vibe. How can I invert an IO Bool?
Here's a minimal working example:
#!/usr/bin/env runhaskell
{-# LANGUAGE UnicodeSyntax #-}
module Main where
import Prelude.Unicode
userEnteredStr ∷ String → IO Bool
userEnteredStr str = do
input ← getLine
return (input ≡ str)
-- doesn't work. How would I write this function?
--userDidntEnterStr ∷ String → IO Bool
--userDidntEnterStr str = not . userEnteredStr
main = do result ← userEnteredStr "y"
print result
Sorry if this is basic! I can't find a function on Hoogle with type IO Bool -> IO Bool and haven't found anything in my web searching.
For the record, "doesn't work" is not a very helpful error description :) Is it a syntax error? a type error? does it compile and typecheck, but return the wrong value? It's probably the most vague description of your problem possible...and is usually a really really big impairment/hurdle for anyone who wants to help you.
The main problem here is that you can't apply not to an IO Bool, because not only works on Bools. An IO Bool is not a Bool, nor does it "contain a Bool", so it's not surprising that it doesn't work. It's like trying to apply (* 2) to your dog. Your dog isn't a number!
But it seems like you know how to work with do notation and binding from IO, so maybe you can understand why this would work?
userDidntEnterStr :: String -> IO Bool
userDidntEnterStr str = do
didEnter <- userEnteredStr str
return (not didEnter)
Alternatively, you can also apply any (a -> b) to the result of an IO a to get a new IO b using fmap:
userDidntEnterStr :: String -> IO Bool
userDidntEnterStr str = fmap not (userEnteredStr str)
I have this very simple function
import qualified Data.ByteString.Lazy as B
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
readJFile :: IO (Maybe Response)
readJFile = parsing >>= (\d ->
case d of
Left err -> return Nothing
Right ps -> return (Just ps))
where parsing = fmap eitherDecode getJson :: IO (Either String Response)
where jsonFile is a path to a file on my harddrive (pardon the lack of do-notation, but I found this more clear to work with)
my question is; is there a way for me to ditch the IO part so I can work with the bytestring alone?
I know that you can pattern match on certain monads like Either and Maybe to get their values out, but can you do something similar with IO?
Or voiced differently: is there a way for me to make readJFile return Maybe Response without the IO?
To expand on my comments, here's how you can do it:
getJson :: IO B.ByteString
getJson = B.readFile jsonFile -- as before
readJFile :: B.ByteString -> Maybe Response -- look, no IO
readJFile b = case eitherDecode b of
Left err -> Nothing
Right ps -> Just ps
In the end, you combine everything in one IO action again:
getAndProcess :: IO (Maybe Response)
getAndProcess = do
b <- getJson
return (readJFile b)
You never need to "drag a monad" through any functions, unless they all need to actually do IO. Just lift the entire chain into the monad with fmap (or liftM / liftM2 / ...).
For instance,
f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M
and your entire thing is supposed to be like
m :: M
m = let k = "f1 getJson"
in f4 (f2 k) (f3 k)
The you can simply do
m = fmap (\b -> let k = f1 b
in f4 (f2 k) (f3 k) )
getJson
Incidentally, this might look nicer with do notation:
m = do
b <- getJson
return $ let k = f1 b
in f4 (f2 k) (f3 k)
Concerning you edit and the question
is there a way for me to make readJFile return Maybe Response without the IO?
No, that can't possibly work, because readJFile does need to do IO. There's no way escaping from the IO monad then, that's the whole point of it! (Well, there is unsafePerformIO as Ricardo says, but this is definitely not a valid application for it.)
If it's the clunkiness of unpacking Maybe values in the IO monad, and the signatures with parens in them, you may want to looks at the MaybeT transformer.
readJFile' :: MaybeT IO Response
readJFile' = do
b <- liftIO getJson
case eitherDecode b of
Left err -> mzero
Right ps -> return ps
No, there is no safe way to get a value out of the IO monad. Instead you should do the work inside the IO monad by applying functions with fmap or bind (>>=). Also you should use decode instead of eitherDecode when you want your result to be in Maybe.
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode
readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON
You could also use do notation if that is clearer to you:
readJFile :: IO (Maybe Response)
readJFile = do
bytestring <- getJson
return $ decode bytestring
Note that you dont even need the parseResponse function since readJFile specifies the type.
In general, yes, there is a way. Accompanied by a lot of "but", but there is. You're asking for what it's called an unsafe IO operation: System.IO.Unsafe. It's used to write wrappers when calling to external libraries usually, it's not something to resort to in regular Haskell code.
Basically, you can call unsafePerformIO :: IO a -> a which does exactly what you want, it strips out the IO part and gives you back wrapped value of type a. But, if you look at the documentation, there are a number of requirements which you should guarantee yourself to the system, which all end up in the same idea: even though you performed the operation via IO, the answer should be the result of a function, as expected from any other haskell function which does not operate in IO: it should always have the same result without side effects, only based on the input values.
Here, given your code, this is obviously NOT the case, since you're reading from a file. You should just continue working within the IO monad, by calling your readJFile from within another function with result type IO something. Then, you'll be able to read the value within the IO wrapper (being in IO yourself), work on it, and then re-wrap the result in another IO when returning.
This is my first Haskell attempt so my understanding is fairly limited. I wanted to write a very basic program to check if a file contains the word ERROR. I came up with the following which of course doesn't even compile.
import Text.Regex.Posix
containsErrorString :: String -> Bool
containsErrorString x = x =~ "ERROR" :: Bool
fileContainsErrorString fileName = do
s <- readFile fileName
containsErrorString s
Is this the right approach and if not what would be the correct way to accomplish this?
Thanks
A light rewrite
I suggest:
Use Text instead of String for any "serious" work.
If you don't need regex, don't use regex.
In code:
import Data.Text as T
containsErrorString :: Text -> Bool
containsErrorString = ("ERROR" `T.isInfixOf`)
fileContainsErrorString :: FilePath -> IO Bool
fileContainsErrorString = containsErrorString `fmap` T.readFile
-- Warning, code typed and not tested.
Your Code
There isn't anything wrong with your approach. The use of String remains common and perfectly acceptable for most uses. Your error is just that you forgot to return the result (you are in a monad, IO, and containsErrorString is a pure function).
Instead of:
containsErrorString s
you should have had:
return (containsErrorString s)
Main problem is in fact that fileContainsErrorString :: IO Bool,
but containsErrorString (s:: String) :: Bool.
So, you need to wrap containsErrorString into IO, for example use return :: Monad m => a -> m a
fileContainsErrorString fileName = do
s <- readFile fileName
return $ containsErrorString s
I wrote a bunch of code in Haskell to create an index of a text. The top function looks like this:
index :: String -> [(String, [Integer])]
index a = [...]
Now I want to give this function a String read from a file:
index readFile "input.txt"
Which won't work because readFile is of type FilePath -> IO String.
Couldn't match expected type 'String'
against inferred type 'IO String'
I see the error, but I can't find any function with type:
IO String -> String
I guess the key to success lies somewhere under some Monads, but I could not find a way to solve my problem.
You can easily enough write a function that calls the readFile action, and passes the result to your index function.
readAndIndex fileName = do
text <- readFile fileName
return $ index text
However, the IO monad taints everything that uses it, so this function has the type:
readAndIndex :: FilePath -> IO [(String, [Integer])]
There is a very good reason why there is no such function.
Haskell has the notion of functional purity. This means that a function will always return the same result when called with the same parameters. The only place where IO is allowed is inside the IO monad.
If there was* a function
index :: IO String -> String
then we could suddenly do IO actions anywhere by calling, for example:
index (launchMissiles >> deleteRoot >> return "PWNd!")
Functional purity is a very useful feature that we don't want to lose, since it allows the compiler to reorder and inline functions much more freely, they can be sparked off to different cores without changing the semantics and it also gives the programmers a sense of security since if you can know what a function can and can't do from it's type.
* Actually there is such a function. It's called unsafePerformIO and it's called that for very, very good reasons. Do not use it unless you're 100% sure of what you are doing!
Well you cannot get rid of the IO monad part of IO String. That means you will have to make your function return IO [(String, [Integer])].
I recommend learning more about monads, but for now you can get away with the liftM function:
liftM index (readFile "input.txt")
liftM has this signature:
liftM :: Monad m => (a -> b) -> m a -> m b
It takes a non-monadic function and transforms it into a monadic function.
fmap index $ readFile "input.txt"
or
readFile "input.txt" >>= return . index
You may want to look into monad and functors