4

I have a made a simple function that wraps common F# functions of signature 'a -> 'b -> 'c option to more "C# compliant" function as : 'a -> b -> byref<'c> -> bool. But somehow when I try to wrap such a method in a class I am getting error FS0001 and I can't locate the error.

Code below

open System
open System.Runtime.InteropServices

// Given a function, f: 'a -> 'b -> 'c option returns
// A new function g: 'a -> 'b -> byref<'c> -> bool
let wrapOptionF f a b (g:byref<'c>) =
    match f a b with
    | Some v ->
        do g <- v
        true
    | None -> 
        false

let tryDivide (a:int) (b:int) =
    match Math.DivRem(a,b) with
    | v, 0 -> Some v
    | _ -> None

type TryDivideWrapper() =
    static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
        let f = wrapOptionF tryDivide a b
        f cRef

The offending line is f cRef.

Overly Excessive
  • 2,095
  • 16
  • 31

2 Answers2

5

This post contains a more in-depth explanation, but in short you can replace your final type definition with the following:

type TryDivideWrapper() =
    static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
        wrapOptionF tryDivide a b &cRef

The reason for this is that your wrapOptionF takes a byref parameter. However, byref<int> isn't really a type like int or int ref - it's just an indication to the compiler that your parameter should be passed by reference (like out in C#). Once inside your function, however, what you have is a regular int.


Edit: Note that Intellisense will show cRef as having type byRef<int>. However, if you bind another variable to cRef, you'll see that the type you get is a regular int. You can put this line inside TryDivide and then hover your mouse over a to see it:

let a = cRef

Using the & operator tells the compiler that you're passing cRef into f by reference - which is exactly what f needs - making the type system happy. I've tested this using a C# project, and TryDivideWrapper.TryDivide(a, b, out c) works as expected. Add in @MarkSeemann's tryDivide and you should be good to go.

Community
  • 1
  • 1
Roujo
  • 503
  • 4
  • 12
  • @OverlyExcessive You've triggered a monster in me http://hyperboleandahalf.blogspot.ie/2010/04/alot-is-better-than-you-at-everything.html :P – Ruben Bartelink Feb 21 '16 at 10:50
1

I'm not exactly sure I understand the reason, but this seems to work:

type TryDivideWrapper() =
    static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
        wrapOptionF tryDivide a b &cRef

BTW, the OP tryDivide implementation throws an exception on tryDivide 1 0. Here's an alternative implementation that works:

let tryDivide (a:int) (b:int) =
    if b = 0
    then None
    else Some (a / b)

FSI:

> tryDivide 1 0;;
val it : int option = None
> tryDivide 10 5;;
val it : int option = Some 2
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736