So I wrote a program to query a forex API (foreign exchange), and it works like a charm, but when I want to query every currency pair available, it evaluates all API calls as it takes a long time to execute but prints nothing.
import Data.Functor ((<&>))
supportedPairs :: IO (Maybe [(String, String)])
forex :: String -> String -> IO (Maybe (Scientific, UnixTime))
main :: IO ()
main = do
x <- supportedPairs
mapM_ (flip (<&>) print . uncurry forex) (fromJust x)
-- this prints nothing at all
The single calls work just fine like this:
main = do
x <- supportedPairs
u <- (uncurry forex . (flip (!!) 10 . fromJust)) x
print u
-- this prints "Just (438.685041,UnixTime {utSeconds = 1649588583, utMicroSeconds = 0})"
Why doesn't the mapM_
print the results although they are evaluated? If I understood Haskell's laziness correctly, then if the results are not to be printed they should not be evaluated in the first place?
CodePudding user response:
Check the types:
print
is ... -> IO ()
.
Therefore, ... <&> print
is IO (IO ())
. Note the double IO here.
Hence, mapping over that, will run the "outermost IO" but not the "innermost IO". More concretely, compare this:
main = do
x <- print True >> return 5 -- x is 5
y <- return (print True >> return 5) -- y is an IO action
...
Only the first print True
here is executed: the second IO action is used to define y
but until we run y
it won't be executed.
The final point: here, you do not need <&>
since that creates the nested IO's. Use flip (>>=) print
(or (=<<) print
, or (>>= print)
) instead of flip <&> print
.