Is it possible to run something in Eff in a halogen ComponentHTML funcion?

183 views Asked by At

There are a couple of bindings to moment.js I'd like to use for rendering time spans in my Halogen UI which have types something like

diffMins :: forall eff. Moment -> Moment -> Eff (now :: NOW | eff) Number

If I want to use this function in my UI like this:

H.span_ [H.text $ diffMins (fromEpoch_ 0) (fromEpoch_ myTimeStamp)]

But this is in Eff so I can't.

What I can do is call into moment with this function:

js:

exports.duration_ = function (millis) {
    return moment.duration(millis).humanize();
};

ps:

foreign import duration_ :: Number -> String

humanizeMilliseconds :: Milliseconds -> String
humanizeMilliseconds (Milliseconds n) = duration_ n

My question (or several) then:

Is it "cheating" to call into javascript without saying it's an Eff. If not when is it considered ok and when not? I could squit either way and see these functions as side effecting or not.

If I couldn't have changed the way I'm calling moment, or indeed it is a bad idea, is there a way to do this in HTML?

1

There are 1 answers

0
gb. On BEST ANSWER

It is indeed not possible to perform anything effectful during renders in Halogen, as HTML is only data and render is state -> HTML.

As Phil says in the comment, you don't have to use Eff in the signature of FFI functions though, if you're sure they perform no effects. In this case, it's probably safe, since it's basically arithmetic on dates - but there may be some locale-specific stuff going on? If so it's only a little bit dodgy, as at least it will always give the same result on the same machine, unless the OS clock is messed with. I'd be a little hesitant to accept that as being effect free, but if it was really a problem and I needed to do it I'd at least ensure the function is not exported so it can't be used anywhere else except in the exceptional circumstance.

You could just do this in the component eval somewhere though and store the value in the component state - myTimeStamp must already be in there, so you could compute this value at the same time? That way you're not recomputing a static value with each render too.