Search code examples
hashf#type-conversionconstraintsrabin-karp

How to prevent this Cyclic polynomial hash function from using a type constraint?


I am trying to implement the Cyclic polynomial hash function in f#. It uses the bit-wise operators ^^^ and <<<. Here is an example of a function that hashes an array:

let createBuzhash (pattern : array<'a>)  =
let n = pattern.Length
let rec loop index pow acc =
    if index < n then
        loop (index+1) (pow-1) (acc ^^^ ((int pattern.[index]) <<< pow))
    else
        acc
loop 0 (n-1) 0

My problem is that the type of 'a will be constrained to an int, while i want this function to work with any of the types that work with bit-wise operators, for example a char. I tried using inline, but that creates some problems farther down in my library. Is there a way to fix this without using inline?

Edit for clarity: The function will be part of a library, and another hash function is provided for types that don't support the bit-wise operators. I want this function to work with arrays of numeric types and/or chars.

Edit 2 (problem solved) : The problem with inline was the way how i loaded the function from my library. instead of

let hashedPattern = library.createBuzhash targetPattern

I used this binding:

let myFunction = library.createBuzhash
let hashedPattern = myFunction targetPattern

that constraints the input type for myFunction to int, although the createBuzhash function is an inline function in the library. Changing the way I call the function fixed the type constraint problem, and inline works perfectly fine, as the answer below suggests.


Solution

  • In the implementation, you are converting the value in the array to an Integer using the int function as follows: int pattern.[index]

    This creates a constraint on the type of array elements requiring them to be "something that can be converted to int". If you mark the function as inline, it will actually work for types like char and you'll be able to write:

    createBuzhash [|'a'; 'b'|]
    

    But there are still many other types that cannot be converted to integer using the int function.

    To make this work for any type, you have to decide how you want to handle types that are not numeric. Do you want to:

    • Provide your own hashing function for all values?
    • Use the built-in .NET GetHashCode operation?
    • Only make your function work on numeric types and arrays of numeric types?

    One option would be to add a parameter that specifies how to do the conversion:

    let inline createBuzhash conv (pattern : array<'a>)  =
      let n = pattern.Length
      let rec loop index pow acc =
          if index < pattern.Length then
              loop (index+1) (pow-1) (acc ^^^ ((conv pattern.[index]) <<< pow))
          else
              acc
      loop 0 (n-1) 0
    

    When calling createBuzhash, you now need to give it a function for hashing the elements. This works on primitive types using the int function:

    createBuzhash int [| 0 .. 10 |]
    createBuzhash int [|'a'; 'b'|]
    

    But you can also use built-in F# hashing mechanism:

    createBuzhash hash [| (1,"foo"); (2,"bar") |]
    

    And you can even handle nested arrays by passing the function to itself:

    createBuzhash (createBuzhash int) [| [| 1 |]; [| 2 |] |]