OlivierBlanvillain / monadic-html

Tiny DOM binding library for Scala.js
https://olivierblanvillain.github.io/monadic-html/examples/
MIT License
225 stars 24 forks source link

implement now method for Rx/Var #83

Closed antonkulaga closed 6 years ago

antonkulaga commented 6 years ago

In Scala.Rx there was useful .now method that allowed to know current value of Rx/Var from outside, please, implement it!

bbarker commented 6 years ago

@antonkulaga This was removed, it was called impure.value; see #43.

However, you can implemented like this (it is used in testing), yourself, if you really want it.

OlivierBlanvillain commented 6 years ago

And it got removed for a very good reason: there is no "current value" of a Rx.

If you are used to an API that doesn't honor referential transparency you might be surprised than (?) is -1 and not 3 in this example:

val source = Var(0)
val odd = source.keepIf(_ % 2 == 1)(-1)

odd.impure.foreach(println) // prints -1
source := 2                 // (nothing)
source := 3                 // prints 3
source := 4                 // (nothing)

println(odd.impure.value)   // prints (?)

(In Scala.Rx filter has a weird semantics, it would happily give you 0 as the first value of odd, but that's an orthogonal problem.)

The reason for that behavior is that every call to impure.foreach does a complete interpretation of the Rx graph, which is completely independent of whether someone else is already "listening" to that Rx or not.

antonkulaga commented 6 years ago

It happens quite often when you want to read current values of Rx-es, for instance, you are sending a message to the server (via websocker or ajax) and you want to get all values of the fields inside the message form that is connected with UI via Rx[String] Maybe you can just make an "unsafe" package with implicits? So, people who need this feature will use it, but will understand that it can lead to some strange corner-cases

OlivierBlanvillain commented 6 years ago

I think I didn't give you a good example. It's not that "getting the current value" is unsafe or wrong, but rather than there is no such thing as the current value of a Rx.

Let me try to answer your question with another question. What would expect as the "current value" of count in this example:

val source: Var[Int] = Var(1)
val count = source.foldp(0)(_ + _)
source := 1
source := 1
source := 1
println(count.currentValue) // ?

What about in this example:

val source: Var[Int] = Var(1)
source := 1
source := 1
source := 1
println(source.foldp(0)(_ + _).currentValue) // ?
bbarker commented 6 years ago

@antonkulaga I haven't worked with an entire form in monadic-html (that I can remember), but I think it would be similar to something like this:

    val currentUrl = Var(URI.create(defaultUrl))
    def app: (Node, Rx[URI]) = {
      val div =
        <div>
          <input type="text" placeholder={prompt}
                 id = {formId} />
          <button onclick={ (ev: dom.Event) => {
            val text: String = ev.target match {
              case elem: dom.Element => elem.parentNode.childNodes.toIterable
                .find(child => child.nodeName === "INPUT") match {
                case Some(elem: html.Input) => elem.value.trim
                case None => defaultUrl
                case _ =>
                  println(s"Error: unrecognized element for SpecifyUri($formId)")
                  defaultUrl
              }
              case _ => println("Event error!")
                defaultUrl
            }
            currentUrl := URI.create(text)
          }}>OK</button>
        </div>

But instead of only updating one Var in the handler (currentUrl), you might have several you need to update. Ultimately, that html is mounted into the page, which runs any Rxs present in the mounted HTML, computing their value (to distinguish from the non-existent current value).

bbarker commented 6 years ago

Actually, now that I think about it, that could get rather tedious for many inputs, though I guess that could be abstracted to some extent by by extracting the case elem to a function.

antonkulaga commented 6 years ago

@bbarker it is A LOT of boilerplate! When I used Scala.Rx what I did is just got .now values (for monadix html I have an ugly workaround that works only for Vars and involves creating an implicit class that gets value of [mhtml] var cacheElem) and it was very convenient, here you propose to do DOM traversal that negates all the advantages of Rx-es.

OlivierBlanvillain commented 6 years ago

@antonkulaga Scala.Rx provides a .now method at the cost of memory leaks and a non referentially transparent API. Nothing's wrong with that, it's just a different design :). As I said before there is no such things as the current value of a mhtml.Rx, and the concept will never ever exist.

Now if you are interested, I can try to explain why this is the case, how things work in mhtml APIs, and also, given a concrete use case for .now, how to reformulate it without a .now.