import java.util.List;
import java.util.ArrayList;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

/* Code isn't a first-class concept in Java.  To pass code around,
 * it has to be an Object.  All objects of the same class share the
 * same methods, so each hunk of code must be its own class.  These
 * are least painfully created as one-method anonymous inner classes.
 *
 * Each reference to these must be fully typed, so you may end up
 * declaring an interface for every function that accepts a hunk of
 * code as an argument.  At the very least, you will need one very
 * loose generic interface per airity.
 */


interface MapFunction<InputType,OutputType>
{
	public OutputType f (InputType x) ;
}

class Map
{
	public static <InputType,OutputType>
	List<OutputType> map (MapFunction<InputType,OutputType> f, List<InputType> l)
	{
		List<OutputType> result = new ArrayList<OutputType> ();
		for (InputType x : l)
		{
			result.add(f.f(x));
		}
		return result;
	}
}


/* Then comes the problem of exceptions.  In the above implementation,
 * the passed hunk of code must not throw any checked exceptions because
 * none are listed in the interface.  In order to allow for exceptions,
 * you have to provide additional copy/paste definitions of each function
 * that accepts code arguments with different exception signatures.  We
 * can use loose generics to reduce the number of these that must be
 * provided to one per exception airity.
 */



interface MapFunction1Throw<InputType,OutputType,Throw1 extends Throwable>
{
	public OutputType f (InputType x) throws Throw1 ;
}

class Map1Throw
{
	public static <InputType,OutputType,Throw1 extends Throwable>
	List<OutputType> map (MapFunction1Throw<InputType,OutputType,Throw1> f, List<InputType> l) throws Throw1
	{
		List<OutputType> result = new ArrayList<OutputType> ();
		for (InputType x : l)
		{
			result.add(f.f(x));
		}
		return result;
	}
}

/* Repeat with MapFunction2Throw and Map2Throw, MapFunction3Throw and Map3Throw, etc. */


class Nothing {}

public class Mapping
{
	public static void main (String[] args)
	{
		/* Double and print some numbers.  Compare to scheme:
		 * (map write (map (lambda (x) (* x 2)) '(1 2 3)))
		 */
		Map.map(new MapFunction<Integer,Nothing> () {
				public Nothing f (Integer x) {
					System.out.println(x);
					return null;
				}
			},
			Map.map(new MapFunction<Integer,Integer> () {
					public Integer f (Integer x) {
						return x * 2;
					}
				},
				new ArrayList<Integer>(){{
					add(1);
					add(2);
					add(3);
				}}
			)
		);

		/* Open some files */
		try {
			List<FileInputStream> files = 
			Map1Throw.map(new MapFunction1Throw<String,FileInputStream,FileNotFoundException> () {
					public FileInputStream f (String filename) throws FileNotFoundException {
						return new FileInputStream(filename);
					}
				},
				new ArrayList<String>(){{
					add("Mapping.java");
					add("nosuchfile");
				}}
			);
		} catch (FileNotFoundException e) {
			System.err.println(e);
		}
	}
}

