-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Random.map without stack overflow #289
Comments
The behavior might depend on |
I should be more specific. Here is a test that fails because [<Fact>]
let ``map does not overflow the stack`` () =
let flip f b a = f a b
let n = 100_000
id
|> List.replicate n
|> List.fold (flip Random.map) (Random.constant 1)
|> Random.run (Seed.random ()) 0
|> ignore |
There is something subtle that I don't understand yet between left and right function composition. This test using right composition fails even when compiled in [<Fact>]
let ``Right composition overflow the stack`` () =
let n = 1_000_000
let f =
id
|> List.replicate n
|> List.fold (>>) id
f () But the same test using left composition passes when compiled in [<Fact>]
let ``Left composition overflow the stack`` () =
let n = 10_000_000
let f =
id
|> List.replicate n
|> List.fold (<<) id
f () |
There is a difference between Maybe a John Hughes difference list could be used to reverse the order of the function composition and thus avoid the stack overflow without any calls to |
Yes and no. I think the calls to Here is the type, a partial implementation, and some tests. I will create a full implementation and some benchmarks soon. type Random<'a> =
private { Initial: Seed -> Size -> obj
Mappings: (obj -> obj) -> obj -> obj
Unbox: obj -> 'a }
module Random =
let private unsafeRun (seed : Seed) (size : Size) (data : Random<'a>) : 'a =
data.Initial seed size
|> data.Mappings id
|> data.Unbox
let run (seed : Seed) (size : Size) (r : Random<'a>) : 'a =
unsafeRun seed (max 1 size) r
let constant (x : 'a) : Random<'a> =
{ Initial = fun _ _ -> box<'a> x
Mappings = fun f obj -> f obj
Unbox = unbox<'a> }
let map (f : 'a -> 'b) (data: Random<'a>) : Random<'b> =
{ Initial = data.Initial
Mappings = (>>) (unbox<'a> >> f >> box<'b>) >> data.Mappings
Unbox = unbox<'b> }
[<Fact>]
let ``map does not overflow the stack`` () =
let flip f b a = f a b
let n = 1_000_000
id
|> List.replicate n
|> List.fold (flip Random.map) (Random.constant 1)
|> Random.run (Seed.random ()) 0
|> ignore
[<Fact>]
let ``map can change its type parameter`` () =
Random.constant 3.14159
|> Random.map (sprintf "%f")
|> Random.map (fun s -> s |> String.length)
|> Random.map (fun n -> n % 2 = 0)
|> Random.run (Seed.random ()) 0
|> ignore |
I was able to simplify. Now there is only one function (like the code in type Random<'a> = private Random of (('a -> obj) -> Seed -> Size -> obj)
module Random =
let private unsafeRun (seed : Seed) (size : Size) (Random r: Random<'a>) : 'a =
r box<'a> seed size |> unbox<'a>
let run (seed : Seed) (size : Size) (r : Random<'a>) : 'a =
unsafeRun seed (max 1 size) r
let constant (x : 'a) : Random<'a> =
Random (fun f _ _ -> x |> f)
let variable (f: Seed -> Size -> 'a) : Random<'a> =
Random (fun g seed size -> f seed size |> g)
let map (f : 'a -> 'b) (Random r: Random<'a>) : Random<'b> =
(>>) f >> r |> Random |
There have been great improvements to Recall that
So we can implement one of these functions "directly" and then define the renaming one via the first one. I think we should directly define |
Ok, I will try. Let's do PR #314 first. |
This issue is a spin off of #238 (comment)
Recall that the type
'a -> 'b
is (covariant) functor in'b
. Themap
function for this functor is function composition (which is either>>
or<<
with only the order of inputs chanding). OurRandom<'c>
type is a (covariant) functor in'c
because it is just an wrapper around the type'a -> 'b -> 'c
, which is also a (covarant) functor in'c
. We can simplify things by uncurrying to get back to a function of the form'a -> 'b
.The naive way to implement
map
for the (covariant) functor'a -> 'b
can overflow the stack. Specifically, the following test fails.Here is one way to avoid overflowing the stack. I will be the first to say that this is not elegant.
The text was updated successfully, but these errors were encountered: