public static Object eval(Object obj, Map env) {
		if (obj == null)
			return null;
		if (obj instanceof Number)
			return obj;
		if (obj instanceof Boolean)
			return obj;
		if (obj instanceof Symbol) {
			if (!env.containsKey(obj))
				throw new RuntimeException("Cannot find symbol " + obj + " in env " + env);
			return env.get(obj);
		}

		// Assume s-expression
		List sexp = (List) obj;

		// Is it a special form?
		if (sexp.get(0) instanceof Symbol 
				&& asList("def", "if", "fn", "defn").contains(((Symbol) sexp.get(0)).name))
			return evalSpecialForm(sexp, env);
		
		// Eval args
		List args = new ArrayList();
		for (Object o : sexp.subList(1, sexp.size()))
			args.add(eval(o, env));
		
		// Call function
		Fn fn = (sexp.get(0) instanceof Symbol) ? 
				(Fn) env.get((Symbol) sexp.get(0)) : 
				(Fn) eval(sexp.get(0), env);
		return fn.invoke(args.toArray());
	}