Distinguish Interfaces of the Arguments

Objective: Define a fucntion that:

  • For arguments that implements get_color method, calls this method and prints the result.
  • For remaining arguments prints transparent.

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

Python

Abstract class can be used to define interface:

from abc import ABC, abstractmethod
from typing import Any

class Colorful(ABC):
    @abstractmethod
    def get_color(self):
        pass

class Apple(Colorful):
    def get_color(self):
        return "green"

class Sky(Colorful):
    def get_color(self):
        return "blue"

def get_color(x: Any):
    if isinstance(x, Colorful):
        return x.get_color()
    else:
        return "transparent"

# types known in advance
print(f"{get_color(Apple())=}")
print(f"{get_color(Sky())=}")
print(f"{get_color(123)=}")
print(f"{get_color('example')=}")

# types determined in runtime
items = [Apple(), Sky(), 123,  "example"]
for item in items:
    color = get_color(item)
    print(f"{item} is {color}")

Alternatively, existence of the methods can be checked in runtime without defining interfaces:

from typing import Any

class Apple:
    def get_color(self):
        return "green"

class Sky:
    def get_color(self):
        return "blue"


def get_color(x: Any):
    if hasattr(x, 'get_color'):
        return x.get_color()
    return "transparent"

Yet another way of implementing get_color:

def get_color(x: Any):
    try:
        get_color_ = x.get_color
    except AttributeError:
        return "transparent"
    else:
        return get_color_()

Rust

#![feature(auto_traits)]
#![feature(negative_impls)]

auto trait Transparent {
}

trait Colorful {
    fn get_color(&self) -> &'static str;
}

struct Apple;

impl !Transparent for Apple {}
impl Colorful for Apple {
  fn get_color(&self) -> &'static str {
    "green"
  }
}
    
struct Sky;

impl !Transparent for Sky {}
impl Colorful for Sky {
  fn get_color(&self) -> &'static str {
    "blue"
  }
}

impl<T> Colorful for T
where
    T: Transparent
{
  fn get_color(&self) -> &'static str {
    "transparent"
  }
}

fn get_color(x: impl Colorful) -> &'static str {
    x.get_color()
}


fn main() {
    // types known in compile time
    dbg!( get_color(Apple{}) );
    dbg!( get_color(Sky{}) );
    dbg!( get_color(123) );
    dbg!( get_color("example") );

    // types determined in runtime
    let items: Vec<&dyn Colorful> = vec![&Apple{}, &Sky{}, &123, &"example"];
    for item in items {
        // Below we print only color of the item. Printing the items themself
        // needs `fmt` to be implemented and is out of the scope of this example.
        println!("item is {}", item.get_color() );
    }
}

Crystal

Abstract struct can be used to define interface:

abstract struct Colorful
  abstract def get_color
end

struct Apple < Colorful
  def get_color
    "green"
  end
end

struct Sky < Colorful
  def get_color
    "blue"
  end
end

def get_color(x : Colorful)
  return x.get_color
end

def get_color(x)
  return "transparent"
end

# types known in compile time
p! get_color Apple.new
p! get_color Sky.new
p! get_color 123
p! get_color "example"

# types determined in runtime
items = [Apple.new, Sky.new, 123, "example"]
items.each do |item|
  color = get_color(item)
  puts "#{item} is #{color}"
end

Alternatively, existence of the methods can be checked in runtime without defining interfaces:

struct Apple
  def get_color
    "green"
  end
end

struct Sky
  def get_color
    "blue"
  end
end

def get_color(x)
  if x.responds_to?(:get_color)
    return x.get_color
  end
  return "transparent"
end