Hi,

a few notes:

- IModel#of(WicketSupplier) is superfluous, since you can use the lambda directly as model instead
- IModel#of(IModel) makes no sense to me
- Model#of(WicketSuppler, WicketConsumer) is a duplicate of LambdaModel#of(WicketSuppler, WicketConsumer), IMHO we should remove the former - Model#loadableDetachable(WicketSupplier) could be moved to LoadableDetachableModel

WDYT?

Sven


On 24.04.2016 20:51, Sven Meier wrote:
Hi Martin,

that's quite nice to have these methods in IModel actually.

Java 8 default methods to the rescue :)

Regards
Sven



On 24.04.2016 17:20, Martin Grigorov wrote:
Hi,

I've moved Matthias' code to IModel in branch monad-model (
https://git1-us-west.apache.org/repos/asf?p=wicket.git;a=commitdiff;h=484132a8;hp=f2f0ba067e9cf3a4f31eb3c387d6244c3c06c44b
)
Just to see how it will look like.
And I think it is not bad at all!
By moving these methods to IModel we make them available to all impls. Once such method is called it effectively makes the model read-only, because a new IModel is returned and IModel#setObject() already throws an exception. If the application developer wants a read-write model then (s)he has to use
#flatMap() and return a writeable model.

The branch is just a playground!
I've touched one of the existing tests just because I was lazy to create a
new one.


Martin Grigorov
Wicket Training and Consulting
https://twitter.com/mtgrigorov

On Sun, Apr 24, 2016 at 12:56 PM, Matthias Metzger <
[email protected]> wrote:

Hi,


thanks for your comments!


whether those transformations should be done by a Model or
it would be better to be done by Functions and finally a Model
would consume the result
my thoughts exactly: Chaining of functions is a broader topic. Note how
ReadOnlyModel and LambdaColumn have identical #map() functions.

Furthermore Java Function objects already support chaining with
#andThen().
The Java compiler just doesn't allow calling methods on a method
reference:

// doesn't work
IModel<String> streetModel = LambdaModel.of(target,
Person::getAdress.andThen(Address::getStreet));
I agree with both of you. The 'first' method is actually something Java 8
is totally missing in my opinion, which is one reason for my ideas. The
other one being potential null values in the above, which we could rule out
using Optional:

LambdaModel.of(target, p ->
Optional.ofNullable(p).map(Person::getAddress).map(Address::getStreet).orElse("n/a"));

Maybe this is a better way to deal with this sort of chaining, because
it's actually the same approach for the 'LambdaColumn' and uses
mechanisms, which are more general. I think one could even make an argument
to create a getter for this case. The 'apply' could then be done in the
following way:

LambdaModel.of(target, p -> Optional.ofNullable(p).
   map(Person::getName).
   map(name -> house.getObject().apply(name)).
   orElse("n/a"));


- if this functionality should be in a model then in which class?
-- AbstractReadOnlyModel is deprecated. Maybe we should un-deprecate it
?!
-- the name "ReadOnlyModel" doesn't tell me what nice functionality I
could expect from it. But on the side all these methods should be
provided
only by a read-only model. Or not? I see they can easily be put in IModel
class, but then the developer should be careful with the usage of
#setObject()

Despite the above, I'll still answer this concern:
You are right. The naming is absolutely terrible and I have been
struggling with a good name not involving all the Haskell stuff I have been
taking inspiration from. At the moment I would tend to call it
MappableModel<T>. In light of the 8.x changes, I would let it implement
IModel<T> - so no need for un-deprecating AbstractReadOnlyModel<T>. This was just because I have been playing around with 7.x. Yes, ReadOnlyModel and/or LoadableDetachableModel and also maybe a MappableColumn. Basically
everything which mostly reads.

I have been tinkering with a method '#toIModel(BiConsumer<T, U> setter)',
transforming the MappableModel to an IModel. That comes with some
non-obvious caveats however.

Have a nice rest of the day,

Matthias




----- Ursprüngliche Message -----
Von: Sven Meier <[email protected]>
An: [email protected]
Gesendet: 12:23 Sonntag, 24.April 2016
Betreff: Re: Further Lambda Model ideas

Hi,

whether those transformations should be done by a Model or
it would be better to be done by Functions and finally a Model
would consume the result
my thoughts exactly: Chaining of functions is a broader topic. Note how
ReadOnlyModel and LambdaColumn have identical #map() functions.

Furthermore Java Function objects already support chaining with #andThen(). The Java compiler just doesn't allow calling methods on a method reference:

      // doesn't work
      IModel<String> streetModel = LambdaModel.of(target,
Person::getAdress.andThen(Address::getStreet));

You have to use a method call to get an actual function object first:

      // this does work
      IModel<String> streetModel = LambdaModel.of(target,
first(Person::getAdress).andThen(Address::getStreet));

      private <T1, T2> WicketFunction<T1, T2> first(WicketFunction<T1,
T2> func) {
          return func;
      }

For this to work we have to add an additional method for serialization
into WicketFunction though:

      // specialization of the super method for serializable functions
default <V> WicketFunction<T, V> andThen(WicketFunction<? super R,
? extends V> after)
      {
          Objects.requireNonNull(after);
          return (T t) -> after.apply(apply(t));
      }

