3

I wrote a function returning the current screen width as IO Integer (working so far).

getScreenWidth:: IO Integer
getScreenWidth = do
                    (sx, sy, w, h) <- getScreenDim 0
                    return $ sx

Now I would like to add the screen width to a string:

> magic_function :: IO Integer -> String -> ... magic output type
> magic_function = ... ? this is where i am stack at ? ...

I would like to pass the magic function to a string, something like "Screen Width: " and i want it to add the current screen width, so that i get "Screen Width: 1680". How can i concat an IO Integer and a common String? Does it work with show?

Could anyone help me with that?

Monkeyphant
  • 105
  • 1
  • 5
  • possible duplicate of [Converting IO Int to Int](http://stackoverflow.com/questions/4235348/converting-io-int-to-int) – shang Apr 24 '13 at 16:30
  • No, its not an duplicate. I dont want to transform an IO type to a non IO Type, I want to concat an IO Int with a String. I know how to concat a string and an integer though.... And sadly the solution mentioned there doesnt work for me. Do I have to translate the String to an IO String maybe? And then concat IO String and IO Integer? – Monkeyphant Apr 24 '13 at 16:34
  • @user2316484 If you know the answer to one, you know the answer to the other. – Carl Apr 24 '13 at 17:05

4 Answers4

5

First, forget about the IO:

labelInteger :: String -> Integer -> String
labelInteger label number = label ++ ": " ++ show number

Now worry about the IO:

import Control.Monad (liftM, liftM2)

labelIOInteger :: String -> IO Integer -> IO String
labelIOInteger label ioNumber = liftM (labelInteger label) ioNumber

Use as e.g. labelIOInteger "Screen Width" getScreenWidth... but beware! If you do something like this:

widthLabel <- labelIOInteger "Screen width" getScreenWidth
isPortrait <- liftM2 (<) getScreenWidth getScreenHeight

...then getScreenWidth will be executed twice... which admittedly for this particular action is unlikely to be a problem, but if it was an action that read an integer from a file or a database or a website, you can see that executing it twice might be undesirable.

It is usually better not to write functions like labelIOInteger, and instead do this:

widthLabel <- liftM (labelInteger "Screen Width") getScreenWidth

...so that if you find yourself needing to use the return value for two different calculations, you can easily refactor to this:

screenWidth <- getScreenWidth
let widthLabel = labelInteger "Screen Width" screenWidth
isPortrait <- liftM (screenWidth <) getScreenHeight
dave4420
  • 46,404
  • 6
  • 118
  • 152
2
magic_function :: IO Integer -> String -> IO String
magic_function num msg = do
                            n <- num
                            return (msg ++ (show n))
Ankur
  • 33,367
  • 2
  • 46
  • 72
2

The suggested duplicate does actually give the answer you need, but I suppose if it was obvious how you wouldn't be asking this in the first place. :]

In general, you can't do anything directly with a value whose type is IO something. This type represents not a value of type something wrapped in IO, but rather a procedure possibly using IO that produces a value of type something when executed. It may be different "something"s if you use the IO something value more than once, after all.

So in your case, you can't simply concatenate the String on; you have to define a new IO procedure that executes the other one, then concatenates the value it produces. The general form will look something like this:

someFunction :: a -> b -> c
someFunction a b = ...

someProcedure :: IO a -> b -> IO c
someProcedure a b = do aValue <- a -- this executes "a"
                       return $ someFunction aValue b

There are shorter ways to write it, but I thought it would help to spell it out. The specifics will depend on what you're actually doing, of course--for your program, you can use show to convert the integer to a String, then concatenate the strings like usual.

Vitus
  • 11,822
  • 7
  • 37
  • 64
C. A. McCann
  • 76,893
  • 19
  • 209
  • 302
1

I think you want this:

magic :: IO Integer -> String -> IO String
magic ios s =
  do i <- ios
     return $ s ++ ": " ++ (show i)
ErikR
  • 51,541
  • 9
  • 73
  • 124