5

Here is my function:

foo :: Int -> Int
foo i = do
    putStrLn "Foo Function has run!"
    return (i*2)

The above has a compile time error.

I want it to be the case that if I did this:

bar = foo 6

bar would be 12.

but when the function is first run, it prints out the string "Foo Function has run!" How can i do this?

0

2 Answers 2

12

You have to make the function into an IO action. Haskell strictly enforces that you separate code that does IO from code that doesn't:

foo :: Int -> IO Int
foo i = do
    putStrLn "Foo Function has run!"
    return (i * 2)

You could then extract this value inside another IO function:

main :: IO ()
main = do
    bar1 <- foo 6
    bar2 <- foo bar1
    print (bar1, bar2)

The reasons why Haskell chooses this route are many and varied, but some of the important ones (in no particular order) are:

  • Testability. Any pure function is much easier to test because it always only depends on its inputs, not on the state of the application
  • Equational reasoning. This means that you can reason about your code as if it were just fancy mathematical functions. You can shuffle around your code much easier.
  • Compiler optimizations. The compiler is free to choose when and how to call your function if it has no side effects, it can defer the call until later, and it can make very tight inner loops in the compiled code that run much faster.
  • Composability. Haskell type system is very rich, and segregating types in this fashion actually allows you to write more abstractions than you might be able to in C#, Java, or Python.
  • Code clarity. If you see a function just returns a value and not an IO action, you immediately know a lot about that function. That type signature is a promise that you don't have to worry as much that the function might break your application. On the other hand if it does return an IO action, this signifies that you have to be more careful.
Sign up to request clarification or add additional context in comments.

10 Comments

Thats amazing but what if I didn't use Int but instead M.Map String Int. I tried "IO M.Map String Int" and "IO (M.Map String Int)" but both produce errors
@YahyaUddin What errors do they produce? The second example you posted above should work just fine. The return function is polymorphic in its argument and its return type, it will work with any type you give it.
@YahyaUddin The first example you posted above won't work because IO M.Map String Int is not a valid type, the IO type constructor only accepts one argument, while M.Map accepts two. This is very much like function application where g f a b is very different from g (f a b). The form IO (M.Map String Int) is the correct one to use.
Thanks. I made a further mistake on another part hence the bracketed version did not work.
In particular, you could write a function foo :: a -> IO a; foo a = do { putStrLn "Foo has been run"; return a } that would work for any type you pass into it. This function isn't particularly useful, but it's certainly possible.
|
0

Make it easy on yourself, use trace :: String -> a -> a from Debug.Trace

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.