whole_func_string: function makeSyncOpGenericGetChangesBetween(handle, ws, rel, recurse, paramFuncs){
  
  	//paramFuncs.forEach(function(pf){
  	for(var i=0;i<paramFuncs.length;++i){
  		var pf = paramFuncs[i]
  		if(pf.isMacro){
  			//_.errout('cannot apply this method if a param is a macro: ' + JSON.stringify(rel))
  			return makeGenericGetChangesBetween(handle, ws, rel)
  		}
  	}
  	//})
  	//console.log(new Error().stack)
  	function syncGenericGetChangesBetween(bindings, startEditId, endEditId, cb){
  		_.assertLength(arguments, 4)
  		
  		if(startEditId === endEditId) _.errout('wasting time')
  		
  		var snaps = []
  		
  		var editIds = []
  		var has = {}
  		
  		//console.log('getting ' + JSON.stringify(rel))
  		
  		var cdl = _.latch(paramFuncs.length, function(){
  			if(editIds.length === 0){
  				//console.log('no changes ' + startEditId + ' ' + endEditId)
  				cb([])
  				return
  			}
  			
  			handle.getStateAt(bindings, startEditId, function(startState){
  				handle.getStateAt(bindings, endEditId, function(state){
  					if(startState === state){
  						cb([])
  					}else{
  						var es = ws.diffFinder(startState, state)
  						var changes = []
  						es.forEach(function(e){
  							e.editId = endEditId
  							changes.push(e)
  						})
  						cb(changes)
  					}
  				})
  			})
  		})
  		//console.log('here2')
  		for(var i=0;i<paramFuncs.length;++i){
  			paramFuncs[i].getChangesBetween(bindings, startEditId, endEditId, function(changes){
  				changes.forEach(function(c){
  					var editId = c.editId
  					if(has[editId]) return
  					has[editId] = true
  					editIds.push(editId)
  				})
  						
  				cdl()
  			})
  		}
  	}
  	return syncGenericGetChangesBetween
  }
func_code_string: function makeSyncOpGenericGetChangesBetween(handle, ws, rel, recurse, paramFuncs){
  
  	//paramFuncs.forEach(function(pf){
  	for(var i=0;i<paramFuncs.length;++i){
  		var pf = paramFuncs[i]
  		if(pf.isMacro){
  			//_.errout('cannot apply this method if a param is a macro: ' + JSON.stringify(rel))
  			return makeGenericGetChangesBetween(handle, ws, rel)
  		}
  	}
  	//})
  	//console.log(new Error().stack)
  	function syncGenericGetChangesBetween(bindings, startEditId, endEditId, cb){
  		_.assertLength(arguments, 4)
  		
  		if(startEditId === endEditId) _.errout('wasting time')
  		
  		var snaps = []
  		
  		var editIds = []
  		var has = {}
  		
  		//console.log('getting ' + JSON.stringify(rel))
  		
  		var cdl = _.latch(paramFuncs.length, function(){
  			if(editIds.length === 0){
  				//console.log('no changes ' + startEditId + ' ' + endEditId)
  				cb([])
  				return
  			}
  			
  			handle.getStateAt(bindings, startEditId, function(startState){
  				handle.getStateAt(bindings, endEditId, function(state){
  					if(startState === state){
  						cb([])
  					}else{
  						var es = ws.diffFinder(startState, state)
  						var changes = []
  						es.forEach(function(e){
  							e.editId = endEditId
  							changes.push(e)
  						})
  						cb(changes)
  					}
  				})
  			})
  		})
  		//console.log('here2')
  		for(var i=0;i<paramFuncs.length;++i){
  			paramFuncs[i].getChangesBetween(bindings, startEditId, endEditId, function(changes){
  				changes.forEach(function(c){
  					var editId = c.editId
  					if(has[editId]) return
  					has[editId] = true
  					editIds.push(editId)
  				})
  						
  				cdl()
  			})
  		}
  	}
  	return syncGenericGetChangesBetween
  }
func_documentation_string: function makeReallyGenericSyncOpGenericGetChangesBetween( find the union of the changed versions for the input parameters, use that to reduce the number of comparisons necessary