F # optional arguments and overloading alternatives

In my F # application, I often have to search across strings of a string within a string, so I created a function with the appropriate mapping:

let indexOf (str:string) (value:string) startIndex =
    match str.IndexOf(value, startIndex, StringComparison.OrdinalIgnoreCase) with
    | index when index >= 0 -> Some index
    | _ -> None

      

I don't like the fact that when I want to search from the beginning, I need to pass the redundant 0 as the starting index.

I am relatively new to both F # and functional programming, so I would like to know what is the preferred (cleanest) solution from a functional point of view?

  • Create two versions:

    let indexOfFrom (str:string) (value:string) startIndex = (...)
    let indexOf str value = indexOfFrom str value 0
    
          

  • Use the parameter type:

    let foundIndex = indexOf "bar foobar" "bar" (Some 4)
    
          

  • Create a dedicated discriminatory union:

    type Position =
        | Beginning
        | StartIndex of index : int
    let foundIndex = indexOf "bar foobar" "bar" (Index 4)
    
          

  • Place the "indexOf" function inside the type and use the "classic" overload.

  • Place the indexOf function inside a type and use the optional F # arguments.

+3


source to share


2 answers


If you are defining functionality as F # functions, then I think using two separate functions (with reasonably descriptive names) is probably the best option you have. So I would go with your first option (I definitely prefer this option for defining discriminatory aggregation for this single purpose only):

let indexOfFrom (str:string) (value:string) startIndex = (...)
let indexOf str value = indexOfFrom str value 0

      

An alternative is to define the functionality as members of the type - then you can use both the overloading and the optional F # arguments, but you will need to access them using the fully qualified name String.IndexOf

. You could write something like:

type String =
  static member IndexOf(str:string, value:string, startIndex) = (...)
  static member IndexOf(str, value) = String.IndexOf(str, value, 0)

      



Or, using optional parameters:

type String =
  static member IndexOf(str:string, value:string, ?startIndex) = (...)

      

Which is the best option?

  • If you are developing a functional API (like a domain specific language), then your option with two separate functions is probably the best choice.

  • If you want to create a good F # API, I think your option (multiple functions) or optional parameters are quite reasonable. Functions are fairly widely used in Deedle and F # Charting relies on optional arguments.

  • The advantage of using overloading is that the library will also be used well from C #. So, if you are thinking about calling a library from C #, this is almost the only option.

+3


source


I think option 1 (with curry function) would be the simplest. Curried functions are fairly common in functional programming.

In options 2 or 3, you still have to pass an extra parameter to the search function from the beginning



Parameters 4 or 5 require additional overhead to create the type. This is sort of overkill for this simple task.

+6


source







All Articles