London Lambdas Hackday: Developer Practices
Howard Lovatt
howard.lovatt at gmail.com
Wed Jul 4 17:28:13 PDT 2012
Hi François,
First a bit of background. I have been using my own
parallel-lazy-functional-List class for a number of years. Surprisingly it
is very similar to the lambdafied collection library and therefore I think
many of my experiences over the past few years are relevant.
In my library when you call toString, which the debugger does, it evaluates
the list - you loose the laziness. It would be possible to write a toString
that retained the laziness by cloning the list, which in my library is
lazy, and applying an evaluating toString to the clone and maybe that
should be done - I haven't checked what JDK 8 does.
Debugging with separate print statements, not using tee, has the same
problem, it calls toString and everything is evaluated.
As a general rule I find debugging with a debugger better than with print
because I don't have to decide in advance what I should look at. You
certainly can debug with print and that may well become more common with
lambdas - I was hoping for an improved debugger though! But your post
brings up another point, should toString retain laziness?
Cheers,
-- Howard.
PS In my library I use a Tuple as an optional type and flatmap, though it
is called flatrecycle, to achieve monad functionality in a manner similar
to that done with Scala. I haven't used this for debugging, so I would
interested in your option 3.
On 4 July 2012 18:04, François Sarradin <fsarradin at gmail.com> wrote:
> Oops! There might be a drawback with solution 2: once you try to debug
> what is supposed to be lazily evaluate, you risk to get it eagerly and then
> lose data for the rest of the chaining.
>
> Francois
> Le 4 juil. 2012 08:35, "François Sarradin" <fsarradin at gmail.com> a
> écrit :
>
>> Howard,
>>
>> Debugging method/function chaining is a recurrent problem in functional
>> programming. In a sense, the way you use chaining in FP should help you to
>> have a better understanding of the code. But it is not always the case.
>>
>> Nevertheless, you can use two workarounds in this situation. 1/ As Brian
>> said, insert a print call in the chaining, but you need the tee method and
>> you cannot use this with a debugger. 2/ You can put part of the chaining in
>> a variable and continue the rest of the chaining from this variable. Like
>> this:
>>
>> Iterable<Whatever> iter = myList.a().b();
>> iter.c().d();
>>
>> In this case, you have to transform the code to observe it in a debugger.
>> This is only possible if you have a write access to the code.
>>
>> I think there is a third possibility. But I have to introduce monads ;)
>> And even with Java 8, it is hard to use them.
>>
>> Francois
>> Le 4 juil. 2012 07:50, "Howard Lovatt" <howard.lovatt at gmail.com> a
>> écrit :
>>
>>> Re. Chaining
>>>
>>> I personally like the chaining style (particularly with the dots lined
>>> up).
>>> However, I have found it a pain with the Netbeans debugger because you
>>> can't see the intermediate results. This has often caused me to introduce
>>> variables for all the steps and to return the final array separately (so
>>> I
>>> can see what is happening).
>>>
>>> Any idea if IDEs and the underlying connection to the JVM will be updated
>>> to support seeing intermediates?
>>>
>>> On 4 July 2012 01:13, Brian Goetz <brian.goetz at oracle.com> wrote:
>>>
>>> > > 1. addAll vs into
>>> > >
>>> > > A developer had an interable that he wanted to put all the values
>>> from
>>> > > into a collection. He tried to use addAll, but it works over a
>>> > > collection, not an iterable. He could have used into and flipped
>>> > > dispatch object with the argument, but it wasn't obvious to do that.
>>> > > This might be just a case of 'old habits die hard' but it might also
>>> > > be
>>> >
>>> > Whatever we do here will change when streams are more Iterator-like and
>>> > less Iterable-like. (This is a big theme for the next round of API
>>> > design, currently in progress in a branch, which should be rolled out
>>> > hopefully soon.) Would a Stream.concat(otherStream) method do what you
>>> > were thinking of?
>>> >
>>> > > 3. Some feedback from Martijn:
>>> > >
>>> > > I noticed that I happily started using a chaining idiom in my code,
>>> e.g.
>>> > >
>>> > > return students.filter(student -> student.getAge() > 18).into(new
>>> > > ArrayList<>());
>>> > >
>>> > > Given my lack of recent functional programming expertise, I think
>>> this
>>> > > is a good thing. But I'm a little concerned that the chaining idiom
>>> > > becomes hard to read for non-functional programmers, some education
>>> > > might be required. Perhaps in the educational material, samples are
>>> > > laid out like so (each 'chained' method is lined up with the method
>>> > > before it):
>>> > >
>>> > > return students.filter(student -> student.getAge() > 18)
>>> > > .into(new ArrayList<>());
>>> >
>>> > Absolutely. In all our sample code, we use one method per line, with
>>> > the dots lining up. IDEs mostly support this.
>>> >
>>> > > You might also find Java developers wanting to pull everything back
>>> > > into a local variable, e.g.:
>>> > >
>>> > > List<Student> filteredStudents = new ArrayList<>()
>>> > > return students.filter(student -> student.getAge() >
>>> > 18).into(filteredStudents);
>>> >
>>> > This is a common first impulse of Java developers -- give everything a
>>> > name -- but will probably be overused until people get comfortable with
>>> > chaining. A major design goal for these libraries has been to enable
>>> > getting rid of the "garbage intermediate variables". The farther
>>> > between their declaration and use (say, if the chain in the above
>>> > example were longer), the more problematic the garbage variables
>>> become.
>>> >
>>> > > Another example pulling the Predicate in locally:
>>> > >
>>> > > Predicate<Student> isLegal = (Student student) -> student.getAge() >
>>> 18;
>>> > > return students.filter(isLegal).into(new ArrayList<Student>());
>>> >
>>> > If people want to do this for readability or reuse, that's fine. (If
>>> > they think there is any performance benefit to it, they're mistaken;
>>> > stateless lambda captures are optimized into constant loads.)
>>> >
>>> > > One other (unecessary?) idiom they might use is assigning back to a
>>> > > collection that is the result of the filter/reduce/map/whatever,
>>> e.g.:
>>> > >
>>> > > List<Student> legalStudents = new ArrayList<>();
>>> > > Predicate<Student> isLegal = (Student student) -> student.getAge() >
>>> 18;
>>> > > legalStudents = students.filter(isLegal).into(legalStudents);
>>> > > return legalStudents;
>>> > > // as opposed to
>>> > > // return students.filter(isLegal).into(legalStudents);
>>> >
>>> > Right. These will be hard habits for people to break.
>>> >
>>> > > 4. Type inference
>>> > >
>>> > > IDE or compiler support might be needed to teach developers that Java
>>> > > is smarter than they think with regards to Type inference. For
>>> > > example a refactor to do the following:
>>> > >
>>> > > // Predicate<Student> isAdult = (Student student) ->
>>> student.getAge() >
>>> > 18;
>>> > > // Can be written as:
>>> > > Predicate<Student> isAdult = student -> student.getAge() > 18;
>>> > >
>>> > > Perhaps a -lint option on javac for this kind of thing would be
>>> > > useful? I guess it ultimately depends on how the expected idiom for
>>> > > writing this code works.
>>> >
>>> > I expect IDEs will auto-suggest for this.
>>> >
>>> > > 5. Function Composition
>>> > >
>>> > > There was a genuine desire from people to have function composition.
>>> > > This was somewhat driven by people with experience of functional
>>> > > programming in other languages, but there were still scenarios where
>>> > > it seemed like the cleanest idiom. The lack of function types seems
>>> > > to make it hard to introduce function composition in the general
>>> case,
>>> > > but is there an expectation that composition methods will be provided
>>> > > for all the functional core library interfaces?
>>> >
>>> > There is some very limited type-specific composition, such as
>>> > Mapper.compose or Predicate.{and,or}. But I suspect whatever we might
>>> > do in the area of general-purpose composition would be deeply
>>> > disappointing to functies. So we're unlikely to do much here.
>>> >
>>> >
>>> >
>>> >
>>> >
>>>
>>>
>>> --
>>> -- Howard.
>>>
>>>
--
-- Howard.
More information about the lambda-dev
mailing list