teaching machines

CS 330 Lecture 38 – Reflection and Metaprogramming

Agenda

  • what ?s
  • a testing framework in Ruby
  • checking for correct overrides in Java
  • handling wild calls in Ruby

Code

Certain.rb

#!/usr/bin/env ruby

module Certain
  def self.assert_equals fail_message, expected, actual
    if expected != actual
      puts fail_message
    end
  end

  def self.run suite
    instance = suite.new
    instance.methods.grep /^test_/ do |test|
      instance.send test
    end
    # puts 'All tests pass' 
  end
end

class Vector2
  def initialize x, y
    @x = x
    @y = y
  end

  def x
    @x
  end

  def y
    @y
  end

  def length
    Math.sqrt(@x * @x + @y * @y)
  end

  def normalize
    l = length
    @x /= l
    @y /= l
    self
  end

  def == other
    (x - other.x).abs < 0.00001 and (y - other.y).abs < 0.00001
  end

  def to_s
    "(#{x},#{y})"
  end
end

class TestVector2
  def test_subscript
    v = Vector2.new 5, 7
    Certain.assert_equals "Vector2.x yields incorrect x", 5, v.x
    Certain.assert_equals "Vector2.y yields incorrect y", 7, v.y
  end

  def test_length
    v = Vector2.new 3, 4
    Certain.assert_equals "Vector2.length wrong", 5, v.length
  end

  def test_normalize
    v = Vector2.new 10, 0
    Certain.assert_equals "Vector2.normalize computes bad vector", Vector2.new(1, 0), v.normalize
    v = Vector2.new 3, 4
    Certain.assert_equals "Vector2.normalize computes bad vector", Vector2.new(0.6, 0.8), v.normalize
  end
end

Certain.run TestVector2

Usurps.java

package cs330_egs;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Usurps {
}

Supertype.java

package cs330_egs;

public class Supertype {
  public Integer get(String s) {
    return 0;
  }
}

Subtype.java

package cs330_egs;

public class Subtype {
  @Usurps
  public Object get(String s) {
    return 0;
  }
  
  public void foo() {
  }
}

UsurpsCheck.java

package cs330_egs;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

public class UsurpsCheck {
  public static void main(String[] args) throws ClassNotFoundException {
    Class<?> subclass = Class.forName("cs330_egs.Subtype");
//    subclass.getSuperclass()
    Class<?> superclass = Class.forName("cs330_egs.Supertype");

    List<Method> subMethods = Arrays.asList(subclass.getDeclaredMethods());
//    System.out.println(subMethods);
    subMethods.stream().forEach(subMethod -> {
      Usurps annotation = subMethod.getAnnotation(Usurps.class);
      if (annotation != null) {
        // we know that this method is supposed to usurper!
        try {
          Method superMethod = superclass.getMethod(subMethod.getName(), subMethod.getParameterTypes());
          if (!superMethod.getReturnType().isAssignableFrom(subMethod.getReturnType())) {
            System.err.println(subMethod.getName() + " says it usurps, but doesn't have a covariant return type! Wah!");
            System.err.println(subMethod.getReturnType() + " must be <= " + superMethod.getReturnType());
          }
        } catch (Exception e) {
          System.err.println(subMethod.getName() + " has nothing to usurp!");
        }
      }
    });
  }
}

weather.json

{"coord":{"lon":-91.5,"lat":44.82},"message":0.0355,"country":"United States of America","sunrise":1399286929,"sunset":1399338988,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"base":"cmc stations","main":{"temp":281.88,"humidity":60,"pressure":1014,"temp_min":280.93,"temp_max":282.15},"wind":{"speed":2.57,"gust":5.65,"deg":87},"rain":{"3h":0},"clouds":{"all":92},"dt":1399287329,"id":5251436,"name":"Eau Claire","cod":200}

ezserial.rb

#!/usr/bin/env ruby

require 'json'

class Zerial
  def initialize path
    @contents = JSON.load(IO.read(path))
  end

  def method_missing name, *args
    if args.length == 0 and @contents.has_key? name.to_s
      @contents[name.to_s]
    elsif args.length == 1 and name[-1] == '='
      @contents[name.to_s[0...-1]] = args[0]
      puts @contents
    else
      super
    end
  end

  def to_s
    @contents.to_s
  end
end

o = Zerial.new 'weather.json'
puts Time.at(o.sunset)
o.sunrise = 6

Haiku

I look at myself
What am I capable of?
***Segmentation fault***

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *