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 >>>>>>> >>>>>> >
