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. :)
----- 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 >>>>>>>>> >>>>>>>> >>> >> >
