CBStreams => AccelerateYour Functional Programming!
WHO AM I?
• Luis Majano
• Computer Engineer
• Born in El Salvador ->Texas
• CEO of Ortus Solutions
• Sandals -> ESRI -> Ortus
@lmajano
@ortussolutions
What are Java Streams
What is CBStreams
Imperative vs Functional Programming
Building Streams
Using Streams
Collecting Streams
What are Java Streams
• Introduced in JDK 8+
• Not I/O Streams
• A data abstraction layer
• Does not store any data, it wraps the data
• Designed to process streams of data elements
• map(), reduce(), filter(), collect()
• Enables functional-style operations on such elements
https://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html
What is CBStreams
• Port of Java 8+ Streams to CFML Land!
• 90% of all Java functionality is there
• Plus some CFML Dynamic Goodness
• Box Module (ColdBox, CommandBox, etc)
https://forgebox.io/view/cbstreams
install cbstreams
Imperative
VS
Functional
Programming
Imperative Programming
• Major OO languages are imperative (C,++,C#, Java)
• Follow a top-down or procedural design to reach a goal
• Each statement changes the state (side-effect) of the program
• Each statement tells the computer what to change and in what order
• Always cons and pros
function isPrime( number ) {
for( var i = 2; i <= sqr( number ); i++) {
if(number % i == 0) return false;
}
return number > 1;
}
isPrime(9220000000000000039) // Output: true
Functional Programming
• Declarative programming
• We tell the computer what things, actions, etc are
• Runtime determines the best way how to do it
• Functions are first class citizens
• No side-effect or iterating state to worry about
• Always cons and pros
function isPrime(number) {
return number > 1 &&
stream
.rangeClosed( 2, sqr( number ) )
.noneMatch( index => number % index == 0 );
}
isPrime( 9220000000000000039 ) // Output: true
Comparing Styles
Why?
Streams Functional Heaven!
• All about functional programming
• Heavy Lambda/Closure usage
• Must focus on the what and not on the how!
• Create a data processing pipeline
• Not for everything, choose wisely….
You have been warned!
Streams Functional Heaven!
var errors = [];
    var errorCount = 0;
    var oFile = fileOpen( filename );
    var thisLine = fileReadLine( oFile );
    while( errorCount < 40 && !isNull( thisLine ) ){
        if( line.startsWith( "ERROR" ) ){
            errors.append( line );
            errorCount++;
        }
        line = fileReadLine( oFile );
    }
var errors = streamBuilder.ofFile( filePath )
        .filter( line => line.startsWith( "ERROR" ) )
        .limit( 40 )
        .collect();
What if I
want to multi-
thread this?
.parallel()
What about CFML Functions?
• They are limited in input, scope & operations
• No short-circuiting operations
• No lazyness, they all fire top-down
• Each operation blocks until it finishes
processing ALL elements
• Creates new arrays/queries/structs for each
new concatenated operation
• What about infinite input or biiiiig files?
• map(), reduce(), each(), filter()
Element Stream
Stream Processing Pipeline
Lazy!
Stream Lazyness!
Lazy Example
var empIds = [ 1, 2, 3, 4 ];
var employee = streamBuilder.new( empIds )
// Convert ID's to Employee Objects, passing function reference
.map( employeeService.findByID )
// only valid employees
.filter( (employee) => !isNull( employee ) )
.filter( function( employee ){ return !isNull (employee); } )
// only salaries > 10000
.filter( (employee) => employee.getSalary() > 100000 )
// Find the first one
.findFirst()
// Return null
.orElse( null );
expect( employee.getSalary() ).toBe( 200000 );
• Stream performs the map and two filter operations, one element at a time.
• Since the salary of id 1 is not greater than 100000, the processing moves on to the next
element.
• Id 2 satisfies both of the filter predicates and hence the stream evaluates the terminal
operation findFirst() and returns the result.
• No operations are performed on id 3 and 4.
Let’s Get Started!
install cbstreams
StreamBuilder@cbstreams
• The StreamBuilder is injected where needed
• Helps you build streams out of native CFML data types
• Strings, Files, Arrays, Structs, Queries, Nulls
• Helps you build infinite or closure based streams
• You can strong type elements for the stream if needed
• For mathematical operations
• int, long, or double
Empty Streams
emptyStream = streamBuilder.new();
emptyStream = streamBuilder.new().empty();
• Simple way to build streams with no elements
• Useful? Maybe…
Building Custom Streams
builder = streamBuilder.builder();
myData.each( function( item ){
    builder.add( item );
} );
myStream = builder.build();
stream = streamBuilder.new()

.of( "a", "hello", "stream" );
stream = streamBuilder.new()

.of( argumentCollection=myData );
• Two approaches:
• builder() - Add your own data via the add() method
• Of( arguments ) -Via an array of arguments
Streams of Characters
stream = streamBuilder.new().ofChars( "Welcome to Streams" );
• Stream of string characters
• Great for parsing, lookups, etc.
File Streams
stream = streamBuilder.new().ofFile( absolutePath );
try{
    //work on the stream
} finally{
    stream.close();
}
• Non Blocking I/O Classes
• Stream of file lines
• Throw any file size to it, I dare ya!
Generate Infinite Streams
// Generate 100 random numbers
stream = streamBuilder.new().generate( function(){
return randRange( 1, 100 );
} ).limit( 100 );
// Seeded iteration
stream = streamBuilder.new().iterate( 40, function( x ){
return x + 2;
} ).limit( 20 );
• Infinite streams of data
• Start with a seed or no seeded results
• Make sure you limit them or wait forever….
Ranged Streams
stream = streamBuilder.new().range( 1, 200 );
stream = streamBuilder.new().rangeClosed( 1, 2030 );
• Create open or closed ranges
• Similar to of() but a whole less typing
Intermediate Operations
• Remember, they are lazy, nothing gets done until a terminator is called.
• Result is always a stream
Operation Description
limit( maxSize ) Limit the stream processing
distinct() Return only distinct elements
skip( n ) Skip from the first element to n
sorted( comparator ) Sort a stream using a compactor closure
unordered() Return an unordered stream (default)
onClose( closeHandler ) Attach a listener to when the close operation is called
concat( stream1, stream2 ) Concatenates two streams together
peek( action ) Allows you to peek on the element in the order is called
Map( mapper ) Transform the elements into something else
filter( predicate ) Returns a new stream containing only the requested elements
parallel() Convert the stream to a parallel multi-threaded stream
Terminal Operations
• They kick off processing of elements sequentially or in parallel
Operation Description
iterator() Returns a java iterator
spliterator() Returns a java spliterator
close() Close the stream
toArray() Convert the stream back into an array
count() Count the elements in the stream
forEach( action ) Iterate through the elements calling the action closure
forEachOrdered( action ) Iterate through the elements calling the action closure in order, even in parallel
reduce( accumulator, identity ) Fold, reduces the stream to a single element.
max( comparator ) Returns the max value in the stream, if a comparator is passed its called for you
min( comparator ) Returns the min value in the stream, if a comparator is passed its called for you
average( comparator ) Returns the avg value in the stream, if a comparator is passed its called for you
summaryStatistics() Gives you a struct of stats containing: { min, max, count, sum, average }
Short-Circuit Operations
• Also terminal, but can short-circuit processing of the stream
Operation Description
findAny() Find any element in the stream
findFirst() Find the first element in the stream
anyMatch( predicate ) Returns a boolean that indicates if any of the elements match the predicate closure
allMatch( predicate ) Returns a boolean that indicates if ALL of the elements match the predicate closure
noneMatch( predicate ) Returns a boolean that indicates if none of the elements match the predicate closure
Collectors
• Finalizes the stream by converting it to concrete collections
• CBStreams auto-converts Java -> CFML DataTypes
Operation Description
collect() Return an array of the final elements
collectGroupingBy( classifier )
Build a final collection according to the classifier lambda/closure that will
classify the keys in the group. End result is usually a struct of data
collectAverage( mapper, primitive=long )
Collect an average according to the mapper function/closure and data
type passed
collectSum( mapper, primitive=long )
Collect a sum according to the mapper function/closure and data type
passed
collectSummary( mapper, primitive=long )
Collect a statistics struct according to the mapper function and data type
passed
collectAsList( delimiter=“,”, prefix, suffix )
Collect results into a string list with a delimiter and attached prefix and/or
suffix.
collectAsStruct( keyId, valueID, overwrite=true )
Collect the elements into a struct by leveraging the key identifier and the
value identifier from the stream of elements to pass into the collection.
collectPartitioningBy( predicate )
partitions the input elements according to a Predicate closure/lambda, and
organizes them into a Struct of <Boolean, array >.
Lambda/Closure References
• CBStreams converts CFML Closures -> Java Lambdas
• Let’s investigate them by Java name:
// BiFunction, BinaryOperator
function( previous, item ){
return item;
}
// Comparator
function compare( o1, o2 ){
return -,+ or 0 for equal
}
// Consumer
void function( item ){
}
// Function, ToDoubleFunction, ToIntFunction,
ToLongFunction, UnaryOperator
function( item ){
return something;
}
// Predicate
boolean function( item ){
return false;
}
// Supplier
function(){
return something;
}
// Runnable
void function(){
// execute something
}
CBStreams Optionals
• Most return values are not the actual values but a CFML Optional
• Wraps a Java Optional
• Simple functional value container instead of doing null checks, with some cool
functions
Operation Description
isPresent() Returns boolean if value is present
ifPresent( consumer ) If value is present call the consumer closure for you
filter( predicate ) If a value is present and the value matches the predicate then return another
Optional :)
map( mapper ) If a value is present, apply the mapping function and return another Optional
get() Get the value!
orElse( other ) Get the value or the `other` if the value is null
orElseGet( other ) Get the value or if not present call the other closure to return a value
hashCode() Unique hash code of the value
toString() Debugging
Examples
myArray = [
    "ddd2",
    "aaa2",
    "bbb1",
    "aaa1",
    "bbb3",
    "ccc",
    "bbb2",
    "ddd1"
];
// Filtering
streamBuilder.new( myArray )
    .filter( function( item ){
        return item.startsWith( "a" );
    } )
    .forEach( function( item ){
        writedump( item );
    } );
