fn bind_monad(args_so_far: Vec<CellRef>, monad: CellRef, remaining_args: CellRef, bindings: SymbolBindings) -> BindResult<CellRef> {
    // The remainder of the function will need to be evaluated in a function
    let mut interior_frame  = bindings.push_new_frame();

    // The first parameter of the flat_map function is the value of the monad argument
    let monad_value_cell    = interior_frame.alloc_cell();

    // Next parameters are bound from the closure and are the arguments so far (including the function, if present)
    let other_arguments     = args_so_far.iter().map(|arg| (interior_frame.alloc_cell(), arg.reference_type())).collect::<Vec<_>>();

    // Generate a partially-bound statement using these arguments (remaining_args are still unbound and go on the end)
    let monad_fn            = SafasCell::List(SafasCell::FrameReference(monad_value_cell, 0, ReferenceType::Value).into(), remaining_args);
    let mut monad_fn        = Arc::new(monad_fn);
    for (cell_id, reference_type) in other_arguments.iter().rev() {
        monad_fn = SafasCell::List(SafasCell::FrameReference(*cell_id, 0, *reference_type).into(), monad_fn).into();
    }

    // Bind this function
    // (Note: the args_so_far are all frame references here so they should bind to themselves, saving us some issues with rebinding)
    let bound_monad_fn                      = bind_statement(monad_fn, interior_frame);
    let (bound_monad_fn, interior_frame)    = match bound_monad_fn { Ok(fun) => fun, Err((err, interior_frame)) => return Err((err, interior_frame.pop().0)) };

    // If the result is not bound to a monad expression, we need to wrap the result
    let is_inner_expression                 = bound_monad_fn.reference_type() != ReferenceType::Monad;

    let (bound_monad_fn, interior_frame)    = if is_inner_expression {
        // The inner result of a monadic expression needs to be wrapped so the return value is itself a monad (note we don't bind the bound monad fn itself here)
        // TODO: add a way to generate a bound wrap expression more simply
        let mut bindings                    = interior_frame;
        let bound_monad_fn                  = SafasCell::List(bound_monad_fn, NIL.clone()).into();
        bindings.args                       = Some(SafasCell::List(bound_monad_fn, NIL.clone()).into());
        let (bindings, bound_monad_fn)      = wrap_keyword().bind(bindings);

        match bound_monad_fn {
            Ok(bound_monad_fn)  => (SafasCell::BoundSyntax(bound_monad_fn).into(), bindings),
            Err(_err)           => unimplemented!()
        }
    } else {
        (bound_monad_fn, interior_frame)
    };

    // Compile to a closure (this generates the function passed to FlatMap later on)
    let monad_flat_map                      = compile_statement(bound_monad_fn);
    let monad_flat_map                      = match monad_flat_map { Ok(flat_map) => flat_map, Err(err) => return Err((err, interior_frame.pop().0)) };
    let interior_frame_size                 = interior_frame.num_cells;

    // Pop the interior frame and bring in any imports
    let (bindings, imports)                 = interior_frame.pop();

    // Add any imports to the list of arguments (all the arguments get imported into our closure)
    let mut other_arguments                 = other_arguments.into_iter().map(|(cell_id, _ref_type)| cell_id).collect::<Vec<_>>();
    let mut args_so_far                     = args_so_far;
    let mut bindings                        = bindings;

    for (symbol_value, import_into_cell_id) in imports.into_iter() {
        match &*symbol_value {
            SafasCell::FrameReference(_our_cell_id, 0, _type) => {
                // Cell from this frame
                other_arguments.push(import_into_cell_id);
                args_so_far.push(symbol_value);
            },

            SafasCell::FrameReference(their_cell_id, frame_count, their_type) => {
                // Import from a parent frame
                let our_cell_id = bindings.alloc_cell();
                bindings.import(SafasCell::FrameReference(*their_cell_id, *frame_count, *their_type).into(), our_cell_id);

                other_arguments.push(import_into_cell_id);
                args_so_far.push(SafasCell::FrameReference(our_cell_id, 0, *their_type).into());
            },

            _ => panic!("Don't know how to import this type of symbol")
        }
    }

    // Arguments are loaded in reverse order from the stack, so we need to reverse their order
    other_arguments.reverse();

    // Bind to a closure
    let monad_flat_map                      = monad_flat_map.to_actions().collect::<Vec<_>>();
    let monad_flat_map                      = StackClosure::new(monad_flat_map, other_arguments, interior_frame_size, 1, true);

    // Convert things to the final result
    let args_so_far                         = SafasCell::list_with_cells(args_so_far);
    let monad_flat_map                      = SafasCell::FrameMonad(Box::new(monad_flat_map)).into();

    // Result is a list starting with the monad
    let result                              = SafasCell::list_with_cells(vec![monad, args_so_far, monad_flat_map]).into();

    Ok((result, bindings))
}
///
/// Given a partially bound function with a monad parameter, rewrites it as a flat_map binding
/// 
/// Say we are evaluating the call (foo x) where 'x' is a monad. This will map this to (flat_map (fun (x) (foo x)) x),
/// returning a new monad as the result of the call. (This is equivalent to 'do' syntax in languages like Haskell but
/// taking account of SAFAS's use of dynamic types instead of static ones)
/// 
/// That is, we can use the monad's `flat_map` function to get the value wrapped in it, so we rewrite the function call
/// such that it occurs inside the `flat_map` call. The function is assumed not to return a monad itself in this case,
/// so the return value will be wrapped into a nil monad (which will get processed by the parent flat_map function)
///
Given a partially bound function with a monad parameter, rewrites it as a flat_map binding
Say we are evaluating the call (foo x) where 'x' is a monad. This will map this to (flat_map (fun (x) (foo x)) x),
returning a new monad as the result of the call. (This is equivalent to 'do' syntax in languages like Haskell but
taking account of SAFAS's use of dynamic types instead of static ones)

That is, we can use the monad's `flat_map` function to get the value wrapped in it, so we rewrite the function call
such that it occurs inside the `flat_map` call. The function is assumed not to return a monad itself in this case,
so the return value will be wrapped into a nil monad (which will get processed by the parent flat_map function)
Given a partially bound function with a monad parameter rewrites it as a flat_map binding Say we are evaluating the call ( foo x ) where ' x ' is a monad . This will map this to ( flat_map ( fun ( x ) ( foo x )) x ) returning a new monad as the result of the call . ( This is equivalent to ' do ' syntax in languages like Haskell but taking account of SAFAS ' s use of dynamic types instead of static ones ) That is we can use the monad ' s ` flat_map ` function to get the value wrapped in it so we rewrite the function call such that it occurs inside the ` flat_map ` call . The function is assumed not to return a monad itself in this case so the return value will be wrapped into a nil monad ( which will get processed by the parent flat_map function )
fn bind_monad(args_so_far: Vec<CellRef>, monad: CellRef, remaining_args: CellRef, bindings: SymbolBindings) -> BindResult<CellRef> {
    // The remainder of the function will need to be evaluated in a function
    let mut interior_frame  = bindings.push_new_frame();
    // The first parameter of the flat_map function is the value of the monad argument
    let monad_value_cell    = interior_frame.alloc_cell();
    // Next parameters are bound from the closure and are the arguments so far (including the function, if present)
    let other_arguments     = args_so_far.iter().map(|arg| (interior_frame.alloc_cell(), arg.reference_type())).collect::<Vec<_>>();
    // Generate a partially-bound statement using these arguments (remaining_args are still unbound and go on the end)
    let monad_fn            = SafasCell::List(SafasCell::FrameReference(monad_value_cell, 0, ReferenceType::Value).into(), remaining_args);
    let mut monad_fn        = Arc::new(monad_fn);
    for (cell_id, reference_type) in other_arguments.iter().rev() {
        monad_fn = SafasCell::List(SafasCell::FrameReference(*cell_id, 0, *reference_type).into(), monad_fn).into();
    }
    // Bind this function
    // (Note: the args_so_far are all frame references here so they should bind to themselves, saving us some issues with rebinding)
    let bound_monad_fn                      = bind_statement(monad_fn, interior_frame);
    let (bound_monad_fn, interior_frame)    = match bound_monad_fn { Ok(fun) => fun, Err((err, interior_frame)) => return Err((err, interior_frame.pop().0)) };
    // If the result is not bound to a monad expression, we need to wrap the result
    let is_inner_expression                 = bound_monad_fn.reference_type() != ReferenceType::Monad;
    let (bound_monad_fn, interior_frame)    = if is_inner_expression {
        // The inner result of a monadic expression needs to be wrapped so the return value is itself a monad (note we don't bind the bound monad fn itself here)
        // TODO: add a way to generate a bound wrap expression more simply
        let mut bindings                    = interior_frame;
        let bound_monad_fn                  = SafasCell::List(bound_monad_fn, NIL.clone()).into();
        bindings.args                       = Some(SafasCell::List(bound_monad_fn, NIL.clone()).into());
        let (bindings, bound_monad_fn)      = wrap_keyword().bind(bindings);
        match bound_monad_fn {
            Ok(bound_monad_fn)  => (SafasCell::BoundSyntax(bound_monad_fn).into(), bindings),
            Err(_err)           => unimplemented!()
        }
    } else {
        (bound_monad_fn, interior_frame)
    };
    // Compile to a closure (this generates the function passed to FlatMap later on)
    let monad_flat_map                      = compile_statement(bound_monad_fn);
    let monad_flat_map                      = match monad_flat_map { Ok(flat_map) => flat_map, Err(err) => return Err((err, interior_frame.pop().0)) };
    let interior_frame_size                 = interior_frame.num_cells;
    // Pop the interior frame and bring in any imports
    let (bindings, imports)                 = interior_frame.pop();
    // Add any imports to the list of arguments (all the arguments get imported into our closure)
    let mut other_arguments                 = other_arguments.into_iter().map(|(cell_id, _ref_type)| cell_id).collect::<Vec<_>>();
    let mut args_so_far                     = args_so_far;
    let mut bindings                        = bindings;
    for (symbol_value, import_into_cell_id) in imports.into_iter() {
        match &*symbol_value {
            SafasCell::FrameReference(_our_cell_id, 0, _type) => {
                // Cell from this frame
                other_arguments.push(import_into_cell_id);
                args_so_far.push(symbol_value);
            },
            SafasCell::FrameReference(their_cell_id, frame_count, their_type) => {
                // Import from a parent frame
                let our_cell_id = bindings.alloc_cell();
                bindings.import(SafasCell::FrameReference(*their_cell_id, *frame_count, *their_type).into(), our_cell_id);
                other_arguments.push(import_into_cell_id);
                args_so_far.push(SafasCell::FrameReference(our_cell_id, 0, *their_type).into());
            },
            _ => panic!("Don't know how to import this type of symbol")
        }
    }
    // Arguments are loaded in reverse order from the stack, so we need to reverse their order
    other_arguments.reverse();
    // Bind to a closure
    let monad_flat_map                      = monad_flat_map.to_actions().collect::<Vec<_>>();
    let monad_flat_map                      = StackClosure::new(monad_flat_map, other_arguments, interior_frame_size, 1, true);
    // Convert things to the final result
    let args_so_far                         = SafasCell::list_with_cells(args_so_far);
    let monad_flat_map                      = SafasCell::FrameMonad(Box::new(monad_flat_map)).into();
    // Result is a list starting with the monad
    let result                              = SafasCell::list_with_cells(vec![monad, args_so_far, monad_flat_map]).into();
    Ok((result, bindings))
}
fn bind_monad ( args_so_far : Vec < CellRef > , monad : CellRef , remaining_args : CellRef , bindings : SymbolBindings ) -> BindResult < CellRef > { // The remainder of the function will need to be evaluated in a function let mut interior_frame = bindings . push_new_frame ( ) ; // The first parameter of the flat_map function is the value of the monad argument let monad_value_cell = interior_frame . alloc_cell ( ) ; // Next parameters are bound from the closure and are the arguments so far (including the function, if present) let other_arguments = args_so_far . iter ( ) . map ( | arg | ( interior_frame . alloc_cell ( ) , arg . reference_type ( ) ) ) . collect :: < Vec < _ > > ( ) ; // Generate a partially-bound statement using these arguments (remaining_args are still unbound and go on the end) let monad_fn = SafasCell :: List ( SafasCell :: FrameReference ( monad_value_cell , 0 , ReferenceType :: Value ) . into ( ) , remaining_args ) ; let mut monad_fn = Arc :: new ( monad_fn ) ; for ( cell_id , reference_type ) in other_arguments . iter ( ) . rev ( ) { monad_fn = SafasCell :: List ( SafasCell :: FrameReference ( * cell_id , 0 , * reference_type ) . into ( ) , monad_fn ) . into ( ) ; } // Bind this function // (Note: the args_so_far are all frame references here so they should bind to themselves, saving us some issues with rebinding) let bound_monad_fn = bind_statement ( monad_fn , interior_frame ) ; let ( bound_monad_fn , interior_frame ) = match bound_monad_fn { Ok ( fun ) => fun , Err ( ( err , interior_frame ) ) => return Err ( ( err , interior_frame . pop ( ) . 0 ) ) } ; // If the result is not bound to a monad expression, we need to wrap the result let is_inner_expression = bound_monad_fn . reference_type ( ) != ReferenceType :: Monad ; let ( bound_monad_fn , interior_frame ) = if is_inner_expression { // The inner result of a monadic expression needs to be wrapped so the return value is itself a monad (note we don't bind the bound monad fn itself here) // TODO: add a way to generate a bound wrap expression more simply let mut bindings = interior_frame ; let bound_monad_fn = SafasCell :: List ( bound_monad_fn , NIL . clone ( ) ) . into ( ) ; bindings . args = Some ( SafasCell :: List ( bound_monad_fn , NIL . clone ( ) ) . into ( ) ) ; let ( bindings , bound_monad_fn ) = wrap_keyword ( ) . bind ( bindings ) ; match bound_monad_fn { Ok ( bound_monad_fn ) => ( SafasCell :: BoundSyntax ( bound_monad_fn ) . into ( ) , bindings ) , Err ( _err ) => unimplemented ! ( ) } } else { ( bound_monad_fn , interior_frame ) } ; // Compile to a closure (this generates the function passed to FlatMap later on) let monad_flat_map = compile_statement ( bound_monad_fn ) ; let monad_flat_map = match monad_flat_map { Ok ( flat_map ) => flat_map , Err ( err ) => return Err ( ( err , interior_frame . pop ( ) . 0 ) ) } ; let interior_frame_size = interior_frame . num_cells ; // Pop the interior frame and bring in any imports let ( bindings , imports ) = interior_frame . pop ( ) ; // Add any imports to the list of arguments (all the arguments get imported into our closure) let mut other_arguments = other_arguments . into_iter ( ) . map ( | ( cell_id , _ref_type ) | cell_id ) . collect :: < Vec < _ > > ( ) ; let mut args_so_far = args_so_far ; let mut bindings = bindings ; for ( symbol_value , import_into_cell_id ) in imports . into_iter ( ) { match & * symbol_value { SafasCell :: FrameReference ( _our_cell_id , 0 , _type ) => { // Cell from this frame other_arguments . push ( import_into_cell_id ) ; args_so_far . push ( symbol_value ) ; } , SafasCell :: FrameReference ( their_cell_id , frame_count , their_type ) => { // Import from a parent frame let our_cell_id = bindings . alloc_cell ( ) ; bindings . import ( SafasCell :: FrameReference ( * their_cell_id , * frame_count , * their_type ) . into ( ) , our_cell_id ) ; other_arguments . push ( import_into_cell_id ) ; args_so_far . push ( SafasCell :: FrameReference ( our_cell_id , 0 , * their_type ) . into ( ) ) ; } , _ => panic ! ( " " ) } } // Arguments are loaded in reverse order from the stack, so we need to reverse their order other_arguments . reverse ( ) ; // Bind to a closure let monad_flat_map = monad_flat_map . to_actions ( ) . collect :: < Vec < _ > > ( ) ; let monad_flat_map = StackClosure :: new ( monad_flat_map , other_arguments , interior_frame_size , 1 , true ) ; // Convert things to the final result let args_so_far = SafasCell :: list_with_cells ( args_so_far ) ; let monad_flat_map = SafasCell :: FrameMonad ( Box :: new ( monad_flat_map ) ) . into ( ) ; // Result is a list starting with the monad let result = SafasCell :: list_with_cells ( vec ! [ monad args_so_far monad_flat_map ] ) . into ( ) ; Ok ( ( result , bindings ) ) }
Given a partially bound function with a monad parameter, rewrites it as a flat_map binding
Say we are evaluating the call (foo x) where 'x' is a monad.
Given a partially bound function with a monad parameter rewrites it as a flat_map binding Say we are evaluating the call ( foo x ) where ' x ' is a monad .
args_so_far
Vec<CellRef>
monad
CellRef
remaining_args
CellRef
bindings
SymbolBindings
args_so_far
Vec<CellRef>
monad
CellRef
remaining_args
CellRef
bindings
SymbolBindings