
function $const(x){ return function(){ return x; }; }

function $id(x){ return x; }

function $not(f){ return function(x){ return !f(x); }; }

function $and(/* fns... */){
    var fns = $A(arguments);
    return function(x){
        for(var i=0; i < fns.length; i++){
            if(!fns[i](x)){ return false; }
        }
        return true;
    };
}

function $or(/* fns... */){
    var fns = $A(arguments);
    return function(x){
        for(var i=0; i < fns.length; i++){
            if(fns[i](x)){ return true; }
        }
        return false;
    };
}

function $compose(/* fns... */) {
    var fns = $A(arguments.length === 1 && $type(arguments[0]) === 'array'  ? 
                   arguments[0] : arguments).map($lambda);

    var len = fns.length;
    return function(/*arguments...*/){
        var args = $A(arguments);
        for(var i = len-1; i >= 0; i--){
            args = [fns[i].apply(this, args)];
        }
        return args[0];
    };
}

function $sequence(/*fns... */){
    return $compose( arguments.reverse() );
}

function $flip(fn){
    return function(a, b){
        return fn(b, a);
    };
}

function $curryBind(fn, bind /*, args... */ ){
    var args = $A(arguments).slice(2);
    return function(){
        fn.apply(bind, args.concat(arguments.slice(0)));
    };
}

function $curry(fn /*, args... */ ){
    var args = $A(arguments).slice(1);
    return function(){
        var a = args.concat( $A(arguments).slice(0));
        return fn.apply(this, a);
    };
}

function $rcurry(fn /*, args... */ ){
    var args = Array.slice(arguments, 1);
    return function(){
        var a = Array.slice(arguments,0).concat(args);
        return fn.apply(this, a);
    };
}

Function.implement({

    map: function(fn){ return $compose(fn, this); },

    curry: function(){ 
            var args = $A(arguments);
            var fn = this;
            return function(){
                var a = args.concat( $A(arguments).slice(0));
                return fn.apply(fn, a);
        };
    },

    rcurry: function(){
        var args = Array.slice(arguments, 0);
        var fn = this;
        return function(){
            var a = Array.slice(arguments,0).concat(args);
            return fn.apply(this, a);
        };
    },

    flip: function(){ return $flip(this); }

});

function $valueForField(obj, fieldName){
    var names = fieldName.split('.');
    var cur = obj;
    for(var i=0; i < names.length && cur; i++){
        cur = cur[names[i]];
    }
    return cur;
}

function arrayFn(arr){
    return function(i){
        return arr[i];
    }
}

