Is it possible to tell mypy invariants? - mypy

I have built an 2d interval class. It either is a closed interval (containing both the left and the right border) or it is empty (left and right border are none). I realize that I can change that and it might simplify some things, but for this question it is good like that. It looks like this:
from typing import Any, Optional
class Interval:
def __init__(self, left: Optional[Any]=None, right: Optional[Any]=None):
if int(left is None) + int(right is None) not in [0, 2]:
raise RuntimeError("Either left and right are None, or neither.")
elif (left is not None) and (left > right):
raise RuntimeError("left may not be bigger than right")
self.left = left
self.right = right
def is_empty(self) -> bool:
return self.left is None
def union(self, other: Interval) -> Interval:
if self.is_empty():
return other
elif other.is_empty():
return self
assert self.left is not None
assert self.right is not None
assert other.left is not None
assert other.right is not None
if other.left > self.left:
other, self = self, other
...
return self # just for this slim example
As the developer, I can see some facts:
Either both, left and right, are None or both are not.
If is_empty returns False, then both left and right are not None
But for mypy 0.782 I have to add the 4 assert statements. Is there any way to annotate is_empty to give mypy a clue what this means for the value of self.left and self.right?

Related

Convert Take to Vec<T> or to &[T]

I have a struct called Cell
pub struct Cell {
x: X, // Some other struct
y: Y, // Some other struct
weight: usize,
}
I was trying to select the top preference cell out of some Row (a collection of Cells).
// Return the top n-matching cells with a positive weight
pub fn select_preference(&mut self) -> Vec<Cell> {
let top = 3;
self.sort();
// After sorting, omit the cells with weight = 0
// And select the top preference cells
self.cells.split(|cell| cell.weight() == 0).take(top)
}
However, I am getting an expected error actually:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:35:9
|
29 | pub fn select_preference(&mut self) -> Vec<Cell> {
| --------- expected `Vec<Cell>` because of return type
...
35 | self.cells.split(|cell| cell.weight() == 0).take(top)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Vec`, found struct `std::iter::Take`
|
= note: expected struct `Vec<Cell>`
found struct `std::iter::Take<std::slice::Split<'_, Cell, [closure#src/lib.rs:35:26: 35:32]>>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error
I don't know how to convert the Take into Vec<Cell> or &[Cell]. I know the Take is some sort of Iterator but unable to convert it :>
Rust Playground
Returning a vector of references to the relevant cells is, I think, the most idiomatic way to do this as it allows using iterators. You can then write:
pub fn select_preference(&mut self) -> Vec<&Cell> {
let top = 3;
self.sort();
self.cells.iter().filter(|cell| cell.weight() != 0).take(top).collect()
}
You can even sort only the iterator and not the Vec<Cell> itself.
Returning a slice is difficult as a slice must always refer to a contiguous part of a sequence. This will always be the case here due to the sorting of cells, however the iterator methods don't take this into account and so cannot be used. One way you could do it is:
pub fn select_preference(&mut self) -> &[Cell] {
let mut top = 3;
self.sort();
let mut ret = &self.cells[..cmp::min(self.cells.len(), top)];
while ret[ret.len() - 1].weight() == 0 {
ret = &ret[..ret.len() - 1];
}
ret
}
But it is probably evident that this is not very idiomatic Rust.
First, split is probably not what you want -- that creates an iterator where each element is a block of nonzero items. You probably want .iter().filter(|cell| cell.weight() != 0): iterate over elements of the vector, then filter out those that are nonzero.
To return a vector from an iterator, you need .collect(). However, this would give a Vec<&Cell> -- which doesn't quite match your function signature. Since you want a Vec<Cell>, you also need to clone the elements first to get new cells -- so you can use .cloned() first. That requires adding #[derive(Clone)] to Cell. This is the end result:
#[derive(Clone)]
pub struct Cell {
x: X,
y: Y,
weight: usize,
}
// Return the top n-matching cells with a positive weight
pub fn select_preference(&mut self) -> Vec<Cell> {
let top = 3;
self.sort();
// After sorting, omit the cells with weight = 0
// And select the top preference cells
self.cells.iter().filter(|cell| cell.weight() != 0).take(top).cloned().collect()
}
As a general rule, it's common to always derive Clone for structs of data.
Other designs are possible too -- you can return the Vec<&Cell> directly, as the other answer suggests. Finally, you could return an iterator instead of a Vec; here's how that looks:
pub fn select_preference(&mut self) -> impl Iterator<Item = &Cell> {
let top = 3;
self.sort();
// After sorting, omit the cells with weight = 0
// And select the top preference cells
self.cells.iter().filter(|cell| cell.weight() != 0).take(top)
}

Having problems with implementing Ledge grab

if $LedgeDetectorh.is_colliding()&&$LedgeDetectorh.get_collider() is Ledge:
if !$LedgeDetectorv.is_colliding():
isLedgeGrabbed = true
elif !$LedgeDetectorh2.is_colliding():
isLedgeGrabbed = false
if isLedgeGrabbed:
Motion = Vector2.ZERO
applyGravity = false
$LedgeDetectorv.enabled = false
$LedgeDetectorh2.enabled = false
if immediateInputLogic:
onLedgeGrabbedIdle = false
$LedgeTimer.start(5)
else:
onLedgeGrabbedIdle = true
if onLedgeGrabbedIdle:
Motion.y =0
Motion.x = 0
if $LedgeTimer.time_left ==0:
if $LedgeDetectorh2.enabled == true:
self.position.direction_to($LedgeDetectorh2.get_collision_point())* 50
elif $LedgeTimer.time_left > 0:
print("works")
Motion.y = lerp(Motion.y, -400,1)
Motion.x = lerp(Motion.x, 100 * direction,0.2)
elif !$LedgeDetectorh2.is_colliding():
$LedgeDetectorh.enabled = true
$LedgeDetectorv.enabled = true
$LedgeDetectorh2.enabled = true
applyGravity = true
problem is as far as I can tell raycast2ds keep on getting enabled and false all the time made some conditions to only enable them if is not colliding with wall still no luck
I'd say keep your multiple RayCast2D enabled. If your goal is to optimize the code, this is not the way (for that I'd suggest intersect_ray). However, first let us make sure it works correctly.
By the way, you can use is_stopped() instead of time_left.
I'm confused about what approach you are going for. I'm aware of a few approaches to do this:
Two horizontal RayCast2D, both horizontal, and parallel to each other, checking for the wall. One on the bottom and one on the top. If the bottom one detects a wall, but the top one does not, it means it there is a ledge. This is easy to implement, but does not give much information for animation/Inverse kinematics. You know there is a ledge, but not exactly where.
Again two RayCast2D. This time one is horizontal and detects a wall, and the other one is vertical, casting downwards from the top, and it is trying to find the floor where the player would stand if it climbed up the ledge. With the catch that you should position the vertical RayCast2D according to the position where the horizontal collided. Which reminds me, use force_update_transform and force_raycast_update if you moved the RayCast2D from code.
With collaboration from ledge objects. You can add small Area2Ds for each ledge on the designer, and if the player overlaps any of them, then it is on a ledge.
Again with collaboration from ledge objects, but this time they are floor extensions that the player can check with a RayCast2D directly upwards. So an horizontal ReyCast2D finds the wall, and a vertical RayCast2D finds the floor extensions that identifies the ledge. Taking care of collision layers and masks so these floor extensions do not interfere with anything else.
Having these ledge objects is great for animation (in particular in 3D), since you can get very precise positions for that inverse kinematics. But it means that a designer has to make sure they are placed in the world, and in any area without them the player can't climb. Don't tell me, you have seen that in games.
You already have ledge objects, don't you? I think it would be easy to add an Area2D to them and use the third approach I listed above, which does not require RayCast2D at all.
So the Ledge would be or have an Area2D that connects "body_entered" to the Area2D itself. When the signal triggers, the Area2D will get a reference to the object that entered. Make sure it is the player, and call a method on it, letting it know there is a ledge it can grab (it can pass its own position as parameter so the player script can snap to it).
Addendum
Let us go over the third approach as described. There will be an Area2D for each ledge. This Area2D will have a script, and the class name is Ledge:
class_name Ledge extends Area2D
So the Ledge would be or have an Area2D that connects "body_entered" to the Area2D itself. When the signal triggers, the Area2D will get a reference to the object that entered.
I'll do the signal connections from code. As to avoid any mistake in connecting it via the editor.
class_name Ledge extends Area2D
func _ready() -> void:
connect("body_entered", self, "_on_body_entered")
func _on_body_entered(body:Node) -> void:
pass
Here body is the reference of the body that entered.
Make sure it is the player
Hopefully you can use collision masks and layers to narrow the checks. I don't know how you would identify the player, but I'll venture to guess it has a class name Player, so I can check like this:
class_name Ledge extends Area2D
func _ready() -> void:
connect("body_entered", self, "_on_body_entered")
func _on_body_entered(body:Node) -> void:
if body is Player:
pass
and call a method on it, letting it know there is a ledge it can grab (it can pass its own position as parameter so the player script can snap to it).
So you can either have the Area2D pass itself, or pass its global position. In the code below go for the global position. I'll call the method it calls on player on_ledge, which also means we can check for the method with has_method (I'll replace the check for that).
class_name Ledge extends Area2D
func _ready() -> void:
connect("body_entered", self, "_on_body_entered")
func _on_body_entered(body:Node) -> void:
if body.has_method("on_legde"):
body.on_ledge(global_position)
And you would, of course, add that method to the player script:
func on_ledge(ledge_global_position:Vector2) -> void:
print(ledge_global_position)
There you can snap the player position, play an animation or whatever is appropriate.
Perhaps passing the Area2D itself is better, we could do this:
class_name Ledge extends Area2D
func _ready() -> void:
connect("body_entered", self, "_on_body_entered")
func _on_body_entered(body:Node) -> void:
if body.has_method("on_legde"):
body.on_ledge(self)
And then on the player:
var current_ledge:Area2D = null
func on_ledge(ledge:Area2D) -> void:
current_ledge = ledge
func _physics_process(_delta:float) -> void:
if not is_instance_valid(current_ledge) or not current_ledge.overlaps_body(self):
current_ledge = null
if current_ledge != null:
var current_ledge_position = current_ledge.global_position
# do something with the ledge position

