I actually have a draft brewing about that. I'll p...
# language-proposals
b
I actually have a draft brewing about that. I'll put it on GitHub gist later
o
val a5 = [] // Compiler error: type cannot be inferred. See below
It can be
List<Nothing>
may be?
🤔 1
j
I think it should be globally overridable also I saw a good suggestion something like
operator fun listLiteral(varargs args) = listOf(*args)
âž• 1
or similar
Also since getting used to
listOf
and similar I havent found myself missing literal syntax
o
List literal is not very useful when you just create a list of primitive types. However, when it’s a list of maps, or list of classes receiving some other list, and in general more complex structure there is lots of repetitive things:
mapOf
for each item, or classname for each item, etc. Literal syntax is really useful when there are 3 things playing together: * list literal * map literal * class literal and these things integrated into a single initialization construct:
Copy code
class ServerConfig(connectors: List<Connector>, properties: Map<String, Any>)
class Connector(protocol : HttpProtocol, host : String, port : Int)

val config = ServerConfig( // normal primary constructor
     connectors = [  // list literal
            (protocol = HTTP, host = "<http://foo.com|foo.com>", port = 80), // class literal, type is inferred from element of list
            (protocol = HTTPS, host = "<http://foo.com|foo.com>", port = 443)
     ],
     properties = [ // map literal
        "debug" = true,
        "logLevel" = INFO,
     ]
)
(this is just my own opinion and not something designed or considered for the language at the moment)
g
@benleggiero my opinion that type syntax should be moved to another proposal. Actually, it's very questionable proposal about special type syntax and if we want to discuss collection literals we should have proposal only about collection literals.
👍🏼 1
b
@orangy a very Kotlinesque approach!
@jkbbwr I really like that. Seems much more like current language constructs!
d
@benleggiero imho this solution isn't very flexible. what if i want array instead of list for specific scope? i think
operator
is more suitable for this.
b
@deviant I think I covered that in the gist. What exactly do you mean? Can you give an example?
d
have not seen any word about operators in the gist i added example in the comments section
b
I'll update it, but if you scroll up in this thread there have been some great ideas
I've updated this gist and would love some feedback before turning it into a KEEP proposal https://gist.github.com/BenLeggiero/1582a959592cadcfee2a0beba3820084
👍 3
r
Added a comment to the Gist regarding support of other collection types and user created collections. I think the proposed syntax is awesome but if it's based on blessing specific implementations such as whatever backs
listOf
then it would only be usable in certain scenarios and you'll have to resort back to function factories in cases for example when you want the underlying data type to be something like
ImmutableList
b
I’m not sure you and I are thinking of this the same way, @raulraja
I’ve responded to the gist comments 🙂
t
how would you specify the type of a list literal explicitly? in dart it looks like this:
val list = <Any>[1,2,3]
val list2 = <Int>[]
b
@tschuchort if you read my proposal, I cover that very early on: https://gist.github.com/BenLeggiero/1582a959592cadcfee2a0beba3820084#sequence-explicit-type
t
not really, the example only deals with assignment to a named variable where a type annotation can be given. But the collection literal may also be argument to a generic function:
Copy code
fun <T> foo(l: List<T>): List<T>

val l2 = foo([1,2,3]) //infered type is List<Int>
l2 += "hello2"  // we actually wanted List<Any>
in many cases this can be solved by specifying the generic type of
foo
explicitly, but what if
foo
is parameterized over many possibly complex or even anonymous types?
<T>[]
is equivalent to
listOf<T>()
b
Again, I did address this. Your solution would be:
Copy code
fun <T> foo(l: List<T>): List<T>

val l2: List<Any> = foo([1,2,3]) // explicit type is List<Any>
l2 += "hello2"  // we actually get and use List<Any>
You may also do this:
Copy code
val l3: = foo([1,2,3]) as List<Any> // explicit type is List<Any>
l3 += "hello2"  // we actually get and use List<Any>
t
Copy code
fun foo() = object {
    var x = 1
    var y = 2
}

fun <S,T,V> Observable<V>.bar(a: List<T>, b: S): Observable<List<T>>

Observable.just(Unit)
        .bar([1,2,3], foo())
        .map { it += "hello" } // error infered type is Observable<List<Int>> but we want Observable<List<Any>>
