-
Notifications
You must be signed in to change notification settings - Fork 52
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
fuseSetters function #117
Comments
Hi @mikesol! This looks interesting! Do you have any metrics on the change, or a snapshot of the difference in generated code? If this were added it would be good to include something like that in the documentation to better understand what this accomplishes. |
Below is a benchmark.
With fuseSettersmodule Main where
import Prelude
import Data.Array (replicate)
import Data.Lens (Setter', _1, _2, over, traversed)
import Data.Tuple (Tuple(..), fst, snd)
import Effect (Effect)
import Effect.Class.Console (log)
fuseSetters :: forall s a b c. Setter' a b -> Setter' a c -> Setter' s a -> Setter' s (Tuple (b -> b) (c -> c))
fuseSetters a b c l = over c (over a fa <<< over b fb)
where
t = l $ Tuple identity identity
fa = fst t
fb = snd t
setter = (traversed <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2)
main :: Effect Unit
main = do
void
( pure
$ over
(fuseSetters (_1 <<< _1) (_2 <<< _2) setter)
(const $ Tuple (const 42) (const 53))
( replicate
1000000
(Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 2 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 2 (Tuple 3 (Tuple (Tuple 0 1) (Tuple 5 8)))))))))))))))))))))
)
)
log "Done" Best run 07:45 meeshkan-abel@Abel:/tmp/lenz$ time spago run
[info] Installation complete.
[info] Build succeeded.
Done
real 0m3,191s
user 0m8,221s
sys 0m1,889s Without fuse settersmodule Main where
import Prelude
import Data.Array (replicate)
import Data.Lens (Setter', _1, _2, over, set, traversed)
import Data.Tuple (Tuple(..), fst, snd)
import Effect (Effect)
import Effect.Class.Console (log)
setter = (traversed <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2 <<< _2)
main :: Effect Unit
main = do
void
( pure
$ ((set (setter <<< _1 <<< _1) 42) <<< (set (setter <<< _2 <<< _2) 53))
( replicate
1000000
(Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 2 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 1 (Tuple 2 (Tuple 3 (Tuple (Tuple 0 1) (Tuple 5 8)))))))))))))))))))))
)
)
log "Done" Best run 07:44 meeshkan-abel@Abel:/tmp/lenz$ time spago run
[info] Installation complete.
[info] Build succeeded.
Done
real 0m6,320s
user 0m14,908s
sys 0m2,051s |
This seems like a useful utility to add. I don't have as much knowledge around how this would compose -- do you build up nested |
Thanks for the questions and feedback! Composition looks like this: module Main where
import Prelude
import Data.Array (replicate)
import Data.Lens (Setter', _1, _2, over, set, traversed)
import Data.Tuple (Tuple(..), fst, snd)
import Effect (Effect)
import Effect.Class.Console (log)
fuseSetters :: forall s a b c. Setter' a b -> Setter' a c -> Setter' s a -> Setter' s (Tuple (b -> b) (c -> c))
fuseSetters a b c l = over c (over a fa <<< over b fb)
where
t = l $ Tuple identity identity
fa = fst t
fb = snd t
cTuple :: forall a b c. a -> b -> c -> Tuple a b
cTuple a b = const $ Tuple a b
main :: Effect Unit
main = do
log
( show
$ over (fuseSetters _1 (fuseSetters _1 _2 _2) _2)
( cTuple
((+) 55)
(cTuple (flip (-) 101) ((*) 57))
)
(Tuple 0 (Tuple 0 (Tuple 1 2)))
) yields 06:35 meeshkan-abel@Abel:/tmp/lenz$ spago run
[info] Installation complete.
[info] Build succeeded.
(Tuple 0 (Tuple 55 (Tuple -100 114))) So the input type is a tree resembling the tree of the fused setters. |
fuseSetters :: forall s a b c. Setter' a b -> Setter' a c -> Setter' s a -> Setter' s (Tuple (b -> b) (c -> c))
fuseSetters a b c l = over c (over a fa <<< over b fb) ... The fuseSettersV2 :: forall a b c. Setter' a b -> Setter' a c -> Setter' a (Tuple (b -> b) (c -> c))
fuseSettersV2 ba ca l = (over ba fa <<< over ca fb) ... where fuseSetters b2a c2a a2s
-- is
a2s <<< fuseSettersV2 b2a c2a |
Nice find! It makes the syntax cleaner too. I've updated the PR. module Main where
import Prelude
import Data.Array (replicate)
import Data.Lens (Setter', Setter, _1, _2, over, set, traversed)
import Data.Tuple (Tuple(..), fst, snd)
import Effect (Effect)
import Effect.Class.Console (log)
fuseSetters :: forall a b c. Setter' a b -> Setter' a c -> Setter' a (Tuple (b -> b) (c -> c))
fuseSetters ba ca l = (over ba fa <<< over ca fb)
where
t = l (Tuple identity identity)
fa = fst t
fb = snd t
cTuple :: forall a b c. a -> b -> c -> Tuple a b
cTuple a b _ = Tuple a b
main :: Effect Unit
main = do
log
( show
$ over
(_2 <<< (fuseSetters _1 (_2 <<< (fuseSetters _1 _2))))
( cTuple
((+) 55)
(cTuple (flip (-) 101) ((*) 57))
)
(Tuple 0 (Tuple 0 (Tuple 1 2)))
) |
I'm not sure what value this provides. Isn't the alternative to After looking over it again, is it just the ability to separate the data from the setter while maintaining performance? I wonder if there's a way to achieve that without using Actually, after some more research, I think that |
This is a bit over my head, but what I would say is that if the idiom you've implemented works, it's much better to use that than Another reason not to include it would be that it's too use-case specific. Most folks won't need speed optimization on setters. |
Hello!
We've been using a function called
fuseSetters
a fair bit in production to speed up code where several setters are called on complex records (in our case, adding custom OpenAPI tags to a large OpenAPI spec).Here is the function with the output. If folks find it useful, I can make a PR to include it in the library:
yields
Thanks and let me know!
The text was updated successfully, but these errors were encountered: