Add map to ReadOnlyProperties
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!?
}
I like it! +1 from me
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 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)
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 : 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.
If you would like to create a PR with implementation. It would be very welcomed.
Excellent! Should have something up this week.
(EDIT: nope, Dec W3 seems more likely)
@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?
It will be done directly in JavaFX: https://github.com/openjdk/jfx/pull/675
map is now part of the JavaFX 19 API and is supported by ScalaFX 19.0.0-R30