
apfelmus schrieb:
Benedikt Huber wrote:
So, the Ref deriviation is really nice for sequential updates; parallel updates on the other hand need some work. .. While the select part of the Ref is expressed using &&&, I don't know how the parallel update can be expressed in terms of combinators. Any hints ?
You can't do that, and for good reason! While
players :: Lens Game (Player,Player)
is entirely fine since Game ~ (Player,Player,Object2D), there cannot be a general parallel combinator
(&&&) :: Lens a b -> Lens a c -> Lens a (b,c)
Thanks for the nice overview. I see there can't be a general purpose combinator (&&&) for lenses. One could still define a helper function though: combineDisjoint :: Lens a b -> Lens a c -> Lens a (b,c) combineDisjoint l1 l2 = Lens $ select &&& update where select = (fst . focus l1) &&& (fst . focus l2) update cx (a,b) = flip (snd . focus l2) b $ (snd . focus l1) cx a which performs the first update using the initial context, and the second one using the updated context. Just to have a simple way of defining lensPlayers in term of lensPlayer1 `combineDisjoint` lensPlayer2. While it is not a (general purpose) combinator, it is still helpfull for combining lenses focusing on fields of a record. -- I just want to rephrase my question about paralell updates; it has more to do with records updates than with References / Lenses, though: Suppose we have a record data R = R { a:: A, b :: B, c :: C } deriving (Show {-! Ref !-}) and update functions fa :: a -> a, fb :: b -> b, fc :: c -> c Now the standard way to perform the update on R would be updateR = \r@(R {a=a,b=b,c=c}) -> r { a = fa a,b = fb b,c = fc c } which is 'a little bit' cumbersome. With update deriviations (like Update using DrIFT), references (Ref using Data.Derive) or lenses it is easy to perform the update sequentially (using DrIFT style for simplicity): updateR' = a_u fa . b_u fb . c_u fc But this corresponds to updateR' = (\f r -> r { a = f (a r) }) fa . (\f r -> r { b = f (b r) }) fb . (\f r -> r { c = f (c r) }) fc which (in some way) is not 'the same' as updateR. First, I (uneducatedly) guess that the record updates cannot be 'executed' in paralell, i.e. the record has to be deconstructed and build up again three times. And second, neither the types of the updates (e.g. a_u fa :: R -> R) nor the structure of updateR' (composing R->R functions) do reflect the fact that actually disjoint fields of the record are updated. Now I know there are great record proposals (which extend the haskell language or use some type hackery), but I wondered if there is also a solution which can be used right now and which works fine with the standard record types. I'll give a naive ad-hoc example to illustrate the idea. One could automatically derive the following data type and the associated functions for R: data R_upd = R_upd { updA :: A -> A, updB :: B -> B, updC :: C -> C } rUpd = R_upd id id id updR :: R_upd -> R -> R updR rupd r@(R { a=a,b=b,c=c }) = r { a = (updA rupd a), b = (updB rupd b), c = (updC rupd c) } which would allow to write things like updGame $ gameUpdate { updPlayer1 = increaseScore, updPlayer2 = decreaseScore }) Though simple, I hope it is possible to understand the idea (I know there is a lot of namespace pollution). And of course, someone has thought of something much more sophistacted already :) What are the drawbacks of such an approach ? thanks, benedikt