Missing "SetVariable"

Dec 1, 2011 at 4:05 AM

Originally from: http://ironscheme.codeplex.com/workitem/16986

iseric asks: 

I cannot find anything like the "SetVariable" methods offered by IronRuby and IronPython engines. How might I accomplish this with the IronSchemeLanguageProvider or other?

*I would like to be able to incorporate IronScheme into my project "Atha" (atha.codeplex.com), which currently has support for writing test scripts in IronPython, IronRuby, PowerShell, and Razor syntax.

Dec 1, 2011 at 4:07 AM


There is no such concept, as all 'variables' get renamed in IronScheme (google alpha-renaming), so even if you used 'SetSymbolValue' there will be no way for you to guess the renamed value.

The good news is that one can ask the system to do that, by executing some code. It will look something like:

"(set! var {0})".Eval(yourvalue);

There are a few problems with this however:

1. Bad Scheme style (but ok for interop usage)
2. This will only work in the REPL (aka interaction-environment)

The good news is that there are several good ways to achieve this, but you will have to write some code in Scheme to support such actions.

The easiest and probably best way to mutate a variable is to use parameters. They are pretty much the same as properties in C# (except that they are ThreadStatic). You have a getter and setter action. Example:


(define foo (make-parameter #f))
(foo 123)
Now these can be easily be done via the extension methods. Example:
"(foo {0})".Eval(123);
To be able to do this, you need to know where the variable is exported from. This can only happen in libraries. 

There are 2 ways to do this:

1. Import the library into the interaction-environment ie
"(import (mylib))".Eval()
2. Call EvalWithEnvironment ie
"(foo {0})".Eval("(mylib)", 123)


Another 'fact' is that this is clearly only for setting toplevel variables (only they can be exported anyways).

If you can give me a better indication of your usage, I can recommend or dish you up a better solution if needed.



Dec 1, 2011 at 10:56 PM

Thank you for the quick reply and FYI: my knowledge of Scheme is limited to a cursory read of "Get Started".

My use case for IronScheme as it relates to the Atha project: Enable acceptance testing scripts to be written in the IronScheme language, utilizing the global variable "atha" to aggregate testing results.

As you can see from the examples below, the idea is that the tester can utilize all of the desired language's capabilities with a common object instance for aggregating testing results. This enables the Atha test runner to embed any language with a basic assumption that test results will be provided through the global variable "atha". *I fully recognize that a more flexible solution would be to have a standard result from running a test script that the test runner could read (test result XML) and then the test script author doesn't need to know anything about "atha", but I have made a design decision that the Atha API exposed to test scripts will be as common as possible across all languages.  *Open to any thoughts / reactions you may have to this decision.

Simple IronPython Example:


atha.equal(1, 1, '1==1')
atha.equal(a, a, 'a==a')
atha.equal(1, 0, '1!=0')
atha.equal(a, b, 'a!=b')


Simple IronRuby Example:

atha.equal(1, 1, '1==1')
atha.equal(a, a, 'a==a')
atha.equal(1, 0, '1!=0')
atha.equal(a, b, 'a!=b')

Simple PowerShell Example:


$atha.equal(1, 1, '1==1')
$atha.equal($a, $a, 'a==a')
$atha.equal(1, 0, '1!=0')
$atha.equal($a, $b, 'a!=b')

Simple Razor Example:

    var a="a";
    var b="b";
    Model.Equal(1, 1, "1==1");
    Model.Equal(a, a, "a==a");
    Model.Equal(1, 0, "1!=0");
    Model.Equal(a, b, "a!=b");
    throw new Exception("whoa");

Dec 2, 2011 at 4:46 AM
Edited Dec 3, 2011 at 5:22 AM

What I can see, you have 2 options (so far).

1. Create a singleton in Atha. This is easy to reference from IronScheme.

2. Create an instance of Atha in say a library and export that (or a factory to make instances, if you need more than 1)

Example for #2:


(library (atha)
  (export atha)
  (import (ironscheme)
             (ironscheme clr))

  (define atha (clr-new Atha)))




(import (atha))

(clr-call Atha Equal atha 1 1 "1==1")


Of course this looks silly, and will require some abstraction eg:


(define-syntax atha.equal
  (syntax-rules ()
    [(_ a b str)
      (clr-call Atha Equal atha a b str)]))

(atha.equal 1 1 "1==1")


But this does not really utilize the meta programming facilities of Scheme! Let's make it nicer.


(define-syntax atha.assert
  (lambda (x)
    (syntax-case x ()
      [(_ expr)
        (with-syntax ((str (format "~a" (syntax->datum #'expr))))
          #'(clr-call Atha Assert atha expr str))])))

(atha.assert (= 1 1))

; transforms into
; (clr-call Atha Assert atha (= 1 1) "(= 1 1)")
; look ma, no hands! Scheme's assert does exactly the same type of thing


As you can see, with macros you can make your own 'testing language/DSL' while in the end the same code is being run. This is what Scheme is about :)

I am not if this really answers your question, but it sure will give you some ideas about the capabilities.

Feel free to ask more questions.







Dec 2, 2011 at 5:44 PM

Very interesting. Thank you! I'll give this a try.

Quick Question: What would be the assembly deployment requirements for this to work? (i.e. "atha.dll" in local directory to execution is picked up by "library(atha)" ?)

(library (atha)
  (export atha)
  (import (ironscheme)
             (ironscheme clr))

  (define atha (clr-new Atha)))

Dec 3, 2011 at 5:20 AM

Not quite, I forgot to mention that.

(library (atha)
  (export atha)
  (import (ironscheme)
             (ironscheme clr))

  (clr-reference "atha.dll") ; 
  (clr-using SOmeNamespace) ; if needed

  (define atha (clr-new Atha)))