This project is read-only.

Passing SEXP as named arguments

Nov 1, 2013 at 12:48 AM
Hi,

I might be wrong about this (let me know if so), but my understanding is that there is currently no way to directly pass SEXP objects to R as named arguments. For example, in the C# style method invocation on the examples page
var x = engine.CreateNumericVector(Enumerable.Range(0, 13).Select(i => i * Math.PI / 12).ToArray());
var cos = engine.GetSymbol("cos").AsFunction();
var y = cos.Invoke(new[] { x }).AsNumeric();
The array passed to invoke has no names, which seems somewhat limiting. To work around this, I gather the F# type provider is passing all arguments as "evaluate" string statements, after loading the variables in the R namespace.

It seems most of the code to do this is already in R.NET. In particular, the Invoke function creates a paired list and populates its two of the three elements (CAR, CDR, TAG), but ignores the third value "TAG" which gives the name of the argument (which could be set by Rf_install C function).

I think adding this would allow the base package to use named arguments more seemlessly, but wanted to ask if there was a reason this wasn't done yet (or was not advisable).

-N
Nov 1, 2013 at 8:50 PM
I just wrote this as an extension method in the REngineExtension.cs class (copied below to avoid making a new pull request). Being able to pass arguments around in a dicitonary and avoid creating strings like this I think really simplifies some coding, so I hope this gets added to the main branch.
/// <summary>
      /// Attemps to call a named method with the named arguments given in the 
      /// </summary>
      /// <param name="engine">The Engine</param>
      /// <param name="methodName">Name of the method to call.</param>
      /// <param name="args">Name/Value pairs to pass to the argument.</param>
      /// <param name="result">The result from the method call.</param>
      /// <returns>True if succeeded, otherwise false</returns>
      public static bool EvaluateMethodWithNamedArguments(this REngine engine,string methodName,Dictionary<string, SymbolicExpression> args, out SymbolicExpression result)
      {
          if (engine == null || methodName == null || args == null) {
              throw new ArgumentNullException();
          }
          result = null;
          //Get the symbol attached to the method name
          var pMethodname = engine.GetSymbol(methodName);
          if (pMethodname == null) {
              return false;
          }
          IntPtr argument = engine.NilValue.DangerousGetHandle();
          //loop through the arguments creating a linked list (LISTSXP) with tag/value for argument names
          var install=engine.GetFunction<Rf_install>();
          var setTag=engine.GetFunction<R_SetExternalPtrTag>();
          var rfCons = engine.GetFunction<Rf_cons>();
          foreach (var arg in args) {
              var sexp = arg.Value;
              var name = arg.Key;
              if(sexp==null){
                  throw new NullReferenceException("Tried to invoke a method with an empty named argument passed.");
              }
              argument = rfCons(sexp.DangerousGetHandle(), argument);
              var tagValue = install(name);
              setTag(argument, tagValue);
          }
          //Create a LANGSXP object to pass in to the evaluater, installing the handle to the  method symbol and argument list
          IntPtr call = engine.GetFunction<Rf_lcons>()(pMethodname.DangerousGetHandle(), argument);
          bool errorOccurred;
          IntPtr pointer = engine.GetFunction<R_tryEval>()(call, engine.GlobalEnvironment.DangerousGetHandle(), out errorOccurred);
          result = errorOccurred ? null : new SymbolicExpression(engine, pointer);
          return !errorOccurred;          
      }
Nov 1, 2013 at 10:02 PM
I've added something similar a couple of weeks ago on a branch. I'll compare feature and code design with yours to see if I can improve, but possibly not before a week or two from now.

Revision: 222
Changeset: 1c6c266a7bedd39106b875cb8689f53b1af65699 [1c6c266a7bed]
Parents: 217
Date: Saturday, 19 October 2013 5:33:30 PM
Branch: jperraud
Labels:
Improve R function SEXP invocation. https://rdotnet.codeplex.com/workitem/61
Nov 1, 2013 at 10:27 PM
Wow, that could have saved me some time :). I was also writing extension methods to create vectors for single values, and you clearly already have that to. Your branch is clearly a lot further along then the main branch!

Looks like our methods are pretty similar. The only two big differences seem to be that you call Rf_eval, while I call Rf_TryEval, and I get the method to run from a string, while you invoke a method symbol already, lots of overlap though.