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