On Sun, Apr 24, 2016 at 10:44 PM, Martin Grigorov <[email protected]> wrote:
> 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. > Now I think there is no need of #apply() at all. What use cases do you see for #apply() ? > > 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 >>>>>>>>>> >>>>>>>>> >>>> >>> >> >
