I’m not really concerned about the static type system, but I don’t want to discard it because sometimes some things are only useful if you also have one, such as for sum types which really only start becoming useful when you have something to remind you to handle all cases.
I’m talking about purely functional languages vs impure functional languages.
The difference would be that in a pure language, all your functions must be pure, no exception. In an impure one, you’re allowed to have a mix of pure functions and impure ones (might as well call them procedures).
Now I understand why given some logic which could be implemented using mutation and use of non local data, such as you would in OOP or imperative language, that you’d want that to instead be implemented with pure functions such as done in FP.
But programs deal with inherently impure effects, like moving a file from one directory to another, sending an email, querying a database, etc.
Due to this truth, pure programming languages must find a way to handle these effects and still pretend to be observably pure.
Haskell does so by having the functions which perform effects such as IO not really perform the IO, but just remember the list of all effects and their order and wraps all that in a chain of IO actions including wrapping all functions that use the result of IO as well inside thunks. So now, your function that deletes a file? Well it doesn’t delete the file, it just returns a data-structure that says please delete this file for me.
What happens next is that the main function will take these data-structures and will perform the effects that they ask of it. So main will go and delete the file.
I can’t seem to come up with any practical benefits to this, apart from conceptual consistency in that you can kind of still pretend to be a purely functional language.
Doing that doesn’t require a static type system, or even something tracking which function do IO or not. All of it can be modeled with discipline in Clojure for example.
So I’m curious, are there any benefits that I’m failing to think of? Could the benefits only manifest itself when also combined with a static type system?
If I wrote Clojure code like that, would I gain anything?