whole_func_string: function computeParamTypes(rel, bindingTypes, implicits, computeTypeWrapper){
  	_.assertLength(arguments, 4)
  	_.assertArray(implicits)
  	rel.params.forEach(function(p){
  		if(p.type === 'macro' || p.type === 'partial-application'){
  			return
  		}
  		computeTypeWrapper(p, bindingTypes, implicits)
  	})
  }
func_code_string: function computeParamTypes(rel, bindingTypes, implicits, computeTypeWrapper){
  	_.assertLength(arguments, 4)
  	_.assertArray(implicits)
  	rel.params.forEach(function(p){
  		if(p.type === 'macro' || p.type === 'partial-application'){
  			return
  		}
  		computeTypeWrapper(p, bindingTypes, implicits)
  	})
  }
func_documentation_string: /*
  function computePathResultType(path, baseType, context, schema, viewMap){
  _.assertLength(arguments, 5);
  _.assertObject(baseType);
  var t = baseType;
  _.each(path, function(p){
  if(p.type === 'property'){
  _.errout('TODO?')
  t = t.properties[p.property];
  if(t === undefined){
  _.errout('unknown property: ' + p.property);
  }
  _.assertObject(t);
  }else if(p.type === 'macro'){
  _.errout('TODO?')
  var input = t;
  t = computeType(p.expr, context, schema, viewMap);
  if(input.type.type === 'set'){
  _.assert(t.type !== 'set' && t.type !== 'list')
  t = {type: 'set', members: t};
  }else if(input.type.type === 'list'){
  t = {type: 'list', members: t};
  }
  _.assertObject(t);
  }else{
  _.errout('TODO support path part type: ' + p.type);
  }
  });
  return t;
  }