In today’s post I am going to show you how to call Clojure functions from a C# project.
The requirements for this post is to have ClojureCLR installed and to have access to a C# compiler. If you haven’t installed Clojure-clr 1.3 you can do so by following the steps in my previous post Getting Started with Clojure-clr. If you do not have a C# compiler already you can grab Visual C# 2010 Express or MonoDevelop. I used ClojureCLR 1.3 and ClojureCLR 1.4-master-snapshot for this post. I will explain the ClojureCLR 1.4 snapshot in moment. For the C# portion of the post I used Visual Studio 2010 Ultimate. I haven’t tried using MonoDevelop but I’m sure it would work fine for the C# portion of this post.
The Clojure Code
I have written a couple of functions in Clojure that I would like to re-use in a C# project. The functions are ba which will calculate a batting average and standings which will tell me where a particular team finished in Major League Baseball’s National League West division.
There isn’t much to either one of the Clojure functions but there are a few things I’d like to point out in the ns statement, mainly the :gen-class call and the :methods options passed to it. The :gen-class call is what forces the generation of the executable, without it only DLLs would be created when the code is compiled. The :methods options us a way to indicate which functions should be exposed as methods in the generated .NET class. :methods expects a vector of method signatures.
The method signature is prefaced with meta data to indicate that I want the methods to be static in the generated class. The exposed method's signature is described by a vector that follows the pattern below:
[method name [parameters] return value]
As you can see the ba method takes two int parameters and returns a double and the standings method takes a string and returns a string.
By default methods listed in :methods will be mapped to a Clojure function with the same name prefixed by a –. You can change the function’s prefix by passing the :prefix option to :gen-class with your desired prefix. As an example if I wanted to prefix the functions with csharp- I would pass :prefix “csharp-“. Then ba would map to a function of csharp-ba and the standings function would be csharp-standings. In this example, I’m using the – functions to echo the parameters the function was called with and then I call the real functions.
You may have noticed that the –ba and –standings functions have a parameter named dummy. The dummy parameter is there as a work-around. When I first started working on calling Clojure from C# I noticed that a function that did math was returning the incorrect value when called from C#. If I ran the code with the same parameters from the REPL or after I compiled the code and ran it from the command line it worked fine. I was only having the problem when I called the functions from C#. To figure out what was going on I opened the clj file in Visual Studio and set a debug point in the –ba function. Here’s an example of stopping at a breakpoint in Visual Studio. If you have VsClojure installed you can debug clojure code in the same way you can C#. You can see how to get clojure support in Visual Studio in my blog post Getting VsClojure Up and Running in Visual Studio 2010
After inspecting all of the parameters I noticed that the last one always had a huge number in it as if it was a memory address when integers were the parameters. When I added an extra parameter to the method declaration and the – function in the in the clj file the calculation returned correctly.
The work-around is only for ClojureCLR 1.3 this has already been fixed in the 1.4 snapshot. Thank you to David Miller – he had it fixed in less than 24 hours of my reporting it!
In addition to the –ba and –standings functions I have a –main function which will be called when I run the executable. I compile it
Then run it:
Gives me the results below
In my github project I have a one14.clj file that works perfectly with the 1.4-snapshot that contains the fix for the issue. It is basically the same as one.clj except it does not need the dummy parameter.
That’s it for the Clojure side of things. Now its time to talk about the C# code
The C# Code
The C# code itself doesn’t have anything remarkable. It is vanilla C# code. Which is exactly the way I like it, no special hoops to jump through if I want to call Clojure functions. In order to call the Clojure code I needed to add a few references. First I added the assemblies generated when I compiled one.clj: one.clj.dll and one.exe files. Next I added the assembly Clojure.dll which can be found in the directory where you installed ClojureCLR. After getting the references in place I started writing code. When I ran the code for the first time I received a System.TypeInitializationException with the message “The type initializer for 'one' threw an exception." which wasn’t all that helpful. I dug into the inner exception’s inner exception and found this message: "Could not locate clojure.core.clj.dll or clojure/core.clj on load path." Now that is a message I can do something with. I added the clojure.core.clj.dll reference and re-ran the app only to find out I was missing a few other references. When I got my code to run I had added the references that are listed on the left. Once I had the one.clj.dll, one.exe references with the clojure related ones on the left I was ready to go! In the future adding the Clojure references will be much easier.
Now I can call the ba and standings methods from my C# app just like I call any other .NET static methods. Here’s the code for calling the Clojure compiled with the 1.3 version compiler.
Notice that the extra parameter is the first parameter in the calls from C# even though in the clojure code it is the last parameter. Here’s what the output looks like for the 1.3 code:
And the output of the 1.4 snapshot version:
Obviously they both produce the same outputs but 1.3 needs the placeholder parameter whereas the 1.4-snapshot version does not.
As you can see there isn’t much work to be done if you want to expose ClojureCLR functions to the outside .NET community. You just need to tell gen-class which methods to expose and make sure you follow the naming convention for the functions on the clojure side. On the C# side once you have all the references in place, which will become easier to do in the future, writing the code to call the clojure generated methods is no different than calling any other method in a .NET library.
If you have any questions or blog post suggestions please feel free to leave a comment.
My Source (this blog’s code is the 3-calling-clojure-from-c-sharp): https://github.com/rippinrobr/clojure-clr-intro/zipball/master