Kotlin: At which situations does implicit conversion happen? - kotlin

I am a beginner in Kotlin.
I'm most familiar with Python, and I just read through the basic Java Tutorial https://docs.oracle.com/javase/tutorial/java/index.html before jumping into Kotlin.
A question came up in my mind while reading this section of the documentation of Kotlin
https://kotlinlang.org/docs/reference/basic-types.html#explicit-conversions
What I have understood from above section of the documentation is:
For assignments =, implicit type conversion happens. If the lefthand side's type is a supertype or same type of righthand side's type the code would compile. Otherwise it's a compile error. In case of Int and Long, they are both subtypes of Number but neither of them subtypes of each other, so the implicit conversion would not work. Therefore we need to use methods like .toLong() or .toInt() to explicitly convert them.
Then when I read the part
val l = 1L + 3 // Long + Int => Long
I started wondering if implicit type conversion would be taking place in this situation.
The documentation says this has to do with the operator overloading.
How is this operator overloading implemented under the hood?
I tried to find the source code at Github https://github.com/JetBrains/kotlin/blob/master/core/builtins/native/kotlin/Primitives.kt ,
but here the functions are only declared but not implemented. Where can I find the implementations?
It seems that operation overloading does not actually perform type conversion. Do we just implementing all possible functions with same name but different parameter type signatures, so that the type is inferred, then the function with the matching signature is selected?
And the general question: In Kotlin, exactly at which situations does implicit conversion happen?

