London Lambdas Hackday: Developer Practices

Brian Goetz brian.goetz at oracle.com
Wed Jul 4 19:21:10 PDT 2012


The idea that the list exists in "lazy" or "eager" mode is simply not 
part of the model; we are not adding any sort of lazy collections. 
Lists (and other things) can be sources for streams, some of whose 
operations consume their contents eagerly (e.g., reduce) and some don't 
(e.g., filter).  But the list itself is never lazy.

This will get clearer in the next round of API changes, where a Stream 
is much more like an Iterator than an Iterable.

On 7/4/2012 8:28 PM, Howard Lovatt wrote:
> 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
> <mailto: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
>     <mailto: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
>         <mailto: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
>             <mailto: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