[Haskell-cafe] Using unsafePerformIO safely

Luke Palmer lrpalmer at gmail.com
Thu Jun 25 02:44:23 EDT 2009


On Wed, Jun 24, 2009 at 11:13 PM, Hector Guilarte <hectorg87 at gmail.com>wrote:

>
> Thanks! Actually,  if I understood well what you proposed, that's how I
> first tought of doing it, but with a [Maybe String] and only append whenever
> I actually had a (Just string), but as I said before, I don't think my
> teacher is gonna like that solution since it is not going to print when the
> interpreter finds the show instruction in the GCL code, it is gonna wait
> until it finishes to interpret the whole code and then print everything.
>

Not true!  Haskell is lazy.  You just have to think about program evaluation
inside out.  So it's not "print *when* you come across this instruction",
but rather "run the program just far enough to print the first line".

That's would be ok with me, but actually in a language point of view that
> wouldn't be to usefull, since trying to debug a program printing flags
> couldn't be done (and I'm not doing a debbuger for it). I know my language
> is not gonna be used for real, but I'm sure that would be my teacher's
> argument to tell me I can't do it that way. Still, I sent him an e-mail
> asking if it can be done like that just in case.
>

I assume that Haskell is *your* language choice, not the teacher's.  In
which case it may be very hard to convince the teacher that this is correct,
if he is used to thinking in an imperative style.  Nonetheless, you chose a
functional language and it is only fair to solve the program in a functional
way, right?  Haskell tries to be a language with *no* side effects at all,
it just "accidentally" falls short here and there; if it achieves its goal,
there would be *no* correct way to solve it by your alleged teacher's
argument.


>
> If I didn't understand what you said, can you explain it again please?
>

I think you've got it, except for the understanding that lists are lazy so
"running the program" is equivalent to asking for elements from the list.


>
> If I did then, does anybody knows how to print on the screen in the moment
> the show instruction is interpreted that guarantees that my code is gonna be
> "safe"
>

That is a very hard thing to ask of a pure language, which does not really
concern itself with such questions.  "The moment the show instruction is
interpreted" is abstracted away from the Haskell programmer, so that the
compiler may choose to evaluate it any time it pleases (as long as it gives
you the answer you asked for).


> Also, nobody has told me why I shouldn't just use my original solution
> using unsafePerformIO, is it really bad? is it dangerous? why is it
> "unsafe"?
>

It is contrary to the spirit of Haskell, and breaks invariants that
reasoning in the language relies on.  It is not technically "dangerous",
i.e. your program will still "work", but you will be contested here and
there about whether it should be considered correct.  If you published a
Haskell module which used such a trick to the community, you would have a
very hard time convincing people to use it.

The invariant it breaks is called "referential transparency", or maybe
"purity" (those terms are kind of muddled together the way I look at
things).  Either way, consider the following program:

mult2 x = 2*x
main = do
    print (mult2 21)
    print (mult2 21)

Now, functions are in the mathematical sense in Haskell, so the result of a
function is *entirely* determined by its arguments.  That means, without
considering anything about the definition of mult2, refactor main into:

main = do
    let answer = mult2 21
    print answer
    print answer

And we *know* that this program will behave exactly the same.

However, if mult2 were instead:

mult2 x = unsafePerformIO $ do { print x; return (2*x) }

Then the former program would probably print 21,42,21,42,  whereas the
latter would print 21,42,42.  Thus our *correct* transformation of programs
changed behavior.

Haskell programmers rely on this sort of refactoring all the time, and
unsafePerformIO has the potential to break it.  That is why it's unsafe.

Safe uses of unsafePerformIO are those where any such transformation won't
change the overall behavior of the program.  They do exist, and are usually
employed for optimization purposes.  But yours is not such a case; your
unsafePerformIO has observable side-effects.

The "list of strings" solution is *the* Haskell way to solve this problem.
If your teacher does not accept the solution, then your teacher is not
actually letting you program in Haskell.

Cheers,
Luke
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20090625/52336bc7/attachment.html


More information about the Haskell-Cafe mailing list