When function should be effectful?

287 views Asked by At

When I use FFI to wrap some API (for example DOM API) is there any rule of thumb that could help me to decide whether function should be effectful or not?

Here is an example:

foreign import querySelectorImpl """
    function querySelectorImpl (Nothing) {
      return function (Just) {
        return function (selector) {
          return function (src) {
            return function () {
              var result = src.querySelector(selector);
              return result ? Just(result) : Nothing;
            };
          };
        };
      };
    }
  """ :: forall a e. Maybe a -> (a -> Maybe a) -> String -> Node -> Eff (dom :: DOM | e) (Maybe Node)

querySelector :: forall e. String -> Node -> Eff (dom :: DOM | e) (Maybe Node)
querySelector = querySelectorImpl Nothing Just

foreign import getTagName """
    function getTagName (n) {
      return function () {
        return n.tagName;
      };
    }
  """ :: forall e. Node -> Eff (dom :: DOM | e) String

It feels right for querySelector to be effectful, but I'm not quite sure about getTagName

Update:

I understand what a pure function is and that it should not change the state of the program and maybe DOM was a bad example.

I ask this question because in most libraries that wrap existing js libraries pretty much every function is effectful even if it doesn't feels right. So maybe my actual question is - does this effect represent the need in this wrapped js lib or is it there just in case it is stateful inside?

2

There are 2 answers

0
Pauan On BEST ANSWER

If a function does not change state, and it always (past, present, and future) returns the same value when given the same arguments, then it does not need to return Eff, otherwise it does.

n.tagName is read-only, and as far as I know, it never changes. Therefore, getTagName is pure, and it's okay to not return Eff.

On the other hand, a getTextContent function must return Eff. It does not change state, but it does return different values at different times.

The vast vast vast majority of JS APIs (including the DOM) are effectful. getTagName is one of the very few exceptions. So when writing an FFI, PureScript authors just assume that all JS functions return Eff, even in the rare situations where they don't need to.

Thankfully the most recent version of purescript-dom uses non-Eff functions for nodeName, tagName, localName, etc.

2
mb21 On

Effectful functions are functions that are not pure, from Wikipedia:

In computer programming, a function may be described as a pure function if both these statements about the function hold:

  1. The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change as program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices [...].

  2. Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices [...].

Since the DOM stores state, functions wrapping calls to the DOM are almost always effectful.

For more details regarding PureScript, see Handling Native Effects with the Eff Monad.