Additional brackets in Java8 stream map() function - java-8

I'm working/testing streams in Java8 and come across very frustrating issue.
I've got the code which compiles well:
List<String> words = Arrays.asList("Oracle", "Java", "Magazine");
List<String> wordLengths = words.stream().map((x) -> x.toUpperCase())
.collect(Collectors.toList());
And second one (nearly the same) which throw a warnings:
List<String> words = Arrays.asList("Oracle", "Java", "Magazine");
List<String> wordLengths = words.stream().map((x) -> {
x.toUpperCase();
}).collect(Collectors.toList());
Warning:
The method map(Function<? super String,? extends R>) in the type Stream<String> is not applicable for the arguments ((<no type> x) -> {})
What does this additional brackets have changed?

Your lambda expression returns a value. If you use brackets you need to add a return statement to your lambda function:
List<String> words = Arrays.asList("Oracle", "Java", "Magazine");
List<String> wordLengths = words.stream().map((x) -> {
return x.toUpperCase();
}).collect(Collectors.toList());

According to the official Oracle tutorial
A lambda expression consists of the following:
A comma-separated list of formal parameters enclosed in parentheses.
The CheckPerson.test method contains one parameter, p, which
represents an instance of the Person class.
Note: You can omit the data type of the parameters in a lambda
expression. In addition, you can omit the parentheses if there is only
one parameter. For example, the following lambda expression is also
valid:
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
The arrow token, ->
A body, which consists of a single expression or a statement block.
This example uses the following expression:
p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
If you specify a single expression, then the Java runtime evaluates
the expression and then returns its value. Alternatively, you can use
a return statement:
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
A return statement is not an expression; in a lambda expression, you
must enclose statements in braces ({}). However, you do not have to
enclose a void method invocation in braces. For example, the following
is a valid lambda expression:
email -> System.out.println(email)
Since there is only one parameter in the provided lambda expression (x) -> x.toUpperCase() we can omit the parentheses: x -> x.toUpperCase(). String#toUpperCase returns a new String so there is no need to use return statement and braces. If instead we had a complex block with return statements we would have to enclose it into braces. Moreover in this case it is better to use Method Reference String::toUpperCase
List<String> wordLengths = words.stream().map(String::toUpperCase).collect(Collectors.toList());

Related

Composing F# code quotations programmatically

