Can i write a fortran library and call it from F#

Can i write a fortran library and call it from F# ?

That is certainly possible with P/Invoke as for almost any other natively compiled language, although FORTRAN calling conventions make it slightly more painful than, for example, an interface to C.

Consider the following silly little FORTRAN 90 function:

FUNCTION foobar(x, y)
    !GCC$ ATTRIBUTES CDECL :: foobar
    REAL, INTENT (IN) :: x, y
    REAL :: foobar
    foobar = x * y + x / y

Let’s say you translate this into a Unix shared library with gfortran -O2 -Wall -fPIC -fdefault-real-8 -fno-underscoring foobar.f95 -shared -o

You can then write an F# module importing the function through P/Invoke:

module FromFortran =
    open System.Runtime.InteropServices

    [<DllImport("foobar", CallingConvention = CallingConvention.Cdecl)>]
    extern float foobar([<In>] float& x, [<In>] float& y)

Note that even though the input arguments were annotated with INTENT (IN) in FORTRAN, the compiler will pass them by reference, so we have to replicate that kind of calling convention on the .NET side. Also note that we had to pass special flags to the compiler to align the default type size between FORTRAN’s REAL and F#'s float, and to avoid decorating the exported names with additional underscores as compared to C language conventions (otherwise the DllImport attribute would likely need another argument like EntryPoint = "foobar_")

A call to the function would look something like this:

let mutable x = 3.0
let mutable y = 2.0

FromFortran.foobar(&x, &y)
|> printfn "foobar(%g, %g) = %g" x y

Since F# favours immutability and FORTRAN insists on passing everything by (mutable) reference, there is some friction here that you would probably want to resolve with another abstraction layer over your FORTRAN library on the F# side for a real world project.

Depending on the concrete scenario it might also be an option to compile FORTRAN code to .NET IL instead of native code. SilverFrost FORTRAN comes to mind here, yet the calling convention issues are not much better in this case.

Let’s say you translate the above FORTRAN code into a .NET assembly with ftn95 foobar.f95 /clr /clr_ver 4 /optimise /dreal /link foobar.dll, then the F# code to call into it would look like this:

#nowarn "9"
#r "foobar.dll"

let x = [|3.0|]
let y = [|2.0|]
let foobar =
    use x = fixed x
    use y = fixed y
    foobar.FOOBAR(x, y)

printfn "foobar(%g, %g) = %g" x[0] y[0] foobar

This is my current code.

module lib
    subroutine print_int(i)
        implicit none
        integer, intent (in) :: i
        print *, i
    end subroutine print_int

    integer function square(i) bind(C)
        implicit none
    end function square
end module lib

lfortran -c -Wall -fPIC lib.f90

F# code:

module test =
    open System.Runtime.InteropServices
    [<DllImport("lib.o", CallingConvention=CallingConvention.Cdecl)>]
    extern int square(int)
    let _ =
        printfn "Hello from F#"
        printfn "%d" (square 5)

Error :

dotnet run
Hello from F#
Unhandled exception. System.DllNotFoundException: Unable to load shared library 'lib.o' or one of its dependencies. In order to help diagnose loading problems, consider using a tool like strace. If you're using glibc, consider setting the LD_DEBUG environment variable: 

Even if i have in the current working directory of the dotnet project a softlink to lib.o

PS : on FreeBSD i cannot build “-shared”.

So i probably must say to dotnet look into the current directory for softlinks to lib.o

Hmm, the BIND (C) attribute takes care of the underscores in the symbol naming convention, but not with the fact that FORTRAN wants to pass things by reference :angry: However, I figured out that the VALUE attribute helps here, and that modern compilers provide an intrinsic ISO_C_BINDING module with constants for the right type kinds:

FUNCTION foobar(x, y) BIND (C)
    !GCC$ ATTRIBUTES CDECL :: foobar
    REAL (C_DOUBLE), VALUE :: x, y
    REAL (C_DOUBLE) :: foobar
    foobar = x * y + x / y

Combined with this F# code:

module F =
    open System.Runtime.InteropServices

    [<DllImport("foobar", CallingConvention = CallingConvention.Cdecl)>]
    extern float foobar(float x, float y)

let x = 3.0
let y = 2.0

F.foobar(x, y)
|> printfn "foobar(%g, %g) => %g" x y

I can successfully run the code using:

gfortran -O2 -Wall -fPIC foobar.f95 -shared -o
LD_LIBRARY_PATH="$PWD" dotnet fsi foobar.fsx

Note that I have to compile the code into a shared object. Obviously, the .NET runtime cannot just load some static object with incomplete linkage into a process.

If the creation of shared libraries really isn’t an option, it may be possible to use the Native AOT machinery to compile your entire .NET code into machine code and link both the full .NET runtime and any externally provided objects you like into one executable.