Using PureScript in an impure way

Hello functional programmers,
a trivial, maybe common, probably blasphemous question.

Is it possible to declare FFI functions with side effects (i.e. IO functions or mutations) without using effects/monads?

I’m wondering if we can use PureScript in a way similar to Reason, Gleam, Grain, F# and so on. Purity is wonderful but sometimes we just don’t need it, yet we could still take advantage of the great PureScript type system.

I suppose that the compiler, assuming referential transparency, could rewrite the transpiled code in an unexpected way, producing a wrong program. In such case is there a way to disable these optimizations?

I’m mainly interested to the Purerl project, I would like to have the option to write Erlang code in type safe manner without having an huge cognitive effort.

Thanks in advance.

1 Like

I didn’t really give much thought to this, but couldn’t you achieve this by wrapping basically everything in Effect?

(Yeah, I realize that might be impractical, but I’m just saying my thoughts out loud here.)

Without a value restriction, it is pretty easy to subvert the type system using polymorphism and mutability (which can crop up in unintuitive ways), so an “impure” subset is not sound. We use Effect and bind as an alternative to the value restriction. Whether or not this is important to you, I can’t say, and of course there’s already unsafeCoerce. I personally would not like to see this style of programming in PureScript become something that is advertised or pushed by a subset of the community.

9 Likes

There’s nothing that technically prevents you from doing this. In some cases there are libraries which do this in a limited way, for example by giving internal functions dodgy types which, say, claim to be pure when they are not. Importantly, though, those functions mostly aren’t exposed so the actual API surface is not making any dodgy claims.

There are no such optimizations that I am aware of off the top of my head which would invalidate this approach, but that’s not to say you’ll be without problems if you write code this way. I think your two main problems with writing PureScript this way are going to be:
a) everyone expects you to write PureScript the normal way, which is going to add friction whenever you are using 3rd party libraries
b) without purity, evaluation order becomes much more important, and it’s sometimes difficult to predict what will be evaluated when, especially when you have lots of where and let involved. An unused binding in a where clause could quite easily result in your code triggering a request to some API unconditionally even if you only intended it to happen on a certain code path, for example.

5 Likes

The one thing i would say in defence of ignoring side-effects and hidden polymorphism is that it can be useful (i think) as a transitionary stage while prototyping how you actually want to wrap some library or JS functionality. Taking a very principled approach from the get-go can - in my experience anyway - lead to a lot of busy-work on the type signatures of the foreign functions.

But i absolutely agree that it’s self-defeating to use PureScript this way and likely something you’ll regret later if you expose such aberrations to other people or other code. It will bite you eventually and it will be hard to debug.

1 Like

Thanks for all your feedback.
I tried but this approach seems more tricky than using PureScript in the right way.
So I agree with you, it’s a bad idea.