Wednesday, January 21, 2009

Some notes on using F# code from IronPython

This post presents a couple of things I learned about the interaction between IronPython and F#.

Most information about this topic can be found by looking at the code generated by the F# compiler, using tools like ILDASM or Reflector. Also the F# from C# section of the F# 1.1.12 documentation is very helpful.

FSharp.Code library

In order to call some F# basic functionality we need to add a reference to FSharp.Core.


import clr

Type parameters

IronPython provides a way to specify type parameters to methods and classes by using square brackets followed by the names of the types to be used. This mechanism can be used to create F# constructs that require them.

The following example shows how to create an F# list:


>>> import clr
>>> clr.AddReference('FSharp.Core')
>>> from Microsoft.FSharp.Collections import List
>>> l1 = List[str].Cons('World',List[str].Empty)
>>> l2 = List[str].Cons('Hello',l1)
>>> " ".join(l2)
'Hello World'

Type parameters, for methods that require them, also can be specified using the same syntax. For example the following code shows the use of Seq.length with an Python sequence.


print Seq.length[int]([45,234,52,345,2,346,657])


Tuples in F# are instances of the Microsoft.FSharp.Core.Tuple class (with several definitions with depending on the number of arguments). A tuple instance contains properties Item# where # is the position of the element.

The following example shows how to use a tuple created in F# from IronPython.


module MyFunctions = begin
let numberEvenTest x = x,x % 2 = 0


print MyFunctions.numberEvenTest(46)
print MyFunctions.numberEvenTest(46).Item1
print MyFunctions.numberEvenTest(46).Item2

This program will print:


To create a tuple inside IronPython the Tuple[type1,type2,...](value1,value2,...) constructor must be used.

For example to given the following definition:


module MyFunctions = begin
let movePoints points xdistance ydistance = (fun (x,y) -> (x+xdistance,y+ydistance)) points

We can used from IronPython like this:

from Microsoft.FSharp.Core import Tuple
points = [ Tuple[int,int](1,2), Tuple[int,int](5,3) ]

for p in MyFunctions.movePoints(points,20,40):
print p

It is tempting to use the Python syntax for assigning a list of variables to the values of the tuples. For example:

x,y,z = 2,3,5

However this is not possible since F# tuples are not compatible with IronPython sequences.


F# operators are created using the same conventions as the C# operators. Thus they can be used directly in IronPython.

For example the following F# definition:

type Complex(real:double,img:double) = class
member this.real with get() = real
member this.img with get() = img
static member (+) (c1:Complex,c2:Complex) =
override this.ToString() = sprintf "%f + %fi" real img

Can be used in IronPython:


c1 = Complex(20,234)
c2 = Complex(34.35,32.2)
c3 = (c1+c2)

Discriminated unions

Discriminated unions in F# provide a compact way to define data structures. The following example shows the definition of simple math expressions that include addition, subtraction and numeric literals.


type MathExpr =
| Addition of MathExpr * MathExpr
| Subtraction of MathExpr * MathExpr
| Literal of double

The following code snippet shows how to create instance of these math expression elements from IronPython.


expr = MathExpr.Addition(MathExpr.Literal(10),\
MathExpr.Literal(40), \

A instance of the discriminated union has methods IsXXXX to verify the kind. Also properties a generated with the name of the constructor followed by a number (for example Addition1) to access values given to the constructor.

The following example shows how to evaluate a math expression.


def evalExpr(expr):
if expr.IsAddition():
return evalExpr(expr.Addition1) + evalExpr(expr.Addition2)
elif expr.IsSubtraction():
return evalExpr(expr.Subtraction1) - evalExpr(expr.Subtraction2)
return expr.Literal1


Functions passed as parameters need to be converted to a special F# element that represent them. In order to do this the FuncConvert.ToFastFunc function is used.

For example the following code shows how to call Seq.iter to print all the elements of a Python list.

from Microsoft.FSharp.Core import FuncConvert
aList = [5,3,42,6]

def foo(x):

Seq.iter[int](FuncConvert.ToFastFunc[int]( foo),aList)

Since IronPython and F# use IEnumerable<T> types to represent collections and sequences we can use F# functions to manipulate IronPython collections.

For example given the following definition of a Python generator for Fibonacci numbers:

def fibo():
last1 = 0
last2 = 1
while True:
yield last1
next = last1+last2
last2 = last1
last1 = next

We can call F# functions to process values generated by fibo. For example the following IronPython code prints the first 15 squared Fibonacci numbers.

for n in[int,int]( FuncConvert.ToFastFunc[int,int](lambda x: x*x) ,\
print n

Code for this post was created using IronPython 2.0 and F# .

No comments: