var sgFun = {
    map: function(fun, list) {
	var res = new Array();
	if (sgFun._isArray(list)) {
	    for (var i = 0, len = list.length; i < len; i++)
		res[i] = fun(list[i]);
	} else {
	    for (field in list)
		res[i] = fun(list[field]);
	}
	return res;
    },

    flatten: function(list) {
	var res = new Array();
	for (var i=0, len=list.length; i<len; i++) {
	    if (sgFun._isArray(list[i])) {
		res = res.concat(sgFun.flatten(list[i]));
	    } else {
		res[res.length] = list[i];
	    }
	}
	return res;
    },

    foldl: function(fun, list, acc) {
	var result = acc;
	for (var i = 0, len = list.length; i < len; i++) {
            result = fun(list[i], result);
	}
	return result;
    },

    foldr: function(fun, list, acc) {
	var result = acc;
	for (var i = list.length-1; i >= 0; i--) {
            result = fun(list[i], result);
	}
	return result;
    },

    filter: function(fun, list) {
	var result = [];
	for (var i = 0, len = list.length; i < len; i++) {
            if (fun(list[i]))
		result[result.length] = list[i];
	}
	return result;
    },


    partial: function(fun) {
	var fixed=arguments;
	var res = function() {
	    var newargs = Array();
	    for (var arg in fixed) {
		newargs[newargs.length] = arg;
	    }
	    for (var arg in arguments) {
		newargs[newargs.length] = arg;
	    }
	    return apply(fun, newargs);
	}
	return res;
    },

    any: function(fun, list) {
	for (var i = 0, len = list.length; i < len; i++) {
            if (fun(list[i]))
		return true;
	}
	return false;
    },

    all: function(fun, list) {
	for (var i = 0, len = list.length; i < len; i++) {
            if (!fun(list[i]))
		return false;
	}
	return true;
    },

    or: function(list) {
	return this.any(function(el) {return el;}, list);
    },
    
    and: function(list) {
	return this.all(function(el) {return el;}, list);
    },

    // Y Combinator
    Y: function(le) {
	return function (f) {
            return f(f);
	}(function (f) {
            return le(function (x) {
		return f(f)(x);
            });
	});
    },

    /* list comprehensions */
    lc: function(expression) {
	var parsed = this._parse_lc(expression);
	var expr = parsed[1];
	var unbound = parsed[2]; //FIXME check if unbound is a list of elements
	var theList = parsed[3];
	try {
	    list = eval(theList);
	} catch (e) {
	    if (e instanceof ReferenceError)
		throw new SyntaxError("SyntaxError: undefined list "+theList);
	}
	var result = new Array(list.length);
	for (var i = 0, len = list.length; i < len; i++) {
	    result[i] = eval(expr.replace(unbound, list[i]));
	}
	return result;
    },
    _parse_lc: function(expession) {
	var result = [];
	var re = /([A-Za-z0-9_()\[\],]+) for (\w+) in ([A-Za-z0-9_()\[\],]+)/;
	var match = re.exec(expession);
	if (! match) {
	    throw new SyntaxError("Invalid syntax: "+expession);
	}
	return match;
    },
    _isArray: function(el) {
	var s = typeof el;
	if (s === 'object') {
            if (el) {
		if (typeof el.length === 'number' &&
                    !(el.propertyIsEnumerable('length')) &&
                    typeof el.splice === 'function') {
                    return true;
		}
            }
	}
	return false;
    }
};
