I think that we first have to find out whether you want a type alias or a “new” type definition: 2 new type definitions (e.g. data class Person(...)
) always lead to different types, even if they store the same data. Think of the type like having an additional tag. Type aliases (e.g. typealias Human = Pair<String, Int>
) of the same underlying type definition (= Pair<String, Int>
) are interchangeable, as well as the alias with the original type.
If you wish to introduce names for the components, then you should either go with a “new” type, because the component names are type specific, or introduce an (extension) function for the underlying type, e.g.
fun Pair<String, Int>.name() = first
The funny thing is that the above function definition has the same effect as
fun Human.name() = first
for the type alias, because every Pair<String, Int>
is trivially a Human
(what you suggested) and then this can provide its name.
Btw: What is “statically structurally typed”? I have heard the notion of product type that you probably refer to as “structurally”. Many functional programming languages are statically typed, as well as Kotlin (and Java) are.
Also it seems that Kotlin is a bit more verbose than TypeScript, i.e. we don’t cram
function test(instance :{name: string, age :int})
all into one line. Instead, you would either go for a standard tuple (instance :Pair<String, Int>
) or introduce a type/typealias beforehand. The most un-kotlinish is the following construction in TypeScript:
function test(instance :{name :string, age :int}) {...}
test({name: "Bob", age: 32})
Why? Because it has very much a scripting feeling. Even if you first define const hans = { name: "Hans", age: 32 }
, the IDE will have a hard time to figure out that you could/should call the previous function test(...)
with that object/tuple. (Yes, it is possible for IntelliJ Ultimate to figure out that in this case all types are correct.) It simplifies the understanding for humans and the job of the IDE a lot if you first introduce a specific type, e.g. Person
. (“Simplifying the job for the IDE” is also implying that the IDE can suggest functions that match if the types are more explicit.)
Rem.: The NamedBeing
in the example from last time is not necessarily an Alien
, but Person
as well as Animal
are valid subtypes (because their type definitions both contain : NamedBeing
). Note that in this case all 3 are different types, i.e. a Person
is not an Animal
(even though you could construct one from its data), v.v. Person
is a proper subtype of NamedBeing
even though in the example they have the same data, but that could easily change.
When dealing with a function definition, it makes sense to think first whether you want an actual Person
, (an Animal
) or just a NamedBeing
. This becomes apparent when you wish to claim that you require a Human
but then want to hand in an Animal
.
To give you an example from the real world: I want to fun marry(spouse :Person)
rather than just any spouse :NamedBeing
or spouse :Pair<String, Int>
. Latest when it turns out that name= "Fifi"
, I will become suspicious. But I also want the compiler to sort out these cases beforehand (i.e. statically), not have a crash on the wedding day.