Evaluating Null Functions in Haskell

Suppose you have a null function in haskell that is used several times in code. Is it always evaluated only once? I have already tested the following code:

sayHello :: Int sayHello = unsafePerformIO $ do putStr "Hello" return 42 test :: Int -> [Int] test 0 = [] test n = (sayHello:(test (n-1))) 

When I call test 10, it writes "Hello" ony once, so it indicates that the result of the function is saved after the first evaluation. My question is, is it guaranteed? Will I get the same result for different compilers?

Edit The reason I used unsafePerformIO was to check if sayHello was checked more than once. I do not use this in my program. I usually expect sayHello to have exactly the same result every time I evaluate it. But it was a time-consuming operation, so I wanted to know if it was possible to access this path or if it should be passed as an argument, if it required that it not be evaluated several times, that is:

 test _ 0 = [] test sn = (s:(test (n-1))) ... test sayHello 10 

According to the answers, this should be used.

+7
source share
3 answers

There is no such thing as a null function. A function in Haskell has exactly one argument and is always of type ... -> ... sayHello - value - a Int - but not a function. See this article for more details.

Warranty: No, you do not have any warranty. The Haskell report states that Haskell is not strict - so you know what value will ultimately lead to a decrease, but not to any specific evaluation strategy. The GHC pricing strategy usually uses lazy pricing , that is, a lax pricing with sharing, but it doesn’t give any serious guarantees about this - the optimizer can shuffle your code so that things are priced more than once.

There are also various exceptions - for example, foo :: Num a => a is polymorphic, so it probably won't be shared (it is compiled for the actual function). Sometimes a pure value can be evaluated by more than one thread at a time (this will not happen in this case, because unsafePerformIO explicitly uses noDuplicate to avoid this). Therefore, when you program, you can usually expect laziness, but if you need any guarantees, you need to be very careful. The report itself will not give you anything about how your program is rated.

unsafePerformIO gives you even less options for guarantees, of course. There is a reason that he called "unsafe."

+17
source

Functions without a top-level argument, such as sayHello , are called constant applicative forms and are always remembered (at least in the GHC - see http://www.haskell.org/ghc/docs/7.2.1/html/users_guide/profiling. html ). You will have to resort to tricks, for example, passing dummy arguments and disabling optimization so as not to share CAF globally.

Edit: quote from the link above -

Haskell is a lazy language, and some expressions are only ever evaluated once. For example, if we write: x = nfib 25 , then x will be evaluated only once (if at all) and subsequent requirements for x will immediately see the result of caching. The definition of x is called CAF (constant applicative form) because it has no arguments.

+5
source

If you want Hello to be printed n times, you need to remove unsafePermformIO , so the runtime will know that it cannot optimize putStr repeated calls. I do not know if you want to return an int list, so I wrote two versions of the test, one of which returns (), one [Int].

 sayHello2 :: IO Int sayHello2 = do putStr "Hello" return 42 test2 :: Int -> IO () test2 0 = return () test2 n = do sayHello2 test2 (n-1) test3 :: Int -> IO [Int] test3 0 = return [] test3 n = do r <- sayHello2 l <- test3 (n-1) return $ r:l 
+1
source

All Articles