3

Lets say I have a function foo :: String -> IO String that makes a request to a web service, downloads some data, parses it, and returns a string. I know that the web service will always return the same string, given the same request string, barring a lack of an internet connection of something of the sort. Is this enough information to "safely" use unsafePerformIO and know that it will never cause problems? Or must I make sure of some other things as well?

Edit: the reason I am considering this is because I have a function deduce :: (String -> String) -> String that will take an input function as a paramater and deduce some properties of the function by evaluating it with different paramaters. Now I want to deduce some properties of this web service but without unsafePerformIO I will have to change deduce substantially, including changing its type signature to deduce :: (String -> IO String) -> String which means I need to change all the other functions I may want to deduce properties of as well.

Drew
  • 12,578
  • 11
  • 58
  • 98
  • 4
    Given the fact that the request may, as you said yourself, fail I doubt this is a good idea. – Cubic Jun 28 '13 at 14:51
  • My rule of thumb is that if you feel okay with the compiler precomputing the entire value at compile time then you can use `unsafe PerformIO`. – Gabriella Gonzalez Jun 28 '13 at 15:04
  • 4
    Speaking of which, if the result never changes then why don't you just hard code the result into your program? – Gabriella Gonzalez Jun 28 '13 at 15:06
  • Yes, that sounds safe enough. – augustss Jun 28 '13 at 15:14
  • 3
    Or use TH to insert that into your program at compile time. – Satvik Jun 28 '13 at 15:48
  • 2
    Question [Load pure global variable from file](http://stackoverflow.com/q/12716215/1333025) is similar, the top answer shows how to use TH for loading an `IO` resource at compile time. – Petr Jun 28 '13 at 17:15
  • I think you guys are misunderstanding. The web service will always return the same string for the same input. The input is generated based on other stuff within the pure portion of my program, so I may call this function multiple times within my program with different paramaters. – Drew Jun 28 '13 at 18:58
  • I added some details. Also, if the request fails, it will throw an exception, instead of returning a different value, so its still almost referentially transparent, it will always either return ⊥ or the same value. – Drew Jun 28 '13 at 19:15
  • If you can't use it in a kernel then it should have `IO`. – Thomas M. DuBuisson Jul 02 '13 at 03:47

1 Answers1

11

Don't do that. unsafePerformIO is for binding foreign primitives, doing deep datastructure hacks, and sometimes establishing top level mutable state.

It sounds safe, but its a bad idea, because you will now have connections occuring magically when you did not expect them to, thus making the rest of your program harder to reason about, in terms of resource usage, etc.

As folks have noted, if the service really always does return the same string, then using Template Haskell to make the request once and for all at compile time is safer and more efficient.

Edit: Ok, I now see the issue. But you don't have to change the type of the other functions you want to deduce things about.

If the type of f is String -> String, then you can always get return . f :: String -> IO String. So don't screw with your other functions, just compose them with return. Now you can handle both effectful and pure code with equal ease..

sclv
  • 38,665
  • 7
  • 99
  • 204
  • I think you misunderstand when i say it 'returns the same string.' I mean it always returns the same string, given the same input, and many different inputs will be used over the life of the program. I added some more details of what exactly I'm doing in the original question. – Drew Jun 28 '13 at 19:17
  • @Drew I'd still recommend that you instead use `IO`. It is not that hard to use. – Gabriella Gonzalez Jun 29 '13 at 01:14