Richard Dalton
Richard Dalton

Reputation: 272

FSharp calling a generic method in a base class

This question is based on the Functional Random Generators in week one of this course: https://www.coursera.org/course/reactive

The course is Scala based, I'm trying to replicate it in FSharp.

Here's my problem:

I have an abstract generator

[<AbstractClass>]
type Generator<'a>() = 
    abstract member Generate: 'a

And I have an implementation of that that generates random integers

type IntGenerator() =
    inherit Generator<Int32>()

    let rand = new System.Random()
    override this.Generate = rand.Next(Int32.MinValue, Int32.MaxValue)

Now, I want to add a Map method to my base class so that new types of generators can be created using code like this

let integers = new IntGenerator()        
let booleans = integers.Map (fun x -> x > 0)

So, here's how I modified the base class

[<AbstractClass>]
type Generator<'a>() = 
    abstract member Generate: 'a
    member this.Map (f:'a -> 'b) = { new Generator<'b>() with member this.Generate = f base.Generate }

Unfortunately that call to base.Generate seems to be constraining the type 'b to the same as 'a

I don't get why. I'm sure I'm tripping up on something simple.

Upvotes: 3

Views: 221

Answers (1)

Random Dev
Random Dev

Reputation: 52280

The problem here is that you have to be careful with the instance names of the member methods:

I think this should work out:

[<AbstractClass>]
type Generator<'a>() = 
    abstract member Generate: unit -> 'a
    static member Map (f:'a -> 'b) = 
        fun (g : Generator<'a>) ->
        { new Generator<'b>() with 
            member this.Generate () = g.Generate () |> f 
        }

or if you insist on the member-method:

[<AbstractClass>]
type Generator<'a>() = 
    abstract member Generate: unit -> 'a
    member this.Map (f:'a -> 'b) : Generator<'b> = 
        { new Generator<'b>() with 
            member __.Generate () = this.Generate () |> f 
        }

note the differences with the this and base and __ ;)

BTW this will even work without the unit -> (as you wrote it):

[<AbstractClass>]
type Generator<'a>() = 
    abstract member Generate: 'a
    member this.Map (f:'a -> 'b) : Generator<'b> = 
        { new Generator<'b>() with 
            member __.Generate = this.Generate |> f 
        }

but I would not recommend this as you get a value-looking method in disguise: Generate

more idiomatic way

type Generator<'a> = unit -> 'a

module Generator =
    let map (f:'a -> 'b) (g : Generator<'a>) =
        fun () -> g () |> f

    let intGenerator : Generator<int> =
        let rand = new System.Random()
        fun () -> rand.Next(System.Int32.MinValue, System.Int32.MaxValue)

fun fact

As you can see here (if you look closely) you will see that this map is really just the Functor-map for unit -> * ;)

disclaimer but of course as the generators are sadly impure none of the functor-laws will really hold (if you don't fix your system time)

Upvotes: 3

Related Questions