Semantic operator of dot in Kotlin - kotlin

I'd like to understand a bit better the 2 functions below. I know it is very compact and understand more or less what it does: it converts each characters of a string into string of '0' and '1'. But...
How does the dot(in front of encodeToByteArray) connect the 's' to encodeToByteArray()?
Where can I find more info about what dot represents?
Also, how and why the code { byte -> binaryStringOf(byte) } can do the job in that place?
How does it "know" that there is a byte with which it calls the function binaryStringOf(byte)
Where can I find more info about it, too?
fun binaryStringOf(message: String): String {
var s: String
s = (message)
.encodeToByteArray()
.joinToString("") { byte -> binaryStringOf(byte) }
return s
}
fun binaryStringOf(b: Byte): String {
return b.toString(2).padStart(8, '0')
}

The formatting above makes things a little bit more confusing, but let me try to explain what is going on.
The = is an assignment operator. It says "assign the variable s to the result of the expression on the right side".
Now we see that message is a parameter in the binaryStringOf function of type String. String is a class which contains a function (also called a method when it is a member of a class) called encodeToByteArray which returns a ByteArray.
ByteArray in turn has a function called joinToString which we're giving two parameters: one of type String, and one of type ((Byte) -> CharSequence) (ie, the function is itself being passed in as a variable, using lambda syntax). Kotlin has some syntactic sugar to make this look nicer when the lambda is the last argument.
So, the statement
s = (message)
.encodeToByteArray()
.joinToString("") { byte -> binaryStringOf(byte) }
means "the variable s is assigned the value that results from calling joinToString on the result of calling encodeToByteArray on message.
Then return s says that the return value from the binaryStringOf should be whatever value was assigned to s.

.encodeToByteArray()
works on the incoming string (message in this case). It returns a ByteArray; so something that represents an array of Byte values.
And on that array object, it invokes the joinToString() method. That method receives various arguments, but only the separator string ("") is provided, and the transform parameter.
Now: transform is a function. It is something that can be invoked, with parameters, and that has to return a specific result.
The key part to understand is that { byte -> ... } is that transform function parameter.

Related

Kotlin Nullable with .toString() understanding Kotlin null safe

So i'm playing around will nullables and null safe in Kotlin to try understand it better.
var stringNull : String? = null
println(stringNull.toString())
println(stringNull?.lowercase())
Both of these return "null" so I'm just trying to figure out what the difference between String and String? is in practical terms. Is the "nullness" of the String not stored in the String Class itself? is it the method, such as toString() or lowerCase(), that is handling the "nullness" passed to it by an operator and not the String class itself? it's hard to see what's happening here in terms of the literal data that is being passed around. how is the value of "null", as in no data, being parsed into a string value of "null"?
The reason println(stringNull.toString()) prints null is because Kotlin decided to create an extension function on Any? called toString() over here.
If you try
class SomeClass {
fun foo() {}
}
fun main() {
val some: SomeClass? = null
println(some?.foo())
println(some.foo())
}
The compiler will complain with Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type SomeClass?
toString is just a very special function so they decided to add it on every type, hence the Any?
P.S
Also, I would recommend to use val by default (even when playing around) and only use var if you really really need to.
Is the "nullness" of the String not stored in the String Class itself?
If I understand your question correctly, that is correct, String is itself not nullable. String? is the nullable version of String.
is it the method, such as toString() or lowerCase(), that is handling the "nullness" passed to it by an operator and not the String class itself?
Among toString and lowerCase, only toString handles nulls. We can see this from their signatures:
public fun Any?.toString(): String
// ^^^^
// nullable type
public expect fun String.lowercase(): String
// ^^^^^^
// non-nullable type
In the internal implementation of toString, you can imagine that there is a bit of logic that says "if the instance is null, return "null"".
lowercase cannot be called on a nullable string directly, and will give you a compiler error if you try to do that. This is why you have to use the ?. safe navigation operator to call it on stringNull. This will check if stringNull is null before calling lowercase, and if it is found that stringNull is null, lowercase won't actually be called, and the whole expression stringNull?.lowercase() evaluates to null. Therefore, "null" is printed.
(And I think I just answered your last question)
stringNull.toString() calls the following extension function:
/**
* Returns a string representation of the object. Can be called with a null receiver, in which case
* it returns the string "null".
*/
public fun Any?.toString(): String
stringNull?.lowercase() call the following extension function:
/**
* Returns a copy of this string converted to lower case using Unicode mapping rules of the invariant locale.
*
* This function supports one-to-many and many-to-one character mapping,
* thus the length of the returned string can be different from the length of the original string.
*
* #sample samples.text.Strings.lowercase
*/
#SinceKotlin("1.5")
#WasExperimental(ExperimentalStdlibApi::class)
public expect fun String.lowercase(): String
So while the two calls both return null, it's two different function calls.

