On Sun, Apr 24, 2016 at 9:02 PM, Sven Meier <[email protected]> wrote: > Hi, > > a few notes: > > - IModel#of(WicketSupplier) is superfluous, since you can use the lambda > directly as model instead >
agreed! > - IModel#of(IModel) makes no sense to me > The only reason to keep it there is that ChainingModel supports both Object and IModel as a target. Also Model class has #of(IModel<?>) So I wanted to keep it consistent. But I agree that it doesn't help much. If we see a need later we can easily add it. > - Model#of(WicketSuppler, WicketConsumer) is a duplicate of > LambdaModel#of(WicketSuppler, WicketConsumer), IMHO we should remove the > former > What about moving it to Lambdas class ? > - Model#loadableDetachable(WicketSupplier) could be moved to > LoadableDetachableModel > Agreed! And to Lambdas ?! > > 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 >>>>>>>> >>>>>>> >> >