Examples
// Sorted Stream
streamBuilder.new( myArray )
    .sorted()
    .filter( function( item ){
        return item.startsWith( "a" );
    } )
    .forEach( function( item ){
        writedump( item );
    } );
Examples
// Mapping
streamBuilder.new( myArray )
    .map( function( item ){
        return item.ucase();
    })
    .sorted( function( a, b ){
        return a.compareNoCase( b );
    }
    .forEach( function( item ){
        writedump( item );
    } );
Examples
// Partition stream to a struct of arrays according to even/odd
var isEven = streamBuilder.new( 2,4,5,6,8 )
.collectPartitioningBy( function(i){
return i % 2 == 0;
} );
expect( isEven[ "true" ].size() ).toBe( 4 );
expect( isEven[ "false" ].size() ).toBe( 1 );
Examples
// Group employees into character groups
component{
var groupByAlphabet = streamBuilder.of( employeeArray )
.collectGroupingBy( function( employee ){
return listFirst( employee.getlastName(), “” );
} );
expect( groupByAlphabet.get( 'B' ).get( 0 ).getName() )
.toBe( "Bill Gates" );
expect( groupByAlphabet.get( 'J' ).get( 0 ).getName() )
.toBe( "Jeff Bezos" );
expect( groupByAlphabet.get( 'M' ).get( 0 ).getName() )
.toBe( "Mark Zuckerberg" );
}
Examples
// Matching
anyStartsWithA =
    streamBuilder
        .new( myArray )
        .anyMatch( function( item ){
            return item.startsWith( "a" );
        } );
writeDump( anyStartsWithA ); // true
allStartsWithA =
    streamBuilder
        .new( myArray )
        .allMatch( function( item ){
            return item.startsWith( "a" );
        } );
writeDump( anyStartsWithA ); // false
Examples
noneStartsWithZ =
    streamBuilder
        .new( myArray )
        .noneMatch((s) -> s.startsWith("z"));
noneStartsWithZ =
    streamBuilder
        .new( myArray )
        .noneMatch( function( item ){
            return item.startsWith( "z" );
        } );
writeDump( noneStartsWithZ ); // true
Examples
// Reduce
optional =
    streamBuilder
        .new( myArray )
        .sorted()
        .reduce( function( s1, s2 ){
            return s1 & "#" & s2;
        } );
writedump( optional.get() );
Examples
// Parallel Sorted Count
count =
    streamBuilder
        .new( myArray )
        .parallel()
        .sorted()
        .count();
Still in infancy
Implement JDK 9-10 features
CFML Query Support
ORM Integration
ColdBox Core
Reactive Streams
Help me: 

Lucee/Adobe Lambda -> Java lambda
Roadmap
QUESTIONS?
Go Build Some Streams!!
www.ortussolutions.com
@ortussolutions

CBStreams - Java Streams for ColdFusion (CFML)

  • 2.
    CBStreams => AccelerateYourFunctional Programming!
  • 3.
    WHO AM I? •Luis Majano • Computer Engineer • Born in El Salvador ->Texas • CEO of Ortus Solutions • Sandals -> ESRI -> Ortus @lmajano @ortussolutions
  • 4.
    What are JavaStreams What is CBStreams Imperative vs Functional Programming Building Streams Using Streams Collecting Streams
  • 5.
    What are JavaStreams • Introduced in JDK 8+ • Not I/O Streams • A data abstraction layer • Does not store any data, it wraps the data • Designed to process streams of data elements • map(), reduce(), filter(), collect() • Enables functional-style operations on such elements https://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html
  • 6.
    What is CBStreams •Port of Java 8+ Streams to CFML Land! • 90% of all Java functionality is there • Plus some CFML Dynamic Goodness • Box Module (ColdBox, CommandBox, etc) https://forgebox.io/view/cbstreams install cbstreams
  • 7.
  • 8.
    Imperative Programming • MajorOO languages are imperative (C,++,C#, Java) • Follow a top-down or procedural design to reach a goal • Each statement changes the state (side-effect) of the program • Each statement tells the computer what to change and in what order • Always cons and pros function isPrime( number ) { for( var i = 2; i <= sqr( number ); i++) { if(number % i == 0) return false; } return number > 1; } isPrime(9220000000000000039) // Output: true
  • 9.
    Functional Programming • Declarativeprogramming • We tell the computer what things, actions, etc are • Runtime determines the best way how to do it • Functions are first class citizens • No side-effect or iterating state to worry about • Always cons and pros function isPrime(number) { return number > 1 && stream .rangeClosed( 2, sqr( number ) ) .noneMatch( index => number % index == 0 ); } isPrime( 9220000000000000039 ) // Output: true
  • 10.
  • 11.
  • 12.
    Streams Functional Heaven! •All about functional programming • Heavy Lambda/Closure usage • Must focus on the what and not on the how! • Create a data processing pipeline • Not for everything, choose wisely…. You have been warned!
  • 13.
    Streams Functional Heaven! varerrors = [];     var errorCount = 0;     var oFile = fileOpen( filename );     var thisLine = fileReadLine( oFile );     while( errorCount < 40 && !isNull( thisLine ) ){         if( line.startsWith( "ERROR" ) ){             errors.append( line );             errorCount++;         }         line = fileReadLine( oFile );     } var errors = streamBuilder.ofFile( filePath )         .filter( line => line.startsWith( "ERROR" ) )         .limit( 40 )         .collect(); What if I want to multi- thread this? .parallel()
  • 14.
    What about CFMLFunctions? • They are limited in input, scope & operations • No short-circuiting operations • No lazyness, they all fire top-down • Each operation blocks until it finishes processing ALL elements • Creates new arrays/queries/structs for each new concatenated operation • What about infinite input or biiiiig files? • map(), reduce(), each(), filter()
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
    Lazy Example var empIds= [ 1, 2, 3, 4 ]; var employee = streamBuilder.new( empIds ) // Convert ID's to Employee Objects, passing function reference .map( employeeService.findByID ) // only valid employees .filter( (employee) => !isNull( employee ) ) .filter( function( employee ){ return !isNull (employee); } ) // only salaries > 10000 .filter( (employee) => employee.getSalary() > 100000 ) // Find the first one .findFirst() // Return null .orElse( null ); expect( employee.getSalary() ).toBe( 200000 ); • Stream performs the map and two filter operations, one element at a time. • Since the salary of id 1 is not greater than 100000, the processing moves on to the next element. • Id 2 satisfies both of the filter predicates and hence the stream evaluates the terminal operation findFirst() and returns the result. • No operations are performed on id 3 and 4.
  • 20.
    Let’s Get Started! installcbstreams StreamBuilder@cbstreams • The StreamBuilder is injected where needed • Helps you build streams out of native CFML data types • Strings, Files, Arrays, Structs, Queries, Nulls • Helps you build infinite or closure based streams • You can strong type elements for the stream if needed • For mathematical operations • int, long, or double
  • 21.
    Empty Streams emptyStream =streamBuilder.new(); emptyStream = streamBuilder.new().empty(); • Simple way to build streams with no elements • Useful? Maybe…
  • 22.
    Building Custom Streams builder= streamBuilder.builder(); myData.each( function( item ){     builder.add( item ); } ); myStream = builder.build(); stream = streamBuilder.new()
 .of( "a", "hello", "stream" ); stream = streamBuilder.new()
 .of( argumentCollection=myData ); • Two approaches: • builder() - Add your own data via the add() method • Of( arguments ) -Via an array of arguments
  • 23.
    Streams of Characters stream= streamBuilder.new().ofChars( "Welcome to Streams" ); • Stream of string characters • Great for parsing, lookups, etc.
  • 24.
    File Streams stream =streamBuilder.new().ofFile( absolutePath ); try{     //work on the stream } finally{     stream.close(); } • Non Blocking I/O Classes • Stream of file lines • Throw any file size to it, I dare ya!
  • 25.
    Generate Infinite Streams //Generate 100 random numbers stream = streamBuilder.new().generate( function(){ return randRange( 1, 100 ); } ).limit( 100 ); // Seeded iteration stream = streamBuilder.new().iterate( 40, function( x ){ return x + 2; } ).limit( 20 ); • Infinite streams of data • Start with a seed or no seeded results • Make sure you limit them or wait forever….
  • 26.
    Ranged Streams stream =streamBuilder.new().range( 1, 200 ); stream = streamBuilder.new().rangeClosed( 1, 2030 ); • Create open or closed ranges • Similar to of() but a whole less typing
  • 27.
    Intermediate Operations • Remember,they are lazy, nothing gets done until a terminator is called. • Result is always a stream Operation Description limit( maxSize ) Limit the stream processing distinct() Return only distinct elements skip( n ) Skip from the first element to n sorted( comparator ) Sort a stream using a compactor closure unordered() Return an unordered stream (default) onClose( closeHandler ) Attach a listener to when the close operation is called concat( stream1, stream2 ) Concatenates two streams together peek( action ) Allows you to peek on the element in the order is called Map( mapper ) Transform the elements into something else filter( predicate ) Returns a new stream containing only the requested elements parallel() Convert the stream to a parallel multi-threaded stream
  • 28.
    Terminal Operations • Theykick off processing of elements sequentially or in parallel Operation Description iterator() Returns a java iterator spliterator() Returns a java spliterator close() Close the stream toArray() Convert the stream back into an array count() Count the elements in the stream forEach( action ) Iterate through the elements calling the action closure forEachOrdered( action ) Iterate through the elements calling the action closure in order, even in parallel reduce( accumulator, identity ) Fold, reduces the stream to a single element. max( comparator ) Returns the max value in the stream, if a comparator is passed its called for you min( comparator ) Returns the min value in the stream, if a comparator is passed its called for you average( comparator ) Returns the avg value in the stream, if a comparator is passed its called for you summaryStatistics() Gives you a struct of stats containing: { min, max, count, sum, average }
  • 29.
    Short-Circuit Operations • Alsoterminal, but can short-circuit processing of the stream Operation Description findAny() Find any element in the stream findFirst() Find the first element in the stream anyMatch( predicate ) Returns a boolean that indicates if any of the elements match the predicate closure allMatch( predicate ) Returns a boolean that indicates if ALL of the elements match the predicate closure noneMatch( predicate ) Returns a boolean that indicates if none of the elements match the predicate closure
  • 30.
    Collectors • Finalizes thestream by converting it to concrete collections • CBStreams auto-converts Java -> CFML DataTypes Operation Description collect() Return an array of the final elements collectGroupingBy( classifier ) Build a final collection according to the classifier lambda/closure that will classify the keys in the group. End result is usually a struct of data collectAverage( mapper, primitive=long ) Collect an average according to the mapper function/closure and data type passed collectSum( mapper, primitive=long ) Collect a sum according to the mapper function/closure and data type passed collectSummary( mapper, primitive=long ) Collect a statistics struct according to the mapper function and data type passed collectAsList( delimiter=“,”, prefix, suffix ) Collect results into a string list with a delimiter and attached prefix and/or suffix. collectAsStruct( keyId, valueID, overwrite=true ) Collect the elements into a struct by leveraging the key identifier and the value identifier from the stream of elements to pass into the collection. collectPartitioningBy( predicate ) partitions the input elements according to a Predicate closure/lambda, and organizes them into a Struct of <Boolean, array >.
  • 31.
    Lambda/Closure References • CBStreamsconverts CFML Closures -> Java Lambdas • Let’s investigate them by Java name: // BiFunction, BinaryOperator function( previous, item ){ return item; } // Comparator function compare( o1, o2 ){ return -,+ or 0 for equal } // Consumer void function( item ){ } // Function, ToDoubleFunction, ToIntFunction, ToLongFunction, UnaryOperator function( item ){ return something; } // Predicate boolean function( item ){ return false; } // Supplier function(){ return something; } // Runnable void function(){ // execute something }
  • 32.
    CBStreams Optionals • Mostreturn values are not the actual values but a CFML Optional • Wraps a Java Optional • Simple functional value container instead of doing null checks, with some cool functions Operation Description isPresent() Returns boolean if value is present ifPresent( consumer ) If value is present call the consumer closure for you filter( predicate ) If a value is present and the value matches the predicate then return another Optional :) map( mapper ) If a value is present, apply the mapping function and return another Optional get() Get the value! orElse( other ) Get the value or the `other` if the value is null orElseGet( other ) Get the value or if not present call the other closure to return a value hashCode() Unique hash code of the value toString() Debugging
  • 33.
    Examples myArray = [     "ddd2",     "aaa2",     "bbb1",     "aaa1",     "bbb3",     "ccc",     "bbb2",     "ddd1" ]; //Filtering streamBuilder.new( myArray )     .filter( function( item ){         return item.startsWith( "a" );     } )     .forEach( function( item ){         writedump( item );     } );
  • 34.
    Examples // Sorted Stream streamBuilder.new(myArray )     .sorted()     .filter( function( item ){         return item.startsWith( "a" );     } )     .forEach( function( item ){         writedump( item );     } );
  • 35.
    Examples // Mapping streamBuilder.new( myArray)     .map( function( item ){         return item.ucase();     })     .sorted( function( a, b ){         return a.compareNoCase( b );     }     .forEach( function( item ){         writedump( item );     } );
  • 36.
    Examples // Partition streamto a struct of arrays according to even/odd var isEven = streamBuilder.new( 2,4,5,6,8 ) .collectPartitioningBy( function(i){ return i % 2 == 0; } ); expect( isEven[ "true" ].size() ).toBe( 4 ); expect( isEven[ "false" ].size() ).toBe( 1 );
  • 37.
    Examples // Group employeesinto character groups component{ var groupByAlphabet = streamBuilder.of( employeeArray ) .collectGroupingBy( function( employee ){ return listFirst( employee.getlastName(), “” ); } ); expect( groupByAlphabet.get( 'B' ).get( 0 ).getName() ) .toBe( "Bill Gates" ); expect( groupByAlphabet.get( 'J' ).get( 0 ).getName() ) .toBe( "Jeff Bezos" ); expect( groupByAlphabet.get( 'M' ).get( 0 ).getName() ) .toBe( "Mark Zuckerberg" ); }
  • 38.
    Examples // Matching anyStartsWithA =     streamBuilder         .new(myArray )         .anyMatch( function( item ){             return item.startsWith( "a" );         } ); writeDump( anyStartsWithA ); // true allStartsWithA =     streamBuilder         .new( myArray )         .allMatch( function( item ){             return item.startsWith( "a" );         } ); writeDump( anyStartsWithA ); // false
  • 39.
    Examples noneStartsWithZ =     streamBuilder         .new( myArray)         .noneMatch((s) -> s.startsWith("z")); noneStartsWithZ =     streamBuilder         .new( myArray )         .noneMatch( function( item ){             return item.startsWith( "z" );         } ); writeDump( noneStartsWithZ ); // true
  • 40.
    Examples // Reduce optional =     streamBuilder         .new(myArray )         .sorted()         .reduce( function( s1, s2 ){             return s1 & "#" & s2;         } ); writedump( optional.get() );
  • 41.
    Examples // Parallel SortedCount count =     streamBuilder         .new( myArray )         .parallel()         .sorted()         .count();
  • 42.
    Still in infancy ImplementJDK 9-10 features CFML Query Support ORM Integration ColdBox Core Reactive Streams Help me: 
 Lucee/Adobe Lambda -> Java lambda Roadmap
  • 43.
    QUESTIONS? Go Build SomeStreams!! www.ortussolutions.com @ortussolutions