This simple scenario
data class Person(var name:String, var age:Int)
var people = listOf(
Person("Adam", 36),
Person("Boris", 18),
Person("Claire", 36),
Person("Adam", 20),
Person("Jack", 20)
)
println(people.sortedBy{compareBy{Person::age, Person::name}})
does not compile with
Error:(27, 29) Kotlin: Type inference failed: Not enough information to infer parameter T in inline> fun compareBy(crossinline selector: (T) -> Comparable<*>?): Comparator Please specify it explicitly.
Changing it to
println(people.sortedBy{compareBy<Person>{Person::age, Person::name}})
doesn't work, neither does
println(people.sortedBy{compareBy<Person>{Person::age}.thenBy { Person::name }})
giving
Error:(28, 20) Kotlin: Type parameter bound for R in inline fun > Iterable.sortedBy(crossinline selector: (T) -> R?): List is not satisfied: inferred type Comparator is not a subtype of Comparable>
So then I also tried the multi-function overload
println(people.sortedBy{compareBy<Person>({it.age}, {it.name})})
and this yielded
Error:(28, 20) Kotlin: Type parameter bound for R in inline fun > Iterable.sortedBy(crossinline selector: (T) -> R?): List
is not satisfied: inferred type Comparator is not a subtype of Comparable>
And, even more fun if I add the type parameter for sortedBy too
println(people.sortedBy<Person>{compareBy<Person>({it.age}, {it.name})})
This yields exactly the same problem, i.e.,
Error:(28, 20) Kotlin: Type parameter bound for R in inline fun > Iterable.sortedBy(crossinline selector: (T) -> R?): List
is not satisfied: inferred type Comparator is not a subtype of Comparable>
What am I doing wrong?
Fooled by distinction between sortedWith and sortedBy. Turns out that sortedBy uses a single criteria (e.g., sortedBy(Person::name)) whereas if you want multiple criteria, you need sortedWith:
people.sortedWith(compareBy(Person::age, Person::name))
// or
people.sortedWith(compareBy({ it.age }, { it.name }))
Related
In the code below, I define a generic linked list. Go1.18 is happy to use an instance of the list as a key to a map. However, the last line, when uncommented, doesn't compile; I get the error:
Cons[int] does not implement comparable
Is there a weaker type constraint I can use that picks out those types that can be used as keys, or is this intended, or is it a compiler bug?
package main
import "fmt"
type List[X any] interface {
isList()
}
type Cons[X any] struct {
Data X
Next List[X]
}
func (Cons[X]) isList() {}
type Nil[X any] struct{}
func (Nil[X]) isList() {}
func id[X comparable](x X) X { return x }
func main() {
x := Cons[int]{5, Nil[int]{}}
m := map[List[int]]string{}
m[x] = "Hi" // succeeds
fmt.Println(m[x]) // prints "Hi"
// fmt.Println(id(x)) // fails
}
Go 1.20 (February 2023)
comparable is the correct catch-all constraint for map keys.
All types that are comparable as per the Go spec, even if the comparison may panic at run time, can satisfy the comparable constraint. Your code will compile as expected in 1.20.
This finally fixes the inconsistency in previous Go version about spec-comparable types vs comparable types. See below for details.
Go 1.18 and 1.19
The predeclared comparable constraint is the correct constraint for map keys, however it can be instantiated only by strictly comparable types, i.e. types that support == and != (condition for being used as map keys) but won't panic at run time. This excludes interfaces1.
This is mentioned here: https://go.dev/ref/spec#Type_constraints
The predeclared interface type comparable denotes the set of all
non-interface types that are comparable. Specifically, a type T
implements comparable if:
T is not an interface type and T supports the operations == and != 2
T is an interface type and each type in T's type set implements comparable
Even though interfaces that are not type parameters can be compared (possibly causing a run-time panic) they do not implement comparable.
This is an important gotcha, because basic interface types normally do support the equality operators — what is compared is their dynamic types/values.
Therefore, your interface List[X] can be used as a map key directly, as in map[List[int]]string{}, but it does not implement comparable because it has an infinite type set (it has no terms, so any type implements it). And Cons doesn’t implement it either because it has a field of type List[X]. There is no "weaker" constraint for this.
Consider that constraints that embed comparable are also valid for map keys, so if you really need the method isList() in the function body, you can define a constraint like this, and have your lists-that-are-map-key structs implement that, instead of declaring an interface field:
// may use this as a constraint
type List interface {
comparable
isList() bool
}
1: the quote from the specs hints there are interface types that implement comparable, but it's effectively not possible to instantiate comparable with any interface at all: interfaces with only methods have an infinite type set, and interfaces with type terms can't be used anywhere except as constraints.
2: this rule actually doesn't cover non-interface types that support ==, like type S struct { data any }, but these types still can't instantiate comparable https://go.dev/play/p/N-pmE0XC-hB. This is a bug in the spec.
In the code below, I define a generic linked list. Go1.18 is happy to use an instance of the list as a key to a map. However, the last line, when uncommented, doesn't compile; I get the error:
Cons[int] does not implement comparable
Is there a weaker type constraint I can use that picks out those types that can be used as keys, or is this intended, or is it a compiler bug?
package main
import "fmt"
type List[X any] interface {
isList()
}
type Cons[X any] struct {
Data X
Next List[X]
}
func (Cons[X]) isList() {}
type Nil[X any] struct{}
func (Nil[X]) isList() {}
func id[X comparable](x X) X { return x }
func main() {
x := Cons[int]{5, Nil[int]{}}
m := map[List[int]]string{}
m[x] = "Hi" // succeeds
fmt.Println(m[x]) // prints "Hi"
// fmt.Println(id(x)) // fails
}
Go 1.20 (February 2023)
comparable is the correct catch-all constraint for map keys.
All types that are comparable as per the Go spec, even if the comparison may panic at run time, can satisfy the comparable constraint. Your code will compile as expected in 1.20.
This finally fixes the inconsistency in previous Go version about spec-comparable types vs comparable types. See below for details.
Go 1.18 and 1.19
The predeclared comparable constraint is the correct constraint for map keys, however it can be instantiated only by strictly comparable types, i.e. types that support == and != (condition for being used as map keys) but won't panic at run time. This excludes interfaces1.
This is mentioned here: https://go.dev/ref/spec#Type_constraints
The predeclared interface type comparable denotes the set of all
non-interface types that are comparable. Specifically, a type T
implements comparable if:
T is not an interface type and T supports the operations == and != 2
T is an interface type and each type in T's type set implements comparable
Even though interfaces that are not type parameters can be compared (possibly causing a run-time panic) they do not implement comparable.
This is an important gotcha, because basic interface types normally do support the equality operators — what is compared is their dynamic types/values.
Therefore, your interface List[X] can be used as a map key directly, as in map[List[int]]string{}, but it does not implement comparable because it has an infinite type set (it has no terms, so any type implements it). And Cons doesn’t implement it either because it has a field of type List[X]. There is no "weaker" constraint for this.
Consider that constraints that embed comparable are also valid for map keys, so if you really need the method isList() in the function body, you can define a constraint like this, and have your lists-that-are-map-key structs implement that, instead of declaring an interface field:
// may use this as a constraint
type List interface {
comparable
isList() bool
}
1: the quote from the specs hints there are interface types that implement comparable, but it's effectively not possible to instantiate comparable with any interface at all: interfaces with only methods have an infinite type set, and interfaces with type terms can't be used anywhere except as constraints.
2: this rule actually doesn't cover non-interface types that support ==, like type S struct { data any }, but these types still can't instantiate comparable https://go.dev/play/p/N-pmE0XC-hB. This is a bug in the spec.
I'm wondering why methods references and lambdas are not recognized as a Function. Why I need to write
Function<Integer, Integer> fun1 = i -> i+2;
Function<Integer, Integer> fun2 = i -> i*i;
fun1.compose(fun2).apply(4);
instead of
((Integer i) -> i*2).compose((Integer i) -> i+2).apply(4)
Lambda expressions have no intrinsic type; the following is an error:
Object lambda = x -> x;
Lambda expressions are poly expressions, which are expressions whose type is dependent on their context. In particular, a lambda expression derives its type from its target type, which must be a functional interface -- an interface with a single (non-Object) abstract method. The same lambda expression could have multiple types, depending on its target type:
Predicate<String> isEmpty = s -> s.isEmpty();
Function<String, Boolean> isEmpty = s -> s.isEmpty();
The interface Function is not part of the language, nor does it have any magic properties; it is merely an ordinary functional interface, just like Runnable or Predicate or Comparable. There's no reason the compiler could guess that you meant the lambda to target Function rather than some other type.
Further, you don't need to be a lambda to implement Function; you could be a named class or an anonymous class. Lambdas (and method refs) are a syntactically compact means of specifying instances of functional interfaces.
Here's my code:
trait UnaryOperator {
fn apply(&self, expr: Expression) -> Expression;
}
pub enum Expression {
UnaryOp(UnaryOperator, Expression),
Value(i64)
}
Which gives the following errors:
error: the trait 'core::marker::sized' is not implemented for type 'parser::UnaryOperator'
note: 'parser::UnaryOperator' does not have a constant size known at compile-time
I'm not sure how to accomplish what I want. I've tried:
trait UnaryOperator: Sized {
...
}
As well as
pub enum Expression {
UnaryOp(UnaryOperator + Sized, Expression),
...
}
And neither solved the issue.
I've seen ways to possibly accomplish what I want with generics, but then it seems like two expressions with different operators would be different types, but that's not what I want. I want all expressions to be the same type regardless of what the operators are.
Traits do not have a known size - they are unsized. To see why, check this out:
trait AddOne {
fn add_one(&self) -> u8;
}
struct Alpha {
a: u8,
}
struct Beta {
a: [u8; 1024],
}
impl AddOne for Alpha {
fn add_one(&self) -> { 0 }
}
impl AddOne for Beta {
fn add_one(&self) -> { 0 }
}
Both Alpha and Beta implement AddOne, so how big should some arbitrary AddOne be? Oh, and remember that other crates may implement your trait sometime in the future.
That's why you get the first error. There are 3 main solutions (note that none of these solutions immediately fix your problem...):
Use a Box<Trait>. This is kind-of-similar-but-different to languages like Java, where you just accept an interface. This has a known size (a pointer's worth) and owns the trait. This has the downside of requiring an allocation.
trait UnaryOperator {
fn apply(&self, expr: Expression) -> Expression;
}
pub enum Expression {
UnaryOp(Box<UnaryOperator>, Expression),
Value(i64)
}
Use a reference to the trait. This also has a known size (a pointer's worth two pointers' worth, see Matthieu M.'s comment). The downside is that something has to own the object and you need to track the lifetime:
trait UnaryOperator {
fn apply<'a>(&self, expr: Expression<'a>) -> Expression<'a>;
}
pub enum Expression<'a> {
UnaryOp(&'a UnaryOperator, Expression<'a>),
Value(i64)
}
Use a generic. This has a fixed size because each usage of the enum will have been specialized for the specific type. This has the downside of causing code bloat if you have many distinct specializations. Update As you point out, this means that Expression<A> and Expression<B> would have different types. Depending on your usage, this could be a problem. You wouldn't be able to easily create a Vec<Expression<A>> if you had both.
trait UnaryOperator {
fn apply<U>(&self, expr: Expression<U>) -> Expression<U>;
}
pub enum Expression<U>
where U: UnaryOperator
{
UnaryOp(U, Expression<U>),
Value(i64)
}
Now, all of these fail as written because you have a recursive type definition. Let's look at this simplification:
enum Expression {
A(Expression),
B(u8),
}
How big is Expression? Well, it needs to have enough space to hold... an Expression! Which needs to be able to hold an Expression.... you see where this is going.
You need to add some amount of indirection here. Similar concepts to #1 and #2 apply - you can use a Box or a reference to get a fixed size:
enum Expression {
A(Box<Expression>),
B(u8),
}
I was building a custom UI and I realize that for some reason I cannot do this.
protocol notImportant{
SegementButtons(segmentControl : VerticalSegmentControl) -> Int
}
//trying to use the function later in this fashion below
for index in 0...delegate?.segementButtonsCount(self)
Now I know there are many other solutions.
First of all, is this valid or must I provide a concrete number or variable?
Continued
Xcode shows an error
Binary operator '...' cannt be applied to oraands of type Int and Int?
I type cast the return value to an Int, changing the error to
Type Int does not conform to protocol SequenceType
Now it would be pretty cool if I could make this work without Xcode cutting itself.
delegate is an optional, therefore the type of the expression
delegate?.segmentButtonsCount(self)
is also an optional (which is nil if delegate == nil).
You can use optional binding to unwrap the delegate
if let theDelegate = delegate {
for index in 0 ..< theDelegate.segmentButtonsCount(self) {
// do something ...
}
}
or use the nil-coalescing operator ?? to provide a
default value:
for index in 0 ..< (delegate?.segmentButtonsCount(self) ?? 0) {
// do something ...
}
Note that since array indices are zero-based, you probably want to use the range operator ..< which
excludes the end element.