Reputation: 15199
To add IO functions to a programming language interpreter written in Haskell, I have basically two options:
unsafePerformIO
.The former feels like a bad idea to me -- this effectively negates any purity benefits by having IO
reach practically everywhere in the program. I also currently use ST
heavily, and would have to modify large quantities of the program to achieve this, as there is no way I can see to use both ST
and IO
at the same time (?).
The latter makes me nervous -- as the function name states, it is unsafe, but I think in this situation it may be justified. Particularly:
seq
at control points during evaluation of interpreted expressions.unsafePerformIO
.In this circumstance, is there a good reason not to use unsafePerformIO
?
I was asked why I want to retain purity in the interpreter. There are a number of reasons, but perhaps the most pressing is that I intend to later build a compiler for this language, and the language will include a variety of metaprogramming techniques that will require the compiler to include the interpreter, but I want to be able to guarantee purity of the results of compilation. The language will have a pure subset for this purpose, and I would like the interpreter to be pure when executing that subset.
Upvotes: 2
Views: 157
Reputation: 13876
If I understand it correctly, you want to add IO
actions to interpreted language (impure primops), while the interpreter itself is pure.
The first option is abstract primops from interpreter. For example, the interpreter could run in some unspecified monad, while priops are injected:
data Primops m = Primops
{ putChar :: Char -> m ()
, getChar :: m Char
, ...
}
interpret :: Monad m => Primops m -> Program -> m ()
Now interpreter can't perform any IO
action except the closed list of primops. (You can achieve similar result using custom monad instead of passing primops as an argument.)
But I'd consider it over-engineering until you say exactly why you need pure interpreter. Probably you don't? If you just want to make pure parts of the interpreter easy to test, then it is probably better to extract those parts into separate pure functions. That way the top level entry point will be impure, but small, yet all the interpreter's logic will be testable.
Upvotes: 2