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.

Python

import clr
clr.AddReference('FSharp.Core')



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:

Python

>>> 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.

Python

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



Tuples



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.

F#

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


Python

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



This program will print:


(46,True)
46
True


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

For example to given the following definition:

F#

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


We can used from IronPython like this:
Python

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.

Operators



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) =
Complex(c1.real+c2.real,c1.img+c2.img)
override this.ToString() = sprintf "%f + %fi" real img
end


Can be used in IronPython:

Python


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.

F#

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.

Python

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


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.

Python

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


Functions



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):
print(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 Seq.map[int,int]( FuncConvert.ToFastFunc[int,int](lambda x: x*x) ,\
Seq.take[int](15,fibo())):
print n




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

No comments: