19

I'd like to understand the original meaning of functional reactive programming in Haskell and how it differs from the actual application of FRP in Javascript. Unfortunately, I have only a superficial understanding of Haskell and have to stick with Javascript.

Here is my attempt to implement Haskell's Behavior data type in an untyped language:

// behavior :: String -> DOMHTMLElement -> a -> (a -> Event -> a) -> (a -> b)

const behavior = type => target => x => f => {
  const destructiveSet = y => (x = f(x) (y), x), // A
   handler = event => destructiveSet(event);

  target.addEventListener(type, handler, true);
  return g => g(x);
};

Line A is necessary, because I have to mutate the initial value x, which is held by the call stack. The function body evaluates from left to right and returns the value of the last operand of the comma operator ,, i.e. the destructively updated version of x.

target.addEventListener merely subscribes the given handler to the given DOM HTML-element.

behavior returns a function that enables read-only access to x.

This implementation introduces a read-only abstract data type in Javascript, whose values solely exist in the call stacks of higher order functions. Provided that DOM Events are only triggered by GUI users, a program has no means to mutate values of type behavior. It can only poll them to observe the time-varying effect.

Is this approach remotely comparable to Haskell's Behavior?

Here is a small example - a mouse click counter (counts for four seconds):

const behavior = type => target => x => f => {
  const destructiveSet = y => (x = f(x) (y), x),
   handler = event => destructiveSet(event);

  target.addEventListener(type, handler, true);
  return g => g(x);
};


const comp = f => g => x => f(g(x));

const K = x => y => x;

const I = x => x;

const get = I;

const defer = n => f => x => setTimeout(f, n, x);

const log = prefix => x => console.log(prefix, x);

const inc = x => x + 1;

const counter = behavior("click") (document) (0) (comp(K) (inc));


console.log("click on the section above");

counter(get); // 0

defer(4000) (counter) (log("counted clicks:"));
  • Is it supposed to count more than 1 click? It only counts 1 for me, no matter how many times I click. – Thank you Feb 21 '17 at 21:10
  • @naomik "Is it supposed to count more than 1 click?" -- It is, and at least for me it works that way (Firefox 51.0.1 on Linux, in case that matters). – duplode Feb 21 '17 at 21:14
  • @duplode ah wups. I waited until the 4 seconds was over. If I click 10 times within the 4-second window, it will display 10 in the log. – Thank you Feb 21 '17 at 21:16
  • 2
    Nitpick: in your signature comment, the result type should be `(a -> b) -> b` rather than `a -> b`. – duplode Feb 21 '17 at 22:44
  • 2
    I highly recommend reading [this paper](http://conal.net/papers/icfp97/), which was deeply inspiring to me and will show you some of the "philosophy" of FRP. It might give you a better grasp of whether your JS implementation is true to the spirit of FRP. I don't think it requires an awful lot of Haskell knowledge, just basics, as far as I remember. – luqui Feb 22 '17 at 00:59
  • I skimmed through the paper @luqui has suggested. My `behavior` implementation is remotely similar to the `stepper` combinator. `Behavior` in Haskell seems to be an unary type constructor that implements the `Applicative` type class. So I somehow mixed a type class and a combinator - it's a big mess. I will continue to study the subject and probably answer the question myself. –  Feb 22 '17 at 09:43
  • @naomik I am glad that you brought up this particular property of higher order functions to create data types in one of your answers. I wonder if we can define `sum types` in Javascript and how they would affect the implementation of monads and other algebras. –  Feb 22 '17 at 09:49
  • @ftor which answer are you referring to? – Thank you Feb 22 '17 at 13:48
  • 1
    @naomik [Inject functions with side-effects](http://stackoverflow.com/a/42193108/6445533) –  Feb 22 '17 at 14:06
  • Haskell FRP Behavior is just a property calculated event results. –  May 02 '17 at 14:26

1 Answers1

1

Your implementation of behavior is closer to events in the FRP terminology, even though FRP events and DOM events have nothing in common. At its core, FRP abstracts the notion of continuous (as opposed to discrete) time. Behavior a represents a continuous flow of values of type a; hence its meaning is as a function from time to value.

It is literally how Conal Eliott defined it:

μ :: Behaviour a -> (Time -> a)

That notation is the one he used to describe the meaning of something as a function from that thing to a concrete computational value.

Events are "behaviours frozen in time", i.e. they represent values that occured at specific moments:

μ :: Event a -> [(Time, a)]

Because Haskell is a lazy language, streams of values can be represented as lists, which is what the above is saying.

Oddly enough, there are not many implementations of FRP that remain true to the original idea, as (I think) it is hard to come up with a performant realisation of continuous time—this C++ one seems to come close.

I encourage you to watch Conal Eliott's talks available online, his are some of the most elegant API designs I've ever seen and he explains it with crystal clarity.

Regis Kuckaertz
  • 941
  • 5
  • 14