scalafx icon indicating copy to clipboard operation
scalafx copied to clipboard

Add map to ReadOnlyProperties

Open tomoki opened this issue 10 years ago • 9 comments

Hi.

I suggest that we should define map for ReadOnlyProperty. Generally speaking, I think it is called fmap in functional reactive programming. It is very simple, easy to read, and can be considered as Scala-style programs.

For example, if we have map for StringProperty, we can write programs like following:

object Main extends JFXApp {
  val a = StringProperty("")
  val b = a.map((s: String) => s + "!")
  val c = b.map((s: String) => s + "?")
  a.value = "i"
  println(a.value) // => i
  println(b.value) // => i!
  println(c.value) // => i!?
}

(this program cannot be compiled)

How do you think about this?

EDIT:

I just wrote working prototype for StringProperty

import scalafx.application.JFXApp
import scalafx.beans.property.StringProperty

object Main extends JFXApp {
  // NOTE: this is just prototype, proof of concept.
  // We should define more generic and safe way.
  def mapProperty(v: StringProperty, f: (String) => String) : StringProperty = {
    val r = StringProperty(f(v.value)) // initialize with current value
    def update(): Unit = {
      r.value = f(v.value)
    }
    v.onChange(update)
    r
  }
  val a = StringProperty("")
  val b = mapProperty(a, (s: String) => s + "!")
  val c = mapProperty(b, (s: String) => s + "?")

  a.value = "i"
  println(a.value) // => i
  println(b.value) // => i!
  println(c.value) // => i!?
}

tomoki avatar Feb 04 '16 16:02 tomoki

I like it! +1 from me

ghost avatar Feb 05 '16 05:02 ghost

Note that ScalaFX already has support for creating expressions using properties. Those expressions can then be used to create other expression or bind to other properties. Here is your example expressed using StringExpression and operator overloading:

  val a = StringProperty("")
  val b = a + "!"
  val c = b + "?"

Above b and c are expressions based on StringProperty a.

jpsacha avatar Feb 06 '16 19:02 jpsacha

@jpsacha I'm sorry, my example which I showed in first comment is not powerful.

You're right, we have StringExpression but I think it is limited to very simple expression. We cannot use functions for String easily, or convert to other Property types.

Here is a small example:

val a = StringProperty("")
// We can use methods for String easily
val b = a.map(_.toLowerCase)
// or, we can apply type conversions.
val c : ReadOnlyNumberProperty = b.map(_.toInt)

tomoki avatar Feb 06 '16 20:02 tomoki

I understand. Still I want make sure that available JavaFX support for writing arbitrary binding "expressions" are considered before adding new functionality. There is a part of JavaFX binding support that is not covered in ScalaFX. Consider method Bindings.createStringBinding it is not currently wrapped in ScalaFX, with not much effort it will allow code like this

  val a = new StringProperty()
  val b = createStringBinding(() => Option(a.value).getOrElse("").toLowerCase(), a)

  a.value = "HEllO"
  System.out.println("a: " + a.value)
  System.out.println("b: " + b.value)

with

  def createStringBinding(op: () => String, values: Observable*): StringBinding = {
    javafx.beans.binding.Bindings.createStringBinding(
      new java.util.concurrent.Callable[String] {
        override def call() = op()
      },
      values.map(_.delegate): _*)
  }

Those missing methods needs to be added to scalafx.beans.binding.Bindings, I added issue #232 for that.

jpsacha avatar Feb 08 '16 03:02 jpsacha

@jpsacha : is the issue still up-to-date? While the convenience expressions are nice, having map (and flatMap as well as map2) would allow to write arguably more Scala-idiomatic property transformations.

mikolak-net avatar Nov 27 '16 22:11 mikolak-net

If you would like to create a PR with implementation. It would be very welcomed.

jpsacha avatar Nov 28 '16 03:11 jpsacha

Excellent! Should have something up this week.

(EDIT: nope, Dec W3 seems more likely)

mikolak-net avatar Nov 28 '16 07:11 mikolak-net

@jpsacha : ...and W3 has moved to W14 mod year. Anyway, could you take a look at https://github.com/scalafx/scalafx/pull/268 and provide feedback?

mikolak-net avatar Mar 30 '17 20:03 mikolak-net

It will be done directly in JavaFX: https://github.com/openjdk/jfx/pull/675

s-bernard avatar Jun 02 '22 21:06 s-bernard

map is now part of the JavaFX 19 API and is supported by ScalaFX 19.0.0-R30

jpsacha avatar Oct 06 '22 00:10 jpsacha