Reputation: 272
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
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
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)
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