Distinguish Types of the Arguments

Objective: Define a fucntion that:

  • Returns inverted value for boolean argument.
  • Returns opposite value for integer argument.
  • Returns object with coordinates swapped for argument of Point type.
  • Prints warning and returns value of the argumen for any other type.

Test it where type of the argument is known in advance time and when it determined only in runtime.

Python

from typing import Union
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

def flip(a: Union[bool, int, Point]):
    
    if isinstance(a, bool):
        return not a
    
    if isinstance(a, int):
        return -a
    
    if isinstance(a, Point):
        return Point(a.y, a.x)

    print(f"Warning: Type {a.__class__.__name__} not flippable");
    return a

# types known in advance
print(f"{flip(False)=}")
print(f"{flip(3)=}")
print(f"{flip(Point(5, 7))=}")
print(f"{flip(33.3)=}")

# types determined in runtime
items = [False, 3,  Point(5, 7), 33.3]
for item in items:
    flipped = flip(item)
    print(f"{item} flipped is {flipped}")

Rust

#![feature(auto_traits)]
#![feature(negative_impls)]
use std::ops::Neg;
use std::any::type_name;

auto trait Flippable {
}

impl !Flippable for bool {}
impl !Flippable for i32 {}
impl !Flippable for i64 {}

#[derive(Debug)]
struct Point {
    x: i32,
    y: i32
}

fn flip_num<T: Neg<Output = T>> (a: T) -> T {
    -a
}

trait Flip {
    fn flip(a: Self) -> Self;
}

impl Flip for bool {
    fn flip(a: bool) -> bool {
        !a
    }
}

impl Flip for i32 {
    fn flip(a: i32) -> i32 {
        flip_num(a)
    }
}

impl Flip for i64 {
    fn flip(a: i64) -> i64 {
        flip_num(a)
    }
}

impl Flip for Point {
    fn flip(a: Point) -> Point {
        Point{x: a.y, y: a.x}
    }
}

impl<T> Flip for T
where
    T: Flippable
{
    fn flip(a: T) -> T {
        println!("Warning: Type {} not flippable", type_name::<T>() );
        a
    }
}

fn flip<T: Flip>(a: T) -> T {
    Flip::flip(a)
}

fn main() {
    // types known in compile time
    dbg!( flip(true));
    dbg!( flip(3 as i32) );
    dbg!( flip(33 as i64) );
    dbg!( flip(Point{x: 5, y: 7}) );
    dbg!( flip(33.3) );
    
    // types determined in runtime
    /* Error: not possible to use it this way
    let items: Vec<&dyn std::any::Any> = vec![&true, &(3 as i32), &(3 as i64), &Point{x: 5, y: 7}, &33.3];
    for item in items {
        flip(*item);
    }
    */
}

Crystal

struct Point
  property x : Int32
  property y : Int32

  def initialize(@x, @y)
  end
end

def flip(a : Bool)
  !a
end

# implementation for all integer types
def flip(a : Int)
  -a
end

def flip(a : Point)
  Point.new(a.y, a.x)
end

# implementation for all remaining types
def flip(a)
  puts "Warning: Type #{a.class} not flippable"
  a
end

# types known in compile time
p! flip false
p! flip 3_i32
p! flip 33_i64
p! flip Point.new(5, 7)
p! flip 33.3

# types determined in runtime
items = [false, 3_i32, 3_i64, Point.new(5, 7), 33.3]
items.each do |item|
  flipped = flip item
  puts "#{item} flipped is #{flipped}"
end