Creating fields
The first thing to consider when creating a field is whether we want to be able to change its value. If the answer is yes, then we'll need a MutableField
.
Most of the time, we will want to be able to set values in our fields manually. For this purpose we need a MutableField
instance.
import com.femastudios.dataflow.util.*
fun main() {
val f /* : MutableField<String> */ = mutableFieldOf("hello world")
f.value = "foo bar" //Allowed
println(f.value)
}
MutableField<String> f = MutableField.of("hello world");
f.setValue("foo bar"); //Allowed
If we want to change the value based on the previous one, we can do something like this:
import com.femastudios.dataflow.util.*
fun main() {
val f = mutableFieldOf(5)
f.setValue { it * 10 } //Will multiply 5 by 10, setting the value to 50
println(f.value)
}
MutableField<Integer> f = MutableField.of(5);
f.setValue(old -> old * 10); //Will multiply 5 by 10, setting the value to 50
The given lambda will atomically change the value of the field: the value won't be changed until the computation is complete, and multiple calls to the function will be enqueued and be executed one at a time.
Sometimes the need may arise to create a field with a constant value (i.e. we cannot set the value).
import com.femastudios.dataflow.util.*
fun main() {
val f /* : Field<String> */ = fieldOf("hello world")
println(f.value) //OK
//Cannot call f.value = "foo bar"
}
Field<String> f = Field.of("hello world");
//Cannot call f.setValue("foo bar");
Please note that there are a couple restrictions on things that should be put as field values:
- Only immutable data: fields cannot know when the internal state of the value they hold changes, thus being unable to call the necessary callbacks in that case. For this reason, you should never put mutable data in fields; this also makes the fields thread-safe.
equals()
implemented correctly: two equal objects should never have some accessible property whose value is different. This because when a value is trying to be set in a field, it is first checked for equality with the old one; iftrue
is returned the value is not set and listeners are not called. This is necessary to improve efficiency as the number of fields in the program explodes.
Obliviously fields cannot check if the values satisfy this restrictions, neither at compile-time nor at run-time, so it is the programmer's duty to avoid this situations.
In order to make field creation easier and more idiomatic, several utility functions are available to create both constant and mutable fields.
These functions allow to create a Field
whose value is a well-known constant (either null
, true
or false
).
import com.femastudios.dataflow.util.*
fun main() {
fieldOfNull() //yields Field<Nothing> containing null
fieldOfTrue() //yields Field<Boolean> containing true
fieldOfFalse() //yields Field<Boolean> containing false
}
FieldUtils.fieldOfNull(); //yields Field containing null
Field.ofNull(); //same as above
FieldUtils.fieldOfTrue(); //yields Field<Boolean> containing true
FieldUtils.fieldOfFalse(); //yields Field<Boolean> containing false
We can also put immutable collections in fields, and Dataflow provides a few utility functions for this purpose. More information about collections in fields can be found here.
A way to create fields or mutable fields that contain an immutable List
.
import com.femastudios.dataflow.util.*
fun main() {
//yields Field<List<String>> containing the immutable list ["a, "b", "c"]
listFieldOf("a", "b", "c")
//yields Field<List<Int?>> containing an immutable empty list
listFieldOf<Int?>()
//yields MutableField<List<Float>> containing the immutable list [1, 2, 3]
mutableListFieldOf(1f, 2f, 3f)
}
//yields Field<List<String>> containing the immutable list ["a, "b", "c"]
FieldUtils.listFieldOf("a", "b", "c")
//yields Field<List<Integer>> containing an immutable empty list
FieldUtils.<Integer>listFieldOf()
//yields MutableField<List<Float>> containing the immutable list [1, 2, 3]
FieldUtils.mutableListFieldOf(1f, 2f, 3f)
Similarly to lists, these functions create fields or mutable fields that contain an immutable Set
.
import com.femastudios.dataflow.util.*
fun main() {
//yields Field<Set<String>> containing the immutable set {"a, "b", "c"}
setFieldOf("a", "b", "c")
//yields Field<Set<Int?>> containing an immutable empty set
setFieldOf<Int?>()
//yields MutableField<Set<Float>> containing the immutable set {1, 2, 3}
mutableSetFieldOf(1f, 2f, 3f)
}
//yields Field<Set<String>> containing the immutable set {"a, "b", "c"}
FieldUtils.setFieldOf("a", "b", "c")
//yields Field<Set<Integer>> containing an immutable empty list
FieldUtils.<Integer>setFieldOf()
//yields MutableField<Set<Float>> containing the immutable set {1, 2, 3}
FieldUtils.mutableSetFieldOf(1f, 2f, 3f)
Lastly, we can also create fields or mutable fields that contain an immutable Map
.
import com.femastudios.dataflow.util.*
fun main() {
//yields Field<Map<Int, Char>> containing the immutable map {1: 'a', 2: 'b'}
mapFieldOf(1 to 'a', 2 to 'b')
//yields Field<Map<Int, Boolean?>> containing an immutable empty map
mapFieldOf<Int, Boolean?>()
//yields MutableField<Map<Char, Double>> containing the immutable map {'a': 1.5, 'b': 5.4}
mutableSetFieldOf('a' to 1.5, 'b' to 5.4)
}
//yields Field<Map<Int, Character>> containing the immutable map {1: 'a', 2: 'b'}
FieldUtils.mapFieldOf(new Pair(1, 'a'), new Pair(2, 'b'))
//yields Field<Map<Int, Boolean>> containing an immutable empty map
FieldUtils.<Int, Boolean>mapFieldOf()
//yields MutableField<Map<Character, Double>> containing the immutable map {'a': 1.5, 'b': 5.4}
FieldUtils.mutableSetFieldOf(new Pair('a', 1.5), new Pair('b', 5.4))