vector icon indicating copy to clipboard operation
vector copied to clipboard

Implement Applicative based API

Open Shimuuar opened this issue 1 year ago • 1 comments

Implementation follows plan described in #477 with generateA :: Applicative f => Int -> (Int -> f a) -> f (v a) as primitive. It is not subject to stream fusion.

Writing generateA is not difficult. Problem is doing it efficiently. Current benchmarks are (suggestions for more are very welcome):

  1. Generate vector of random numbers using state monad
  2. Generate vector using IO action
  3. Compute sum of vector using lens
  4. Map vector using lens

First and naive implementation uses list as intermediate data structure. Sum benchmark performs well in this case. Using STA instead brings map benchmark on par with explicit loop and produces slight (5-10%) improvements in state and IO benchmark)

newtype STA v a = STA {  _runSTA :: forall s. Mutable v s a -> ST s (v a) }

Currently sum and map perform on par with explicit loop. State gives 7x slowdown and 8x allocations, IO benchmark 4x slowdown and 8x allocations. We obviously can add rewrite rules for IO/ST but maybe there're more general optimizations.


Fixes #477, #69, #132, #144

Shimuuar avatar Jan 28 '25 09:01 Shimuuar

What about for/for_/traverse_ etc?

Good point! I forgot about them. But they're simpler since in this case one isn't in business of constructing vector so it simply reduces to fold.

Shimuuar avatar Jan 28 '25 14:01 Shimuuar

I think that's about as far as reasonably possible for improving performance.

For many PrimMonads we would like to simply create buffer and just write to it. But it's not safe in general case, applicatives with backtracking can break referential transparency. And it's not clear how to express such rewrite rule and whether it's even possible. Also unstreamM suffers from same problem

Shimuuar avatar Sep 03 '25 16:09 Shimuuar

I think PR is ready.

Shimuuar avatar Sep 05 '25 08:09 Shimuuar

I think that's about as far as reasonably possible for improving performance.

For many PrimMonads we would like to simply create buffer and just write to it. But it's not safe in general case, applicatives with backtracking can break referential transparency. And it's not clear how to express such rewrite rule and whether it's even possible. Also unstreamM suffers from same problem

We definitely can write such rewrite rules for specific cases.

treeowl avatar Sep 09 '25 10:09 treeowl

And for IO/ST there're such rules already. But it seems there's no way to write such rules compositionally.

If for example we want to add rewrite rules for WriterT/ReaderT/StateT over IO then there're exponentially many types for given stack depth. We probably need some variant of typeclass guided rewrites

Shimuuar avatar Sep 09 '25 10:09 Shimuuar

Sorry for not reviewing it earlier. Very busy time for me right now.

No problem. I you want to make a review but don't have time at the moment please leave note. I would wait

Shimuuar avatar Sep 12 '25 15:09 Shimuuar