Thanks, Sven! I'll take a look at the suggested improvements to LambdaColumn tonight/tomorrow unless you/someone beat me.
Martin Grigorov Wicket Training and Consulting https://twitter.com/mtgrigorov On Mon, Apr 25, 2016 at 9:56 AM, Sven Meier <[email protected]> wrote: > Hi, > > to make things consistent I've moved around some methods: > > - Model#loadableDetachable(Supplier) -> now: > LoadableDetachableModel#of(Supplier) > - Model#of(Supplier,Consumer) -> already present as: > LambdaModel#of(Supplier,Consumer) > - IModel#of(T) -> already present as: Model.of(T) > - Model#of(IModel<?>) -> now: IModel#of(IModel<?>) > > Furthermore I've noticed some more duplications. > The following two solutions are identical: > > IModel<Person> target = Model.of(new Person()); > IModel<String> personNameModel1 = LambdaModel.of(target, > Person::getName); > IModel<String> personNameModel2 = target.map(Person::getName); > > We should discuss whether we want to remove the former (personNameModel1). > > Have fun > Sven > > > > > On 24.04.2016 23:17, Martin Grigorov wrote: > >> On Sun, Apr 24, 2016 at 11:13 PM, Matthias Metzger < >> [email protected]> wrote: >> >> And it would actually reduce the API, since we can achieve the same using >>> #map, so in my opinion you could probably throw it out entirely. :) >>> >>> I'd prefer to remove it for now. >> If we see a need we could re-add it later. >> Thank you for all the help! >> >> >> >>> >>> >>> ----- Ursprüngliche Message ----- >>> Von: Matthias Metzger <[email protected]> >>> An: "[email protected]" <[email protected]> >>> Gesendet: 23:10 Sonntag, 24.April 2016 >>> Betreff: Re: Further Lambda Model ideas >>> >>> 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 >>>>>>>>>>>> >>>>>>>>>>>> >