Having trouble passing a value in a Kotlin function. The variable being convert:(String)->R

I have some code here that reads a file and converts it to a list of a type depending what is being passed to the function. The code for this function looks like so.
fun <R> readFile(path:String,convert:(String)->R): List<R> {
val reader = BufferedReader(FileReader(File(path)))
var line = reader.readLine()
val result = mutableListOf<R>()
while(line!=null) {
result.add(convert(line))
line = reader.readLine()
}
reader.close()
return result.toList()
}
The problem I have is that I am unsure sure what to pass into the variable convert. I am trying to return back a list of Person objects back. Below is the code I tried myself to hit the function readFile
val listOfPerson = readFile("path/person.txt","Person")
With this I'm getting an error with my Type. Intellij is flagging "Required (String) -> ???"
The type (String) -> R is a function type. It represents a function that takes a String as argument and returns R (which is generic and can be different on every call to readFile()).
In order to call readFile(), you need to pass a String and a function:
readFile("path/person.txt") { line -> /* whatever you need */}
Note that in Kotlin, the { line -> ...} is a lambda expression, i.e. a function literal. This particular lambda expression represents a function that takes the argument line and does whatever is in the body.
Lambda expressions can be passed out of the parentheses of the function call, but it's still an argument to the function. The above code is equivalent to:
readFile("path/person.txt", { line -> /* whatever you need */})
In your specific case, the convert argument is a conversion function, so you need to provide some code that converts the line: String argument into whatever R type you need in the output list.
If you just want a list of the lines as strings (without conversion) you can simply provide the identity function:
readFile("path/person.txt") { line -> line }
Or in short:
readFile("path/person.txt") { it }
it is an implicit argument (a shortcut) for lambda expressions that represent functions with only 1 argument, which is the case here.

Higher order functions is complex?

I have read many articles, but there are still things I am having difficulty understanding. Where's the point I can't understand? My questions are in the code. I hope I asked right.
fun main() {
/*
1- Is it argument or parameter in numbers{} block?
where is it sent to the argument? why do we send "4" if it is parameter?
Are all the functions I will write in (eg println) sent to the numbers function? But this HOF can
only take one parameter.
*/
numbers {
/*
2-How does this know that he will work 3 times?
According to the following 3 functions? Is there a for loop logic??
*/
println(it)
"4" // 3- Which one does this represent? f:(String) or ->String?
}
}
fun numbers(f: (String) -> String) {
f("one")
f("two")
f("three")
println(f(" - "))
}
There is no argument or parameter defined in your lambda block above. It's just the content of your lambda function. You've used the implicit single parameter name of it. "4" is the return value of your lambda.
The lambda itself isn't "aware" of how many times it will be called. In this case, it is called four times, because your numbers function invokes the parameter f four times.
A lambda's return value is whatever its last expression evaluates to. In this case, it returns the String "4".
Maybe this will help. Lambda syntax is a convenience. But we can take away each piece of syntactic sugar one at a time to see what it actually means.
All of the code blocks below have the exact same meaning.
Here is your original statement:
numbers {
println(it)
"4"
}
First, when a lambda omits the single parameter, it gets the implicit parameter name it. If we avoid using this sugar, it would look like this:
numbers { inputString ->
println(inputString)
"4"
}
The evaluated value of the last expression in a lambda is what it returns. You can also explicitly write a return statement, but you must specify that you are returning from the lambda, so you have to put its name. So if we put this in, it looks like this:
numbers { inputString ->
println(inputString)
return#numbers "4"
}
When a lambda is the last argument you pass to a function, you can put it outside the parentheses. This is called "trailing lambda". And if the function is the only argument, you don't need parentheses at all. If we skip this convenience, it looks like this:
numbers({ inputString ->
println(inputString)
return#numbers "4"
})
A lambda is just a very compact way of defining a function. If we define the function directly, it looks like this:
numbers(fun(inputString: String): String {
println(inputString)
return "4"
})
The function you are passing is the argument of the numbers() function. You can also define it separately and then pass the function reference like this:
fun myFunction(inputString: String): String {
println(inputString)
return "4"
}
numbers(::myFunction)

When do I use another function without paramters in Kotlin?