Have fun
Sven


On 24.04.2016 11:34, Martin Grigorov wrote:
On Sun, Apr 24, 2016 at 11:16 AM, Martin Grigorov <[email protected]>
wrote:

Hi,

I like the functionality provided by ReadOnlyModel!
I have two questions/doubts:
- whether those transformations should be done by a Model or it would be
better to be done by Functions and finally a Model would consume the
result
- if this functionality should be in a model then in which class?
-- AbstractReadOnlyModel is deprecated. Maybe we should un-deprecate it
?!
-- the name "ReadOnlyModel" doesn't tell me what nice functionality I
could expect from it. But on the side all these methods should be
provided
only by a read-only model. Or not? I see they can easily be put in
IModel
class, but then the developer should be careful with the usage of
#setObject()

I think a better solution would be a read-write Model that uses
composition of functions internally for all the transformations.
#setObject() will manipulate the model's object. #getObject() will apply
the model object to the composed function to get the final result.

This won't work. The mapping cannot be in the same IModel, a new IModel
should be returned.


Martin Grigorov
Wicket Training and Consulting
https://twitter.com/mtgrigorov

On Fri, Apr 22, 2016 at 10:52 PM, Matthias Metzger <
[email protected]> wrote:

Hi,


thanks to all of you for the interest! I just added some examples to
the
README of my repository and implemented an alternative to/enhanced the
LambdaColumn. Hope this makes your life a little easier. If there is
anything else I can do for that matter, just hit me up.


@Carl-Eric: I am very keen to see what you'll come up with. Mapping
over
everything with a <T> was the best I could think of, without resorting
to
reflection. :D

Regards,
Matthias


________________________________
Von: Carl-Eric Menzel <[email protected]>
An: [email protected]
Gesendet: 10:10 Freitag, 22.April 2016
Betreff: Re: Further Lambda Model ideas


Hi Matthias,

interesting ideas, thank you for sharing! I'm going to have a closer
look either this weekend or next (a bit busy right now). Currently I'm working on some Java 8 helpers for Wicket 7, similar to the LambdaModel
thing currently in WicketStuff. I'm doing that mostly to just play
around with some ideas but also because I'm not entirely happy with the WicketStuff LambdaModel - basically I'm exploring to see whether I can
come up with something I like better.

Carl-Eric


On Thu, 21 Apr 2016 20:19:52 +0000 (UTC)
Matthias Metzger <[email protected]> wrote:

Hello everyone,
I just saw yesterday, that the IModel became/will become a
@FunctionalInterface in Wicket 8 and was very happy about it. I am
also very happy about the ongoing implementations and discussions
around lambda usage in Wicket, because it simplifies a lot of code.
Now, one thing I have been working on, is implementing some of the
now known methods of Stream<T> for a AbstractReadOnlyModel and also
for other things - like columns. I have not seen such discussions on
here, please forgive me if I missed that. If it has indeed not been
discussed and you find the idea useful, you can take a look at an
implementation in the following repository:

https://github.com/noobymatze/lambdawicket-model/blob/master/src/main/java/com/github/noobymatze/lambdawicket/model/ReadOnlyModel.java
What might we be able to do with that?
Consider a Person containing an Address, containing a Street with a
name. Now I want to display the name of the street. How would I go
about that?new Label("streetName", () ->
person.getAddress().getStreet().getName()); This is one approach, but
it completely ignores the possibility of null values and a default
value for such cases. I could wrap the above in an Optional or create
a method in the Person class like 'Optional<String> getStreetName()
{...}'.

With the proposed ReadOnlyModel I could also do it in the following
way: new Label("streetName", ReadOnlyModel.of(person).
map(Person::getAddress).  map(Address::getStreet).
map(Street::getName). orElse("n/a")); But that's not all. Imagine we
would need to dynamically truncate the streetName based on user
input: IModel<WicketFunction<String, String>> truncate = () -> str ->
str.length() > 10 ? str.substring(0, 10) + "..." : str;
IModel<String> truncatedStreetName = ReadOnlyModel.of(person).
map(Person::getAddress).    map(Address::getStreet).
map(Street::getName).    apply(truncate). orElse("n/a"); Now we
could switch the truncate Model using a
DropDownChoice<WicketFunction<String, String>> or by just setting the
contained function somewhere.truncate.setObject(str ->
str.substring(0, 20));

The whole approach is - if I am not mistaken - heavier on memory,
because instead of one IModel<String> we now have 5 or 6 Models, that
cannot be garbage collected, because they are all needed to perform
the computation.

This doesn't apply for mapping over an IColumn though, because it
doesn't need to rely on the laziness, like the ReadOnlyModel does
(otherwise a change in truncate wouldn't mean a change in the final
value), so:new LambdaColumn(of("Name"), Person::getAddress).
map(Address::getStreet).  map(Street::getName). orElse("n/a"));
would create 4 new instances of the LambdaColumn, but 3 of them could be gc'd more or less the second they were created - like I said, if I
am not mistaken. If this is at all an approach you think might be
useful, I would be happy to provide the implementation of the
LambdaColumn. Otherwise just ignore my message. :) Have a nice
day,Matthias


Reply via email to