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