Another question about unsafePerformIO

I have a program that optimizes train schedules. It employs an external solver for Integer Linear Programs. The solve function has the following type:
solve :: Constraints -> IO (Maybe Solution)
And this works. However, my external solver also behaves like a pure function from input to output. I wonder whether this guarantee should be reflected in the type system. I'd also appreciate if the compiler would be able to eliminate some calls to the solver.
solvePure :: Constraints -> Maybe Solution solvePure = unsafePerformIO . solve
Is this a good idea?

Matthias Görgens wrote:
I have a program that optimizes train schedules. It employs an external solver for Integer Linear Programs. The solve function has the following type:
solve :: Constraints -> IO (Maybe Solution)
And this works. However, my external solver also behaves like a pure function from input to output. I wonder whether this guarantee should be reflected in the type system. I'd also appreciate if the compiler would be able to eliminate some calls to the solver.
solvePure :: Constraints -> Maybe Solution solvePure = unsafePerformIO . solve
Is this a good idea?
This is safe as long as there are no side effects, and not depend on its environment (e.g. don't open files, read from environment variables). In general, functions that do not IO should not have IO in their type signature, because this will contaminate calling functions unnecessarily. Adding 'unsafePerformIO' will work, but a better idea might be to understand why your solver has IO in its type signature. Is this because of FFI calls? You can remove IO in FFI calls if they are free from side effects as well. Regards, -- Jochem Berndsen | jochem@functor.nl GPG: 0xE6FABFAB

On Thu, Jun 25, 2009 at 03:38:41PM +0200, Matthias Görgens wrote:
I have a program that optimizes train schedules. It employs an external solver for Integer Linear Programs. The solve function has the following type:
solve :: Constraints -> IO (Maybe Solution)
And this works. However, my external solver also behaves like a pure function from input to output. I wonder whether this guarantee should be reflected in the type system. I'd also appreciate if the compiler would be able to eliminate some calls to the solver.
solvePure :: Constraints -> Maybe Solution solvePure = unsafePerformIO . solve
Is this a good idea?
If it is actually fully pure, including things like debugging output, then this is fine. However, if the algorithm takes a signifigant amount of time or resources, you may want to keep it in IO just so users can have control over exactly when and how often it is run. unsafePerformIO was included in the FFI spec for just this sort of case. John -- John Meacham - ⑆repetae.net⑆john⑈ - http://notanumber.net/

On Thu, Jun 25, 2009 at 3:49 PM, John Meacham
However, if the algorithm takes a signifigant amount of time or resources, you may want to keep it in IO just so users can have control over exactly when and how often it is run.
If you had a pure function written in Haskell that used a lot of time and memory, would you put it in IO just so users can have control over exactly when and how often it is run? bigFatFunction :: Foo -> IO Bar bigFatFunction x = return . bigFatFunction $ x If the function really does use a lot of time and memory and your compiler is duplicating calls to it, then you have a pessimizing compiler and should file a bug. --Max

Adding 'unsafePerformIO' will work, but a better idea might be to understand why your solver has IO in its type signature. Is this because of FFI calls? You can remove IO in FFI calls if they are free from side effects as well.
My solver has IO in the type signature, because I said so. :o) The solve function is defined like this:
solve :: Constraints -> IOMayfail Solution solve constraints = do { solString <- scip (genZimpl constraints); parseSol nfnrs solString;}
scip :: String -> IOMayfail String scip zimplCode = do {lift $ writeFileAtomic zplFile zimplCode; exitCode <- lift $ system (command zplFile solFile); case exitCode of ExitSuccess -> lift (readFile solFile) ExitFailure n -> fail ("Calling Scip failed with code "++ show n ++".\n");}
command inFile outFile = "./scip -c 'read \"" ++ inFile ++"\"' -c 'optimize' " ++"-c 'write solution \""++outFile++"\"' -c 'quit'"
(I added {}; because I some mail clients use variable width fonts and mess up layout. Eg mine does.) The solver SCIP also offers a FFI interface. But I was too lazy to use that, yet. So I use a temp-file (which should really use openTempFile instead of a fixed name) for communication. So because of my lazyness there are still things in it that look like side-effects, but they are not so in principal. (Also I am the only user of my code, so I can get away with deferring true FFI.) Matthias.
participants (4)
-
Jochem Berndsen
-
John Meacham
-
Matthias Görgens
-
Max Rabkin