For assignments =, implicit type conversion happens. If the lefthand side's type is a supertype or same type of righthand side's type the code would compile.
Assignments aren't special in this. More generally, if the expected type of an expression is a supertype or the same as actual type, the code compiles; it's just that the expected type for the right-hand side of an assignment is the left-hand side's type. I wouldn't say there is an implicit conversion inserted, but I don't think anything breaks if you see it that way.
Do we just implementing all possible functions with same name but different parameter type signatures, so that the type is inferred, then the function with the matching signature is selected?
Yes, exactly (for this case). If you want to support primitive types, you need to provide overloads for all of them.
So 1L + 3 is just a method call so far as the parsing and type-checking is concerned (`Long.plus(Int): Long specifically), no implicit conversion is involved. But these methods are built into the compiler to handle specially which is why you don't see an implementation.
It becomes two bytecode instructions i2l ("convert an int into a long") and ladd ("add two longs"), but that's not something you should probably care about yet, or for a long time.
And the general question: In Kotlin, exactly at which situations does implicit conversion happen?
Smart casts are the closest Kotlin has to implicit conversions but they are sufficiently different from i.c.s in other languages that I wouldn't use the name. So I'd say never.

Related

In a dependently typed programming language is Type-in-Type practical for programming?

In a language with dependent types you can have Type-in-Type which simplifies the language and gives it a lot of power. This makes the language logically inconsistent but this might not be a problem if you are interested in programming only and not theorem proving.
In the Cayenne paper (a dependently typed language for programming) it is mentioned about Type-in-Type that "the unstratified type system would make it impossible during type checking to determine if an expression corresponds to a type or a real value and it would be impossible to remove the types at runtime" (section 2.4).
I have two questions about this:
In some dependently typed languages (like Agda) you can explicitly say which variables should be erased. In that case does Type-in-Type still cause problems?
We could extend the hierarchy one extra step with Kind where Type : Kind and Kind : Kind. This is still inconsistent but it seems that now you can know if a term is a type or a value. Is this correct?
the unstratified type system would make it impossible during type
checking to determine if an expression corresponds to a type or a real
value and it would be impossible to remove the types at runtime
This is not correct. Type-in-type prevents erasure of proofs, but it does not prevent erasure of types, assuming that we have parametric polymorphism with no typecase operation. Recent GHC Haskell is an example for a system which supports type-in-type, type erasure and type-level computation at the same time, but which does not support proof erasure. In dependently typed settings, we always know if a term is a type or not; we just check whether its type is Type.
Type erasure is just erasure of all things with type Type.
Proof erasure is more complicated. Let's assume that we have a Prop universe like in Coq, which is intended to be a universe of computationally irrelevant types. Here, we can use some p : Bool = Int proof to coerce Bool-s to Int. If the language is consistent, there is no closed proof of Bool = Int so closed program execution never encounters such coercion. Thus, closed program execution is safe even if we erase all coercions.
If the language is inconsistent, and the only way of proving contradiction is by an infinite loop, there is a diverging closed proof of Bool = Int. Now, closed program execution can actually hit a proof of falsehood; but we can still have type safety, by requiring that coercion must evaluate the proof argument. Then, the program loops whenever we coerce by falsehood, so execution never reaches the unsound parts of the program.
Probably the key point here is that A = B : Prop supports coercion, which eliminates into computationally relevant universe, but a parametric Type universe has no elimination principle at all and cannot influence computation.
Erasure can be generalized in several ways. For example, we may have any inductive data type with a single constructor (and no stored data which is not available from elsewhere, e.g. type indices), and try to erase every matching on that constructor. This is again sound if the language is total, and not otherwise. If we don't have a Prop universe, we can still do erasure like this. IIRC Idris does this a lot.
I just want to add a note that I believe is related to the question. Formality, a minimal proof language based on self-types, is non-terminating. I was involved in a Reddit discussion about whether Formality can segfault. One way that could happen is if you could prove Nat == String, then cast 42 :: Nat to 42 :: String and then print it as if it was a string, for example. But that's not the case. While you can prove String == Int in Formality:
nat_is_string: Nat == String
nat_is_string
And you can use it to cast a Nat to a String:
nat_str: String
42 :: rewrite x in x with nat_is_string
Any attempt to print nat_str, your program will not segfault, it will just hang. That's because you can't erase the equality evidence in Formality. To understand why, let's see the definition of Equal.rewrite (which is used to cast 42 to String):
Equal.rewrite<A: Type, a: A, b: A>(e: Equal(A,a,b))<P: A -> Type>(x: P(a)): P(b)
case e {
refl: x
} : P(e.b)
Once we erase the types, the normal form of rewrite becomes λe. λx. e(x). The e there is the equality evidence. In the example above, the normal form of nat_str is not 42, but nat_is_string(42). Since nat_is_string is an equality proof, then it has two options: either it will halt and become identity, in which case it will just return 42, or it will hang forever. In this case, it doesn't halt, so nat_is_string(42) will never return 42. As such, it can't be printed, and any attempt to use it will cause your entire program to hang, but not segfault.
So, in short, the insight is that self types allow us to encode the Equal, rewrite / subst, and erase all the type information, but not the equality evidence itself.

What is the significance of a ! following a type in kotlin? [duplicate]

What does a single exclamation mark mean in Kotlin? I've seen it a few times especially when using Java APIs. But I couldn't find it in the documentation nor on StackOverflow.
They're called platform types and they mean that Kotlin doesn't know whether that value can or cannot be null and it's up to you to decide if it's nullable or not.
In a nutshell, the problem is that any reference coming from Java may be null, and Kotlin, being null-safe by design, forced the user to null-check every Java value, or use safe calls (?.) or not-null assertions (!!). Those being very handy features in the pure Kotlin world, tend to turn into a disaster when you have to use them too often in the Kotlin/Java setting.
This is why we took a radical approach and made Kotlin’s type system more relaxed when it comes to Java interop: now references coming from Java have specially marked types -- Kotlin Blog
It's the notation for platform types:
T! means "T or T?"
Platform Types
The type names or class names ending with single exclamation mark ! are called platform types in Kotlin. You find them when you are working in Kotlin with old Java code that doesn't contain nullability information.
Examples:
Nullable Information: Nullable Type
#Nullable String in Java is considered as String? by Kotlin.
Non-null Information: Non-null Type
#NotNull String in Java is considered as String by Kotlin.
No Information: Platform Type
String without annotations in Java is considered as String! by Kotlin.
How to deal with Platform Types?
You can work with a platform type either as a nullable or a non-null. The compiler will allow you to call all methods on this type. It’s your responsibility how to use them. If you know that the value can be null, you should compare it with null before you call methods on it. If you know it’s not null, you can use it directly but as in Java, you’ll get exception if your assumption about the nullability is wrong.
Note that you can't declare platform types in Kotlin code, they come only from Java code.
Inheritance and Platform Types
While overriding Java methods in Kotlin code, you have the option to declare parameters and return types as nullable or non-null. You need to choose this wisely, because if you decide to make the parameters non-null, the Kotlin compiler generates non-null assertions for these non-null parameters. And when next time you access this Kotlin code back from Java and you pass a null value, you'll get exception.
Hope that helps clearing all your doubts about Platform Types.
A Type notated with ! is called platform type, which is a type coming from Java and thus can most probably be null. It’s what the Kotlin compiler infers by default when calling Java (for the most basic cases, Java methods can be annotated to get around this). You should handle platform types as nullable types, unless you certainly know that the particular API will never return null. The compiler allows platform types to be assigned to variables of both nullable and non-null types.
Notation for Platform Types
[...]
T! means "T or T?" [...]
You could refer to platform types as "types of unknown nullability". Also important to know is that you cannot use the exclamation-marked type for your own types, it's not part of the Kotlin syntax, it's only a notation.
I use the funny interpretation to remember those things as below:
?: I dont know whether it is null or not.
!: Be careful! This might be null.
!!: Be careful, and yes I know it. This is always not null.
I've seen it a few times especially when using Java APIs
As mentioned by s1m0nw1, T! means T or T?. The next question is: what is T?? This is nicely documented at https://kotlinlang.org/docs/reference/null-safety.html. Kotlin does not allow certain elements to be null, e.g. String, unlike Java
To allow nulls, we can declare a variable as nullable string, written
String?:
var b: String? = "abc"
b = null // ok
[...]
b?.length
This returns b.length if b is not null, and null otherwise. The type of this expression is Int?.
Excerpt from Platform Types in Kotlin :
Besides explicitly specifying a type as optional (e.g. Person?), Kotlin presents us with another beast, called Platform Type, specified by putting a single exclamation mark instead (e.g. Person!). This concept has been created for compatibility reasons, when accessing code from null-unsafe platforms like Java. It is often the case that when using a Java library, many methods return SomeType!, since the Kotlin compiler cannot infer if the result is nullable or not.
For example:
(Mutable)Collection<T>!
Just means the following: "Java collection of T may be mutable or not, may be nullable or not".
Hope this helps.

How does Julia recognize values as singleton types?

It is a cool feature of Julia that values can be used as types, at least as type parameters. For example, one can assert that arrays are of a particular dimensionality, such as x :: Array{Int,2}. My question is: how does Julia do that and how do users of Julia get access to that power? I assume that 2 is being converted to or interpreted as some sort of singleton type of 2. I am curious to know what function does that conversion. I tried to assert 2 :: Type{2} and isa(2, Type{2}), but that only asserts a singleton if 2 is replaced by an actual type.
You can not define your own imutables and use them as singleton types (yet).
Currently anything that makes static int valid_type_param(jl_value_t *v) defined in jltypes.c return true, can be used as a type parameter. There is a TODO to add more types, and you'll probably just need a compelling usecase to get help to change the behaviour.
Update:
See also the manual documentation on types: Both abstract and concrete types can be paramaterized by other types and by certain other values (currently integers, symbols, bools, and tuples thereof). Type parameters may be completely omitted when they do not need to be referenced or restricted.

Why isn't Eiffel's automatic type conversion feature more popular?

What happened to me while programming in Java:
String str
// want to call something(), but signature does not match
something(Foo foo)
// but I have this conversion function
Foo fooFrom(String)
// Obviously I am about to create another method overload.. sigh
something(String s) {
something(fooFrom(s));
}
But then I thought of the possibility of a "automatic type conversion" which just uses my defined conversion function fooFrom everytime a string is passed in where a Foo object is excepted.
My search brought me to the wikipedia page about type conversion with this Eiffel example:
class STRING_8
…
create
make_from_cil
…
convert
make_from_cil ({SYSTEM_STRING})
to_cil: {SYSTEM_STRING}
…
The methods after convert are called automatically if a STRING_8 is used as a SYSTEM_STRING and vice-versa.
Somehow surprising for me I could not find any other language supporting this.
So my question: are there any other languages supporting this feature?
If not, are there any reasons for that, since it seems quite useful to me?
Further I think it would not be difficult to implement it as a language add-on.
There is one minor point that may make the things a bit more complicated. At the moment Eiffel has a rule that conversion can be applied only when the source of reattachment is attached to an object, i.e. is not Void (not null in Java/C#).
Let's look at the original example:
something (str);
Suppose that str is null. Do we get a NullPointerException / InvalidArgumentException, because the code is transformed into
something (fooFrom (str));
and fooFrom does not expect null? Or is the compiler smart enough to transform this into
if (str == null)
something (null);
else
something (fooFrom (str));
?
The current Eiffel standard makes sure that such issues simply do not happen and str is not null if conversion is involved. However many other languages like Java or C# do not guarantee that and the additional complexity may be not worth the effort for them.
I believe that Eiffel is not the only language to support conversion routines, but I would say that it might be one of the very few that integrate this very nicely with the rest of the language definition.
In .NET, for example, you have both op_Explicit and op_Implicit routines that can be used for conversion for languages that support them. And I believe C# does.
Manu
Type coercion (implicit conversion) is a curse and a blessing--handy in some case, but it can also backfire.
For instance, Javascript has many weird coercion rules, that can leads to bug when coercings string to number, etc.
Scala has something called "implicit" which achieves something similar (at least to me) to what you describe in Eiffel. With little surprise, they can lead to certain gotchas. But they can be also very handy, see for instance the article Pimp My Library.
C++ has copy constructors and assignment operator.

Does static typing mean that you have to cast a variable if you want to change its type?

Are there any other ways of changing a variable's type in a statically typed language like Java and C++, except 'casting'?
I'm trying to figure out what the main difference is in practical terms between dynamic and static typing and keep finding very academic definitions. I'm wondering what it means in terms of what my code looks like.
Make sure you don't get static vs. dynamic typing confused with strong vs. weak typing.
Static typing: Each variable, method parameter, return type etc. has a type known at compile time, either declared or inferred.
Dynamic typing: types are ignored/don't exist at compile time
Strong typing: each object at runtime has a specific type, and you can only perform those operations on it that are defined for that type.
Weak typing: runtime objects either don't have an explicit type, or the system attempts to automatically convert types wherever necessary.
These two opposites can be combined freely:
Java is statically and strongly typed
C is statically and weakly typed (pointer arithmetics!)
Ruby is dynamically and strongly typed
JavaScript is dynamically and weakly typed
Genrally, static typing means that a lot of errors are caught by the compiler which are runtime errors in a dynamically typed language - but it also means that you spend a lot of time worrying about types, in many cases unnecessarily (see interfaces vs. duck typing).
Strong typing means that any conversion between types must be explicit, either through a cast or through the use of conversion methods (e.g. parsing a string into an integer). This means more typing work, but has the advantage of keeping you in control of things, whereas weak typing often results in confusion when the system does some obscure implicit conversion that leaves you with a completely wrong variable value that causes havoc ten method calls down the line.
In C++/Java you can't change the type of a variable.
Static typing: A variable has one type assigned at compile type and that does not change.
Dynamic typing: A variable's type can change while runtime, e.g. in JavaScript:
js> x="5" <-- String
5
js> x=x*5 <-- Int
25
The main difference is that in dynamically typed languages you don't know until you go to use a method at runtime whether that method exists. In statically typed languages the check is made at compile time and the compilation fails if the method doesn't exist.
I'm wondering what it means in terms of what my code looks like.
The type system does not necessarily have any impact on what code looks like, e.g. languages with static typing, type inference and implicit conversion (like Scala for instance) look a lot like dynamically typed languages. See also: What To Know Before Debating Type Systems.
You don't need explicit casting. In many cases implicit casting works.
For example:
int i = 42;
float f = i; // f ~= 42.0
int b = f; // i == 42
class Base {
};
class Subclass : public Base {
};
Subclass *subclass = new Subclass();
Base *base = subclass; // Legal
Subclass *s = dynamic_cast<Subclass *>(base); // == subclass. Performs type checking. If base isn't a Subclass, NULL is returned instead. (This is type-safe explicit casting.)
You cannot, however, change the type of a variable. You can use unions in C++, though, to achieve some sort of dynamic typing.
Lets look at Java for he staitically typed language and JavaScript for the dynamc. In Java, for objects, the variable is a reference to an object. The object has a runtime type and the reference has a type. The type of the reference must be the type of the runtime object or one of its ancestors. This is how polymorphism works. You have to cast to go up the hierarchy of the reference type, but not down. The compiler ensures that these conditions are met. In a language like JavaScript, your variable is just that, a variable. You can have it point to whatever object you want, and you don't know the type of it until you check.
For conversions, though, there are lots of methods like toInteger and toFloat in Java to do a conversion and generate an object of a new type with the same relative value. In JavaScript there are also conversion methods, but they generate new objects too.
Your code should actally not look very much different, regardless if you are using a staticly typed language or not. Just because you can change the data type of a variable in a dynamically typed language, doesn't mean that it is a good idea to do so.
In VBScript, for example, hungarian notation is often used to specify the preferred data type of a variable. That way you can easily spot if the code is mixing types. (This was not the original use of hungarian notation, but it's pretty useful.)
By keeping to the same data type, you avoid situations where it's hard to tell what the code actually does, and situations where the code simply doesn't work properly. For example:
Dim id
id = Request.QueryString("id") ' this variable is now a string
If id = "42" Then
id = 142 ' sometimes turned into a number
End If
If id > 100 Then ' will not work properly for strings
Using hungarian notation you can spot code that is mixing types, like:
lngId = Request.QueryString("id") ' putting a string in a numeric variable
strId = 42 ' putting a number in a string variable