London Lambdas Hackday: Developer Practices

François Sarradin fsarradin at gmail.com
Wed Jul 4 23:43:14 PDT 2012


Howard,

In fact, I didn't think to use an Optional type here. The writer monad
would a better candidate. By definition, this monad helps you to aggregate
data among a succession of operations. It is generally used to introduce a
logging system in FP.

To use this monad, you have to transform all of your operations in the
chaining to be adapted to the monad. You also have to transform the
succession of method calls into a recursive lambda calls.

I guess it is overkill in your situation.

François
 Le 5 juil. 2012 02:28, "Howard Lovatt" <howard.lovatt at gmail.com> a écrit :

> 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