Type annotation for Callables with *args

I know how to type-annotate functions which take arguments.
def function(text: str, *args: int) -> None:
print(text)
for arg in args:
print(arg)
My problem, however, is that I don't know what this looks like when another function takes it as an input:
def introduce(foo: Callable[[str, XXX], None], text: str, *args: int) -> None:
print("Enter...")
foo(text, *args)
print("Exit...")
introduce(function, "Hello, World!", (1, 2, 3))
Naturally, the XXX represents whatever it is that *args is supposed to be. However, I can't figure that out, and I've been unable to intuit what belongs there. I don't think it would be int, as that would suggest exactly one proceeding value. Experimentation has failed me here.

Idiomatic Scala way to come a list of Eithers into a Left or Right depending on the lists content

I have a list of Eithers
val list: List[Either[String, Int]] = List(Right(5), Left("abc"), Right(42))
As a result I want a Right if everything in the list is a Right else I want a Left. This sounds like the list should be biased (e.g. use Try instead) but let's assume it isn't or shouldn't be.
The content of the resulting Right or Left will always be the same (e.g. a string, see blow) - only the Container shall differ. E.g. with the list above we want to create a string from this list so the result should be of a Left like Left("Right(5) -> Left(abc) -> Right(42)"). If we had another Right(12) instead of the Left("abc") it should be Right("Right(5) -> Right(12) -> Right(42)").
I could manually check if there is at least one Left in the list and branch with an if to create a Left or a Right as result, but I wonder: is there a more Scala-like way to do it?
You can achieve that in a functional way with a foldLeft.
Basically in the fold function you fold using the rules
Right + Right => Right
SomethingElse => Left
In scala code:
def string(a: Any, b: Any): String = if (a.toString.isEmpty)
s"${b.toString}" else s"${a.toString} -> ${b.toString}"
def transform[T, U](list: List[Either[T, U]]) =
list.foldLeft[Either[String, String]](Right("")) {
case (Right(a), bb # Right(b)) => Right(string(a, bb))
case (Right(a), bb) => Left(string(a, bb))
case (Left(a), bb) => Left(string(a, bb))
}
transform(List(Right(5), Left("abc"), Right(42)))
// prints Left(Right(5) -> Left(abc) -> Right(42))
transform(List(Right(5), Right("abc"), Right(42)))
// prints Right(Right(5) -> Right(abc) -> Right(42))
Either is actually used in sence of Value or Error in Haskell.
But scala Either's design does not allow it's use as "Error Monad". That was fixed in scalaz library via \/ type. Thing you want to basically implemented via sequence extension of Traverse types.
So prefix your code with:
import scalaz._
import Scalaz._
and get your result like
type Error[A] = String \/ A
val mlist = (list map \/.fromEither).sequence[Error, Int]
or even oneline type currying:
val mlist = (list map \/.fromEither).sequence[({type E[A] = String \/ A})#E, Int]

Mutable vectors in struct

I'm trying to get a graph clustering algorithm to work in Rust. Part of the code is a WeightedGraph data structure with an adjacency list representation. The core would be represented like this (shown in Python to make it clear what I'm trying to do):
class Edge(object):
def __init__(self, target, weight):
self.target = target
self.weight = weight
class WeightedGraph(object):
def __init__(self, initial_size):
self.adjacency_list = [[] for i in range(initial_size)]
self.size = initial_size
self.edge_count = 0
def add_edge(self, source, target, weight):
self.adjacency_list[source].append(Edge(target, weight))
self.edge_count += 1
So, the adjacency list holds an array of n arrays: one array for each node in the graph. The inner array holds the neighbors of that node, represented as Edge (the target node number and the double weight).
My attempt to translate the whole thing to Rust looks like this:
struct Edge {
target: uint,
weight: f64
}
struct WeightedGraph {
adjacency_list: ~Vec<~Vec<Edge>>,
size: uint,
edge_count: int
}
impl WeightedGraph {
fn new(num_nodes: uint) -> WeightedGraph {
let mut adjacency_list: ~Vec<~Vec<Edge>> = box Vec::from_fn(num_nodes, |idx| box Vec::new());
WeightedGraph {
adjacency_list: adjacency_list,
size: num_nodes,
edge_count: 0
}
}
fn add_edge(mut self, source: uint, target: uint, weight: f64) {
self.adjacency_list.get(source).push(Edge { target: target, weight: weight });
self.edge_count += 1;
}
}
But rustc gives me this error:
weightedgraph.rs:24:9: 24:40 error: cannot borrow immutable dereference of `~`-pointer as mutable
weightedgraph.rs:24 self.adjacency_list.get(source).push(Edge { target: target, weight: weight });
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So, 2 main questions:
1. How can I get the add_edge method to work?
I'm thinking that WeightedGraph is supposed to own all its inner data (please correct me if I'm wrong). But why can add_edge not modify the graph's own data?
2. Is ~Vec<~Vec<Edge>> the correct way to represent a variable-sized array/list that holds a dynamic list in each element?
The tutorial also mentions ~[int] as vector syntax, so should it be: ~[~[Edge]] instead? Or what is the difference between Vec<Edge> and ~[Edge]? And if I'm supposed to use ~[~[Edge]], how would I construct/initialize the inner lists then? (currently, I tried to use Vec::from_fn)
The WeightedGraph does own all its inner data, but even if you own something you have to opt into mutating it. get gives you a & pointer, to mutate you need a &mut pointer. Vec::get_mut will give you that: self.adjacency_list.get_mut(source).push(...).
Regarding ~Vec<Edge> and ~[Edge]: It used to be (until very recently) that ~[T] denoted a growable vector of T, unlike every other type that's written ~... This special case was removed and ~[T] is now just a unique pointer to a T-slice, i.e. an owning pointer to a bunch of Ts in memory without any growth capability. Vec<T> is now the growable vector type.
Note that it's Vec<T>, not ~Vec<T>; the ~ used to be part of the vector syntax but here it's just an ordinary unique pointer and represents completely unnecessary indirection and allocation. You want adjacency_list: Vec<Vec<Edge>>. A Vec<T> is a fully fledged concrete type (a triple data, length, capacity if that means anything to you), it encapsulates the memory allocation and indirection and you can use it as a value. You gain nothing by boxing it, and lose clarity as well as performance.
You have another (minor) issue: fn add_edge(mut self, ...), like fn add_edge(self, ...), means "take self by value". Since the adjacency_list member is a linear type (it can be dropped, it is moved instead of copied implicitly), your WeightedGraph is also a linear type. The following code will fail because the first add_edge call consumed the graph.
let g = WeightedGraph::new(2);
g.add_edge(1, 0, 2); // moving out of g
g.add_edge(0, 1, 3); // error: use of g after move
You want &mut self: Allow mutation of self but don't take ownership of it/don't move it.
get only returns immutable references, you have to use get_mut if you want to modify the data
You only need Vec<Vec<Edge>>, Vec is the right thing to use, ~[] was for that purpose in the past but now means something else (or will, not sure if that is changed already)
You also have to change the signature of add_edge to take &mut self because now you are moving the ownership of self to add_edge and that is not what you want

Resources