I'm a novice of Kotlin.
I found that I can use another function without parameters even if it has.
Let me know when I can use it.
Q1) Why can I use 2 types? (with parameters & without parameters) Is it Kotlin's feature?
Q2) What does it mean? ((Result!) -> Unit)!
It seems you are confused, you can never use a function without arguments. If the function has arguments then you have to fill the slot somehow.
The closest thing that could relate to what you are referring to is default values for arguments.
fun example(boolean: Boolean = true) {}
example()
example(true)
example(false)
You can omit the argument because it has defaulted in the function signature.
The documentation
What you are showing in the image file is a lambda.
In the first example:
signIn(listener: Session...)
That seems to be a callback. So it is gonna be an interface or maybe an abstract class called when some async operation is finished.
The second example, it is the callback implemented as a lambda
signIn() { result ->
//do something
}
In Kotlin the last argument if it is a lambda or something that can be implemented as a lambda can be moved out of the parenthesis for syntactic sugar. A lambda is like an anonymous function, it is a literal of a function.
By example you can declare a lambda:
val lambda = {text: String -> text.lenght % 2 == 0}
fun setRuleForText(rule: (String)-> Boolean) {...}
setRuleForText(lambda)
setRuleForText() { text: String
text.isNotEmpty()
}
In this case, the argument is a kotlin function. The argument rule is a function that receives a String as an argument and returns Boolean. Something to point out is that expressions return the last written value without the need for the reserved return word.
This is the documentation. And here you can see from a good source more about functions (the author is a Kotlin certified trained by Jetbrains)
In your case (Result) -> Unit means the lambda should receive a Result type as argument and then return Unit (unit is like void in Java but is more than that), no explicit return type.
signIn() { result ->
//do something
}
Most of the types, the argument on lambdas is inferred automatically, but if not, then
signIn() { result: Result ->
//do something
}
Both of the listed functions take a parameter.
The first is:
signIn(listener: Session.SignInListener!)
That takes a single parameter, of type Session.SignInListener.  (The ! means that Kotlin doesn't know whether it's nullable or not, because it's from Java and isn't annotated.)
The other is:
signIn {...} (listener: ((Result!) -> Unit)!)
That's the IDE's representation of the function with this signature:
signIn(listener: ((Result!) -> Unit)!)
That takes a single parameter, which is a function type (see below).
The reason the IDE shows it with braces is that in Kotlin, if the last parameter to a function is a lambda, you can move it outside the parentheses.  So a call like this:
signIn({ println(it) })
could equally well be written like this:
signIn() { println(it) }
The two forms mean exactly the same.  And, further, if the lambda is the only parameter, you can omit the parens entirely:
signIn { println(it) }
Again, it means exactly the same thing.
That syntax allows you to write functions that look like new language syntax.  For example:
repeat (10) {
// ...
}
looks like a new form of loop construct, but it's really just a function called repeat, which takes two parameter (an integer, and a lambda).
OK, let's look at that function type: ((Result!) -> Unit)!
That's the type of a function which takes one parameter of type Result, and returns Unit (i.e. nothing useful; think of it as the equivalent of void).  Again, it's complicated because Kotlin doesn't know about the nullability; both the Result parameter and the parameter holding this function have !s to indicate this.  (Without them, it would just be (Result) -> Unit.)

Using implicit variable in function literal

I started reading Kotlin course book. I stopped on function literals. Here I have a code:
val printMessage = { message: String -> println(message) }
printMessage("hello")
printMessage("world")
Then I have an information that I can omit parameter type:
{ message -> println(message) }
And now I have next step:
"In fact, Kotlin has a neater trick. If there is only a single parameter and the type can beinferred, then the compiler will allow us to omit the parameter completely. In this case, itmakes the implicit variable it available:
{println(it)}
And now after using this code I get an error "unresolved reference: it" and "too many arguments for public operator fun invoke(): ??? defined in kotlin.Function()":
val printMessage = {println(it)}
printMessage("print something")
My question is how to use implicit variable in single paramenter function literal?
See the Kotlin documentation, specifically where it says:
If the compiler can figure the signature out itself, it is allowed not
to declare the only parameter and to omit ->. The parameter will be
implicitly declared under the name it.
In your case, the compiler (at least up to current version 1.3.31) can't figure the signature out itself:
val printMessage = {println(it)}
But if you give your printMessage variable an explicit type, it will work:
val printMessage: (String) -> Unit = { println(it) }
You always need to provide all information about all generic parameters. If you want to omit it, it needs to be inferable from some other part of the code. The only information you provide though is that you want printMessage to be a lambda. So it assumes it to be of type ()->Unit. This is because you don't declare a parameter for the lambda itself. The implicit parameter it is therefore not usable.
val printMessage = { it: String -> println(it) }
val printMessage: (String)->Unit = { println(it) }
Simply put: If you're inside a lambda with one parameter, the implicit it can be used as this parameters name, but a reference named it within the body of the lambda doesn't declare the single parameter.