Radosław Łazarz
Radosław Łazarz

Reputation: 968

Problems with generics and casting in F#

Using a genome type implemented in a following way

type Genome<'T> (listIn : List<'T>)=
    member this.list = listIn
    member this.GetValues() =
        this.list

I ran into a problem while writing another code fragment.

type IMutation =
    abstract member MutateMethod: Genome<'T> -> Genome<'T>

type DoubleGenomeMutation() = 
    member this.Mutate = (this :> IMutation).MutateMethod
    interface IMutation with
        member this.MutateMethod(genome: Genome<'T>) = 
            let randomIndex = MyRandom.Next(0, genome.list.Length)
            printfn "%d" randomIndex
            let newValue = MyRandom.NextDouble(Parameters.LowerBounds.Item(randomIndex), Parameters.UpperBounds.Item(randomIndex))

            let (newGenomeList : List<'double>, _) = 
                List.foldBack (fun elem acc -> 
                match acc with
                | (l, 0) -> (newValue::l, -1)
                | (l, i) -> (elem::l, i-1)) (genome.GetValues()) ([], genome.GetValues().Length - randomIndex - 1)

            new Genome<'T>(newGenomeList)

I found it impossible to use the generics mechanism correctly here. The quoted sample effects in

Error   2   Type mismatch. Expecting a
    float list    
but given a
    List<'a>    
The type 'float' does not match the type ''a'

in line

| (l, i) -> (elem::l, i-1)) (genome.GetValues()) ([], genome.GetValues().Length - randomIndex - 1)

Casting a variable up or down was ofter described as solution in similar cases - unfortunately I was unable to fix my program this way. I would appreciate any direct help as well as any suggestions about the root of all problems.

Upvotes: 1

Views: 100

Answers (1)

Ganesh Sittampalam
Ganesh Sittampalam

Reputation: 29120

I don't have the source of MyRandom, but if I replace MyRandom.Next with an int as it looks like it should be and replace MyRandom.Double with a double, then I can reproduce your error.

The problem is that the return type of MyRandom.NextDouble is constraining the rest of the code inappropriately; MutateMethod is declared to be able to take any type argument, but then you use the newValue as part of the return list in the (l, 0) case which constrains the type of the other case too.

You may also have got a warning from newValue pointing out that it was constraining your 'double type variable to be of type float which is another clue. Really 'double should be 'T as that's how the parameter is originally declared.

The error goes away if I replace the call to MyRandom.NextDouble with throwing an exception (which can have any result type), which confirms this diagnosis:

type DoubleGenomeMutation() = 
    member this.Mutate = (this :> IMutation).MutateMethod
    interface IMutation with
        member this.MutateMethod(genome: Genome<'T>) = 
            let randomIndex = 1 // MyRandom.Next(0, genome.list.Length)
            printfn "%d" randomIndex
            let newValue = failwith "foo"

            let (newGenomeList : List<'T>, _) = 
                List.foldBack (fun elem acc -> 
                match acc with
                | (l, 0) -> (newValue::l, -1)
                | (l, i) -> (elem::l, i-1)) (genome.GetValues()) ([], genome.GetValues().Length - randomIndex - 1)

            new Genome<'T>(newGenomeList)

Upvotes: 2

Related Questions