specifying the type arguments of
bar
is not feasible since
S
is the anonymous return type of
foo
(Or may just be really long in a different example)
you can of course just cast
[1,2,3]
to
List<Any>
but I think it would be cleaner to have syntax for this instead of casting
question is of course what happens internally with this cast. Does the compiler actually use it for type inference or does it just infer
List<Int>
and then cast to
List<Any>
. In the latter case this would lead to issues when we are dealing with custom collection types:
[1,2,3] as MyList<Any> // illegal cast
A possible solution would be a syntax like this:
MyList.<Any>[1,2,3]
analog to
MyList.listOf<Any>(1,2,3)
b
Just use
as
...
Copy code
Observable.just(Unit)
        .bar([1,2,3] as List<Any>, foo())
        .map { it += "hello" } // error infered type is Observable<List<Int>> but we want Observable<List<Any>>
What you propose seems like reducing the number of characters without a clear advantage. Also, note that in this usage,
as
tells the compiler the initial type of the sequence. It's not casting anything.
r
your response makes sense but then you loose inference because you'd have to bless a specific collection type to make
val x = [1, 2, 3]
work. In your proposed solution you have to type the receiver always. Not saying that it's not acceptable, I think it'd be a fair compromise to support multiple collection types that have similar semantics as
List
b
I understand where you're coming from... But the Kotlin team has made it clear to me that
List
is the superior default sequence type. That combined with the fact that they can improve the performance and safety of the backing class while keeping the interfaces exactly the same make it a very good default type. I don't necessarily agree, but it's good enough and has enough support behind it that I'm not willing to argue against it in this proposal, especially given the amount of usefulness that I think this proposal will bring to the language as a whole.
r
I'm not arguing against it, I'm just saying that it introduces ambiguity because the syntax requires to bless an implicit receiver in this case
List
but if one it's specified then it uses the implementation from the particular receiver. That can introduce unexpected runtime behaviors since both may be of
List
interface but have different backing implementations and it's not obvious that for example:
Copy code
val x = [0, 1, 2] // Default list impl
val y : LinkedList<Int> = [0, 1, 2] //LinkedList impl
I'd expect the first one to result in a compiler error expecting it to at least be typed to
List
Copy code
val x: List<Int> = [0, 1, 2, 3] // same as listOf
b
I understand your concerns completely. I even agree with them. But the opaque, "blessed" approach is one that the JetBrains team has doubled down on with the entire stdlib. If you have a counter-proposal feel free to draft it up and submit it, but I don't have the energy to argue against what seems to be a very strongly-argued approach to collection types. Are there any [JB] people in @here who can clarify any position on this?
r
The std lib doesn't have an opaque blessed approach. It has different functions for different list types.
listOf
,
mutableListOf
,
arrayListOf
,
arrayOf
... I don't have a counter proposal, I was hoping to discuss yours because I thought it was good but we needed to workout some of the ambiguity to see what the trade off are beside the obvious syntactic enhancement. Since you don't have the energy I don't have much to add. Good luck with the proposal and I hope it makes it through. Looking forward to enhanced syntax for collections. 🍻
g
It can be
List<Nothing>
may be?
@orangy Actually it’s interesting question. I know this proposal + related issues https://youtrack.jetbrains.com/issue/KT-7590 This proposal has “Target versions: 1.3” but in “To be discussed” state, really curious are there any plans about this?
b
@raulraja Thanks for the luck and well-wishes! 🍻 Just to clarify: That’s what I was talking about; the
listOf
,
mutableListOf
,
mapOf
, and
mutableMapOf
all return opaque types masked by their generic interfaces. In fact, as far as I can tell,
listOf
and
mutableListOf
are generally both backed by the same type (like
java.util.ArrayList
on JVM). And their usage is highly encouraged throughout the standard library, with things like
.filter
and
.map
returning that same opaque
List
type.
@gildor I don’t think I like that… it might lead to confusion of “Well I expected it to be of some type” or “I thought that meant I could put anything in it”. But I suppose my proposal doesn’t preclude KT-7590 nor vice versa, so I’ll just let them coexist. If that one gets voted in, so be it.
g
@benleggiero Yes, I completely agree that it’s not related directly to your proposal, just rises this question again. btw I added a couple comment to your gist with proposal
👍🏼 1