The following code is extracted from an application and adapted to highlight the issue as easy as possible
module Mo
open System
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Linq.RuntimeHelpers
open System.Linq.Expressions
type Type() =
member _.Prop1 with get() = 1
member _.Prop2 with get() = 2
let toFunc<'t when 't :> Type>(filter: 't -> Expr<bool>) =
let xp = <# Func<'t, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) #>
LeafExpressionConverter.QuotationToExpression xp |> unbox<Expression<Func<'t, bool>>>
let getFunc (i: int) =
let filter (t: Type) = <# t.Prop1 = i #>
toFunc filter
the problem is in the line
let xp = <# Func< 't, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) #>
The compiler complains in fun (t: 't) as follows:
Error FS0446
The variable 't' is bound in a quotation but is used as part of a spliced expression.
This is not permitted since it may escape its scope.
The intent is to compose quotations into a filter Linq expression.
Is there a (alternative) way to do this?
EDIT:
Some more context looks necessary:
The returned Func expression is later passed to Azure.Data.Tables.TableClient.Query(). Unfortunately this method doesn't support expressions containing function calls.
Converting filter to a quoted function (as suggested by Fyodor) was my first version, but I had to abandon it because of this requirement of the Azure Tables SDK.
So the question becomes :
Is it possible to achieve the result of an expression that doesn't contain calls to external method/function?
You're mixing up your variables between quotation realm and "regular" realm.
filter is not a quoted function. It's a regular function that returns a quotation. Regular functions get regular parameters, quoted functions get quoted parameters.
The t parameter here:
let xp = <# Func<'t, bool>(fun (t: 't) -> %(filter t) && t.Prop2 = 2) #>
^
|
This one
That's a quoted parameter. It's defined inside a quotation.
And yet, you're trying to pass its value to the filter function. But at the moment of constructing the quotation, the parameter t doesn't have a value yet! Only when you're done constructing the quotation, then compile it to IL, and then call it, - only then will the parameter t have a value, allowing you to call the filter function. But you need the result of filter function to finish constructing the quotation in the first place!
The most straightforward fix is to make filter a quoted function. Then you can splice it into the quotation, and then pass the parameter t to the result of splicing:
let toFunc<'t when 't :> Type>(filter: Expr<'t -> bool>) =
let xp = <# Func<'t, bool>(fun (t: 't) -> (%filter) t && t.Prop2 = 2) #>
LeafExpressionConverter.QuotationToExpression xp |> unbox<Expression<Func<'t, bool>>>
let getFunc (i: int) =
let filter = <# fun (t: Type) -> t.Prop1 = i #>
toFunc filter

Joining Strings via stream

I am trying to join a list of names:
List<String> names;
names = books.stream()
.map( b -> b.getName() )
.filter( n -> ( (n != null) && (!n.isEmpty()) ) )
.collect(Collectors.joining(", "));
This does not compile saying:
incompatible types. inference variable R has incompatible bounds
So after some research, it seems that there is something I misunderstood. I thought that .map( b -> b.getName() ) returned/changed the type to a String, and it seems something is wrong there. If I use .map(Book::getName) instead, I still get an error, but I probably don't fully understand the difference.
However, this does not complain:
List<String> names;
names = books.stream()
.map( b -> b.getName() )
.map( Book::getName )
.filter( n -> ( (n != null) && (!n.isEmpty()) ) )
.collect(Collectors.joining(", "));
Can someone explain me why? Some didactic explanation about differences between .map( b -> b.getName() ) and .map(Book::getName) are appreciated too, since I think I didn't get it right.
The joining(", ") collector will collect and join all Strings into a single string using the given separator. The returning type of collect in this case is String, but you are trying to assign the result to a List. If you want to collect Strings into a List, use Collectors.toList().
If you have a collection with Book instances, then it will be enough to map a stream of Books to a stream of Strings once.
Difference between lamdba & method refrence
A lamdba expression may be written as a block, containing multiple operations:
b -> {
// here you can have other operations
return b.getName();
}
if lambda has single operation, it can be shortened:
b -> b.getName()
Method reference is just a "shortcut" for a lambda with a single operation. This way:
b -> b.getName()
can be replaced with:
Book::getName
but if you have a lambda like this:
b -> b.getName().toLowerCase()
you cant use a reference to the getName method, because you are making and additional call to toLowerCase().
If you are using Collectors.joining(), the result will be a single concatenated String:
String names = books.stream()
.map( b -> b.getName() )
.filter(n -> (n != null) && !n.isEmpty())
.collect(Collectors.joining(", "));
The Collectors.toList() is the one that returns a List:
List<String> namesList = books.stream()
.map( b -> b.getName() )
.filter(n -> (n != null) && !n.isEmpty())
.collect(Collectors.toList());
Book::getName is a method reference and will have the same result as b -> b.getName(). Method reference is clearer and enables to pass other existing methods as a parameter to methods such as map(), as long as the passed method conforms to the signature of the expected functional interface. In this case, map() expects an instance of the Function interface. Thus, you can give any reference to a method that conforms to the signature of the abstract R apply(T t) method from such an interface.
Since you are mapping a Book to a String, the actual signature for the method to be given to the map() must be String apply(Book t). This can be read as "receive a Book and return a String". This way, any method you pass that conforms to this definition is valid. When you pass a method reference Book::getName, the getName method itself doesn't conform to the signature presented above (because it has no parameter at all), but it conforms to the definition of such a signature: you pass a book and return a String from its name.
Thus, consider that, inside the class where you have your book list, you also have a method which performs any operation over a Book, returning a String. The method below is an example that receives a Book and gets the first 10 characters from its name:
public String getReducedBookName(Book b){
if(b.getName() == null)
return "";
String name = b.getName();
return name.substring(0, name.length() > 10 ? 10 : name.length());
}
You can also pass this method (which is not inside the Book class) as parameter to the map() method:
String names = books.stream()
.map(this::getReducedBookName)
.filter(n -> !n.isEmpty())
.collect(Collectors.joining(", "));
if you prefer mapping over map
as String
String names = books.stream().collect(mapping(Book::getName,
filtering(s -> s != null && ! s.isBlank(),
joining(", "))));
as List
List<String> names = books.stream().collect(mapping(Book::getName,
filtering(s -> s != null && ! s.isBlank(),
toList())));

Are semicolons optional in Rust?

Since semicolons are apparently optional in Rust, why, if I do this:
fn fn1() -> i32 {
let a = 1
let b = 2
3
}
I get the error:
error: expected one of `.`, `;`, `?`, or an operator, found `let`
--> src/main.rs:3:9
|
2 | let a = 1
| - expected one of `.`, `;`, `?`, or an operator here
3 | let b = 2
| ^^^ unexpected token
They're not optional. Semicolons modify the behaviour of an expression statement so it should be a conscious decision whether you use them or not for a line of code.
Almost everything in Rust is an expression. An expression is something that returns a value. If you put a semicolon you are suppressing the result of this expression, which in most cases is what you want.
On the other hand, this means that if you end your function with an expression without a semicolon, the result of this last expression will be returned. The same can be applied for a block in a match statement.
You can use expressions without semicolons anywhere else a value is expected.
For example:
let a = {
let inner = 2;
inner * inner
};
Here the expression inner * inner does not end with a semicolon, so its value is not suppressed. Since it is the last expression in the block, its value will be returned and assigned to a. If you put a semicolon on this same line, the value of inner * inner won't be returned.
In your specific case, not suppressing the value of your let statement doesn't make sense, and the compiler is rightly giving you an error for it. In fact, let is not an expression.
Semicolons are generally not optional, but there are a few situations where they are. Namely after control expressions like for, if/else, match, etc.
fn main() {
let a: u32 = 5;
if 5 == a {
println!("Hello!");
}
if 5 == a {
println!("Hello!");
};
for x in "World".chars() {
println!("{}", x);
}
for x in "World".chars() {
println!("{}", x);
};
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1bf94760dccae285a2bdc9c44e8f658a
(There are situations where you do need to have or not have semicolons for these statements. If you're returning the value from within you can't have a semicolon, and if you're setting a variable to be the value from within you'll need a semicolon.)

Confused on how to use lambda in ruby for select

I'm working with ruby and I'm just learning lambdas. I have an array of objects and I want to select certain objects from the array based of different conditions like so:
result = array.select{|ar| ar.number > 4}
I want to put the arguments of the select into a lambda expression i.e. I want to put |ar| ar.number > 4 into a lambda expression. I've tried a few things including:
result = array.select{lambda{|g| g.number > 4}}
But this doesn't work.
I've also tried this:
l = lambda {g.number > 4}
result = array.select{|g| l}
and that also doesn't work. Also I need to pass my lambda express to a function so I don't think the first way I did it would have worked. How would I do this?
Enumerable#select takes a block, not a lambda. So, you need to pass it a block.
Thankfully, there is an operator which will convert a lambda or proc (and in fact anything which responds to to_proc) to a block: the unary prefix & operator, which is only valid in argument lists (where it converts a proc to a block) and in parameter lists (where it converts a block to a proc).
So, if you have a lambda l, you can pass it as a block to method foo like so:
foo(&l)
Your second problem is that your lambda doesn't take any arguments, but the block passed to select takes one argument, so you need to create your lambda with one argument:
l = lambda {|el| el.number > 4 }
# I prefer this syntax, though:
l = -> el { el.number > 4 }
Putting all that together, we have:
result = array.select(&l)
To pass a lambda (or a proc) to a method as a block, you have to use the special &syntax:
result = array.select &mylambda
Also, your lambda (or proc) must specify the name for parameters it will receive and use. Thus:
l = lambda { |g| g.number > 4}
result = array.select &l
A clean way to do this is to use Ruby's "stabby" lambda syntax:
l = ->(ar) { ar.number > 4 }
result = array.select(&l)
The more "traditional" way to define a lambda is as such:
l = lambda { |ar| ar.number > 4 }
result = array.select(&l)
Note that you don't strictly need to use a lambda; you could also use a proc*:
p = proc { |ar| ar.number > 4 }
result = array.select(&p)
* The main difference between proc and lambda is that lambda will throw an error when passed too few or too many arguments, while proc will ignore it. Another subtle difference is that the return statement inside a proc will cause execution to return both from the closure and the enclosing method (if any).

Using Where() with a dynamic Func fails, but works with a hard coded Where() clause

See the two Linq (to SharePoint) code samples below.
The only differences are the highlighted sections of code. The first statement works as expected with a hard-coded where clause, but the 2nd set of code throws the error “Value does not fall in within the expected range” when I try to do a count on the items. What am I missing?
Works
relatedListItems = dc.GetList<GeneralPage>("Pages")
.Where(x => x.RelatedPracticesTitle.Any(y=>y=="Foo"))
if (relatedListItems.Count() == 0)
{…}
Fails - “Value does not fall within the expected range”
Func<GeneralPage, bool> f = x => x.RelatedPracticesTitle.Any(y => y == "Foo");
relatedListItems = dc.GetList<GeneralPage>("Pages")
.Where(f)
if (relatedListItems.Count() == 0)
{…}
If it's LINQ to Sharepoint, presumably that means it should be using expression trees, not delegates. Try:
Expression<Func<GeneralPage, bool>> f =
x => x.RelatedPracticesTitle.Any(y => y == "Foo");
relatedListItems = dc.GetList<GeneralPage>("Pages").Where(f);
By the way, it's generally a better idea to use Any() rather than Count() if you just want to find out if there are any results - that way it can return as soon as it's found the first one. (It also expresses what you're interested in more clearly, IMO.)
In the first case, you're using the Expression<Func<GeneralPage, bool>> overload and pass an expression which I assume LINQ to SharePoint will try to convert to CAML and execute.
In the second case, you're passing the plain Func<GeneralPage, bool> so LINQ to SharePoint can't figure out how to compose a query (it only sees the delegate, not the expression).

Resources