Hi, wow, thank you for the consideration, even just for playing around!
> 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. The only concern I have with this is, that it is not immediately obvious, that a call to #map() on any impl will result in a RuntimeException, when calling #setObject() on the resulting model. But that would probably just be a documentation effort. Besides that, I really like the methods in IModel. > If the application developer wants a read-write model then (s)he has to use > #flatMap() and return a writeable model. Ha! Funny, I hadn't considered using flatMap to make the Model 'writeable' again. Regards, Matthias ----- Ursprüngliche Message ----- Von: Martin Grigorov <[email protected]> An: "[email protected]" <[email protected]>; Matthias Metzger <[email protected]> Gesendet: 17:20 Sonntag, 24.April 2016 Betreff: Re: Further Lambda Model ideas 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 > >> >
