M.Y. Babt
M.Y. Babt

Reputation: 2911

F#: Error FS0030: Value restriction

I am new to programming and F# is my first language.

Here are the relevant parts of my code:

let rec splitArrayIntoGroups (inputArray: string[]) (groupSize: int) (hashSetOfGroups: HashSet<string[]>)=
    let startIndex = 0
    let endIndex = groupSize - 1

    let group = inputArray.[startIndex .. endIndex]
    let nextInputArray = inputArray.[groupSize .. inputArray.Length - 1]

    hashSetOfGroups.Add(group) |> ignore
    splitArrayIntoGroups nextInputArray groupSize hashSetOfGroups

let hashSetOfGroups = new HashSet<string[]>()

splitArrayIntoGroups urlArray 10 hashSetOfGroups

urlArray is an array of almost 3200 URLs.

When I try to run the code in F# interactive, I receive the following error message:

Program.fs(119,1): error FS0030: Value restriction. The value 'it' has been inferred to have generic type val it : '_a Either define 'it' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.

What went wrong, and what changes should I make?

Upvotes: 1

Views: 1104

Answers (1)

Grundoon
Grundoon

Reputation: 2764

As it stands, the code will loop indefinitely. What is the exit condition? As @Petr points out, what does the function return?

Below is a version which exits and returns unit when the inputArray is empty:

let rec splitArrayIntoGroups (inputArray: string[]) (groupSize: int) (hashSetOfGroups: HashSet<string[]>)=

    match inputArray with
    | [||] -> ()
    | _ ->
        let startIndex = 0
        let endIndex = groupSize - 1
        let group = inputArray.[startIndex .. endIndex]
        let nextInputArray = inputArray.[groupSize .. inputArray.Length - 1]

        hashSetOfGroups.Add(group) |> ignore
        splitArrayIntoGroups nextInputArray groupSize hashSetOfGroups

Rather than using a mutable set, a more idiomatic way would be to use the F# Set type and then pass a new version to each recursion, like this:

let rec splitArrayIntoGroups2 inputArray groupSize hashSetOfGroups =

    match inputArray with
    | [||] -> hashSetOfGroups 
    | _ ->
        let startIndex = 0
        let endIndex = groupSize - 1
        let group = inputArray.[startIndex .. endIndex]
        let nextInputArray = inputArray.[groupSize .. inputArray.Length - 1]

        let newSet = Set.add group hashSetOfGroups
        splitArrayIntoGroups2 nextInputArray groupSize newSet 

Btw, the logic as it stands appears to be buggy re the index logic. If I try the following:

let urlArray = [| "a"; "b"; "c"; "d" |]
let result = splitArrayIntoGroups2 urlArray 10 Set.empty

then I get an IndexOutOfRangeException.

Did you mean something like this instead?

let rec splitArrayIntoGroups3 inputArray startIndex groupSize hashSetOfGroups =

    let maxIndex = Array.length inputArray - 1
    if startIndex > maxIndex  then
        hashSetOfGroups 
    else
        let endIndex = min (startIndex + groupSize - 1) maxIndex 
        let group = inputArray.[startIndex .. endIndex]
        let newSet = Set.add group hashSetOfGroups

        let nextStartIndex = endIndex + 1
        splitArrayIntoGroups3 inputArray nextStartIndex groupSize newSet 

let urlArray = [| "a"; "b"; "c"; "d"; "e"  |]
let result = splitArrayIntoGroups3 urlArray 0 2 Set.empty

Note that this final version works with arrays of any type, not just string arrays.

Upvotes: 5

Related Questions