I am using Javaslang-2.1.0-alpha and its Javaslang-match equivalent to do some object decomposition. According to this by blog post by Daniel in the "Match the Fancy way" section:
Match(person).of( Case(Person("Carl", Address($(), $())), (street, number) -> ...) )
Should retrieve values matching the two wildcard patterns inside Address
into street
and number
but the example does not even compile. I later realized all objects must be wrapped inside atomic patterns i.e. "Carl" becomes $("Carl"). This was after reading this issue.
I followed the updated tutorial but there was no update to this example.
I updated the example to this:
Person person = new Person("Carl", new Address("Milkyway", 42));
String result2 = Match(person).of(
Case(Person($("Carl"), Address($(),$())),
(street, number) -> "Carl lives in " + street + " " + number),
Case($(), () -> "not found")
);
System.out.println(result2);
It compiles but my values are not being matched properly, judging from the console output:
Carl lives in Carl Address [street=Milkyway, number=42]
It's clear that street
contains Carl and number
, the entire Address
object.
When I try to add a third lambda parameter to catch Carl:
Case(Person($("Carl"), Address($(),$())),
(name, street, number) -> "Carl lives in " + street + " " + number)
The code can't compile, the lambda expression gets a red underline with the following error text:
The target type of this expression must be a functional interface
There is no way of ignoring a value with $_
in the latest versions of javaslang-match. So I want to match each of the atomic patterns which would return three lambda parameters as above.
I need somebody who understands this library to explain to me how to do this object decomposition in the latest version.
Disclaimer: I'm the creator of Javaslang.
The case needs to handle (String, Address) -> {...}. The $() match arbitrary values but the handler/function receives only the first layer of the decomposed object tree. The $() are at the second layer.
Rule: All layers are matched against patterns, only the first layer is passed to the handler.
The first prototype of Match in fact handled arbitrary tree depths but methods hat to be generated under the hood for all possible combinations - max byte code size easily exceeded and compile time exponentially exploded to infinite.
The current version of Match is the only practical way in Java I see at the moment.
Update:
Please let me give a more figurative update on this topic.
We distinguish between
Ad 1) The Object Graph
Given an object, the object graph is spanned by traversing the properties (resp. instance variables) of that object. Notably we do not prohibit that an object contains cycles (e.g. a mutable list that contains itself).
In Javaslang there is no natural way how to decompose an object into its parts. We need a so-called pattern for that purpose.
Example of an object graph:
Person <-- root
/ \
"Carl" Address <-- 1st level
/ \
"Milkyway" 42 <-- 2nd level
Ad 2) The Pattern Tree
A pattern (instance) inherently defines how to decompose an object.
In our example the pattern types look like this (simplified generics):
Pattern2<Person, String, Address<String, Integer>>
/ \
Pattern0<String> Pattern2<Address, String, Integer>
/ \
Pattern0<String> Pattern0<Integer>
The called pattern methods return instances of the above types:
Person(...)
/ \
$("Carl") Address(...)
/ \
$() $()
Javaslang's Match API does the following:
person
object to the first Case.person
object to the pattern Person(...)
Person(...)
pattern checks if the given object person
is of type Person
.
$("Carl")
and Address(...)
match these parts (recursively repeats 3.)Currently Java's type system does not allow us to pass matched objects of arbitrary object graph/tree levels in a typed way to the handler.
Ad 3) Decomposed Objects
We already mentioned object decomposition above in 2). In particular it is used when parts of our given objects are send down the pattern tree.
Because of the limitation of the type system we mentioned above, we separate the process of matching an object from the process of handling decomposed parts.
Java allows us to match arbitrary object graphs. We are not limited to any level here.
However, when an object successfully matched, we can only pass the decomposed objects of the first layer to the handler.
In our example these decomposed objects are name
and address
of the given person
(and not street
and number
).
I know that this is not obvious to the user of the Match API.
One of the next Java versions will contain value objects and native pattern matching! However, that version of pattern matching will be limited entirely to the first level.
Javaslang allows to match arbitrary object graphs - but it has a price. The handler does receive only the first layer of decomposed objects, which might be confusing.
I hope this answered the question in an understandable way.
- Daniel
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With