In Haskell land it would be the implementation of an Applicative. Now the apply 
is just like the #map-method, which would make them duplicates, if I am not 
mistaken. It could mainly be used to dynamically update the value contained 
inside the model. So if I have some sort of Model containing an implementation 
of a Function, I might do stuff like this:

WicketFunction<Integer, Integer> square = (x) -> x * x;


IModel<WicketFunction<Integer, Integer>> model = () -> (age) -> age + 10;

Person matthias = new Person("Matthias", 24);

IModel<Integer> matthiasAge = IModel.of(matthias).
  map(Person::getAge).
  apply(model);

matthiasAge.getObject() // => 34

model.setObject(square);
matthiasAge.getObject() // => 48


The same could be achieved in this way:
IModel<Integer> matthiasAge = IModel.of(matthias).
  map(Person::getAge).
  map(age -> model.getObject().apply(age));

I personally like the first one a little better, but that's probably down to 
opinion.




----- Ursprüngliche Message -----
Von: Martin Grigorov <[email protected]>
An: "[email protected]" <[email protected]>
CC: Matthias Metzger <[email protected]>
Gesendet: 22:44 Sonntag, 24.April 2016
Betreff: Re: Further Lambda Model ideas

I've removed IModel from the type of the parameter of #apply():
https://git1-us-west.apache.org/repos/asf?p=wicket.git;a=commitdiff;h=506ada45;hp=268bce97f173e5a2ee2f2f7d734a0c35c9ee960d
Please let me know if the IModel is really needed for something.

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


On Sun, Apr 24, 2016 at 10:34 PM, Martin Grigorov <[email protected]>
wrote:

> Unfortunately #flatMap() is still not what I'd like it to be.
>
> @Test
> public void flatMap()
> {
>    IModel<String> heirModel = IModel.of(person).flatMap(person1 -> new
> Model<String>() {
>       @Override
>       public String getObject()
>       {
>          return person1.getName() + " is my parent";
>       }
>    });
>    assertThat(heirModel.getObject(), is(equalTo("John is my parent")));
>
>    String newValue = "New Value";
>    heirModel.setObject(newValue);
>    assertThat(heirModel.getObject(), is(equalTo(newValue)));   // << THIS
> FAILS
> }
>
> the new value is set on a *fresh* instance of "new Model". I.e. it goes to
> /dev/null
>
> Martin Grigorov
> Wicket Training and Consulting
> https://twitter.com/mtgrigorov
>
> On Sun, Apr 24, 2016 at 9:14 PM, Martin Grigorov <[email protected]>
> wrote:
>
>>
>> On Sun, Apr 24, 2016 at 8:51 PM, Sven Meier <[email protected]> wrote:
>>
>>> Hi Martin,
>>>
>>> that's quite nice to have these methods in IModel actually.
>>>
>>> Java 8 default methods to the rescue :)
>>>
>>
>> The only method impl that bothers me is #flatMap().
>> I've made it non-lazy to be able to return the produced IModel<R>.
>> I have to see how other Monad impls do it.
>>
>>
>>>
>>> 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