-
Notifications
You must be signed in to change notification settings - Fork 139
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
Enrich mutable API for vectors #334
Comments
Looks reasonable, but I would strongly prefer names which at least do not clash with immutable vector functions. Also it is quite unexpected to encounter In CC @haskell-core (not sure if I can ping an organization) |
RE: name clashes. Only way to avoid them is to add prefixes for all mutable versions. Also there's precedent for name clashes: init, tail, drop, replicate etc clash already. RE: |
I am 💯 in favor of everything in this issue. The only thing I guess we need to reach consensus on is the naming. I agree |
I'd like to propose following naming conventions.
I think that |
I do not see much benefit in brevity, certainly not enough to sacrifice clarity, to be honest. |
I'd agree with you if That being said, this is not something I care too deeply about, so whatever you guys decide on I'll be fine with. I just prefer shorter names as well thus wanted expressed my opinion on this subject. Most importantly we all agree on the concepts: folds / in place / creation 👍 |
I think we can absolutely go ahead with |
Inplace Suffix is probably the least confusing |
I feel strongly that something called
but an For the suggested functions that don't perform mutation – well, I'm not completely convinced these are necessary at all if they always just boil to some pure operation over a Name clashes I personally don't really mind, since I always import the immutable and mutable modules with different qualifiers, but how universal is this style? I could imagine quite a few packages would break if If we're going to have clashes, then once again: please no different semantics for same-name functions! |
Good points. Or ModifyWith?
It’s a tricky bike shed.
…On Sun, Oct 25, 2020 at 3:41 PM Justus Sagemüller ***@***.***> wrote:
I feel strongly that something called map or mapM should not perform any
in-place updates. As I commented in #326
<#326>, I would suggest
modifyM :: MVector (PrimState m) a -> (a -> m a) -> Int -> m ()
modifyAll :: MVector (PrimState m) a -> (a -> a) -> m ()
modifyAllM :: MVector (PrimState m) a -> (a -> m a) -> m ()
but an InPlace suffix also seems reasonable.
For the suggested functions that *don't* perform mutation – well, I'm not
*completely* convinced these are necessary at all if they always just
boil to some pure operation over a freezed MVector. Is there a
performance reason we'd want these directly for mutable vectors?
Name clashes I personally don't really mind, since I always import the
immutable and mutable modules with different qualifiers, but how universal
is this style? I could imagine quite a few packages would break if V.mapM
becomes ambiguous.
*If* we're going to have clashes, then once again: please no different
semantics for same-name functions!
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#334 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAABBQTH3AXBHH6ZSMULTFTSMR5O3ANCNFSM4SVEIFZA>
.
|
I thought about this one as well:
... just looked at the linked ticket and that was exactly where I thought about it 😄 Not sure
If you use This is what we want: mutableVectorOps mvec = do
MV.write mvec 0 "foo"
MV.mapM_ putStrLn mvec
MV.write mvec 1 "bar"
MV.mapM_ putStrLn mvec Above function is something we would like the user be able to write and not worry about issues that come along with conversion to immutable vectors:
mutableVectorOps mvec = do
MV.write mvec 0 "foo"
vec1 <- V.freeze mvec
V.mapM_ putStrLn vec1
write mvec 1 "bar"
vec2 <- V.freeze mvec -- second copy of the same vector, ouch
V.mapM_ putStrLn vec2
mutableVectorOps mvec = do
MV.write mvec 0 "foo"
vec <- V.unsafeFreeze mvec
V.mapM_ putStrLn vec
write mvec 1 "bar"
V.mapM_ putStrLn vec -- immutable vector was mutated, ouch |
Yeah, hand writing the unsafe freeze or thaw operations without a lotta
care results in terrible bugs in the presence of optimization. So having fp
style analogous operations provided for users makes sense
…On Sun, Oct 25, 2020 at 4:08 PM Alexey Kuleshevich ***@***.***> wrote:
I thought about this one as well:
I feel strongly that something called map or mapM should not perform any
in-place updates.
... just looked at the linked ticket and that was exactly where I thought
about it 😄
Not sure modify by itself is the right name though, but modifyAll and
modifyAllM sound reasonable and correlates nicely with modify
<https://hackage.haskell.org/package/vector-0.12.1.2/docs/Data-Vector-Mutable.html#v:modify>
function that deals with a single element..
For the suggested functions that don't perform mutation – well, I'm not
completely convinced these are necessary at all ...
If you use unsafeFreeze, then they are indeed not necessary, but the
problem is we do not want users to rely on unsafe functions. Let me outline
the actual problems with relying on freezing into pure vectors with
examples.
This is what we want:
mutableVectorOps mvec = do
MV.write mvec 0 "foo"
MV.mapM_ putStrLn mvec
MV.write mvec 1 "bar"
MV.mapM_ putStrLn mvec
Above function is something we would like the user be able to write and
not worry about issues that come along with conversion to immutable vectors:
- redundant copies:
mutableVectorOps mvec = do
MV.write mvec 0 "foo"
vec1 <- V.freeze mvec
V.mapM_ putStrLn vec1
write mvec 1 "bar"
vec2 <- V.freeze mvec -- second copy of the same vector, ouch
V.mapM_ putStrLn vec2
- unsafety of operations when no-copy approach is used
mutableVectorOps mvec = do
MV.write mvec 0 "foo"
vec <- V.unafeFreeze mvec
V.mapM_ putStrLn vec
write mvec 1 "bar"
V.mapM_ putStrLn vec -- immutable vector was mutated, ouch
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#334 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAABBQUXF7X2XOLPWPLBA7LSMSAUPANCNFSM4SVEIFZA>
.
|
I've added PR with what I think uncontroversial additions. Now there's question whether right folds should be included. We can't use laziness to abort computation early and have to read all elements anyway |
I suggest you pin this issue instead of #203, which was closed in favor of this. |
@konsumlamm Thanks, done. |
I just had a look at the different ways to construct mutable and immutable vectors and i noticed that mutable vectors have very few. It is proposed to add We could just port these as well, though that would mean writing a lot of (possibly unnecessary) code. It should be possible to implement all the mutable variants with Another thought I had was implementing a safe O(1) |
Not only it requires a new language extension, but it also makes this API available to GHC 9.0+ users only. I think it is too early to adopt linear types in |
I noticed that there already is an in-place |
What is the status of this work? Is there anything I can do to help it along? I am constantly frustrated when working with mutable vectors that I don't have |
There were quite a few proposal to enrich API of mutable vectors: #326, #203, #64, #175. Tris issue tries to collect all proposals from them in one nice list (and adds few on top of them)
Folds & lookup
foldlM :: (b -> a -> m b) -> b -> v s a -> m b
foldrM :: (a -> b -> m b) -> b -> v s a -> m b
mapM_ :: (a -> m ()) -> v s a -> m ()
/forM_
imapM_ :: (Int -> a -> m ()) -> v s a -> m ()
/forM_
ifoldlM
/ifoldrM
elemIndex
I'm not sure that
foldlM_
& friends are worth adding. It's easy to ignore return result of fold anyway:void
,_ <- ...
. It however could be useful to add pure variants:foldl :: (b -> a -> b) -> b -> v s a -> m b
foldr :: (a -> b -> b) -> b -> v s a -> m b
They come with performance bonus. We can perform entire fold in the ST monad and only lift to
m
at last moment. This way GHC only have to use bind from ST monad and it's good at optimizing itIn place mutation
Naming could be slightly confusing here. Should in place map be called
mapM
ormapM_
. Underscore usually means throwing away results of function andmapM
builds new structure. I'm slightly lean towardsmapM
since it's in-place mapmapM :: (a -> m a) -> v s a -> m ()
/forM
imapM :: (Int -> a -> m a) -> v s a -> m ()
/iforM
map :: (a -> a) -> v s a -> m ()
/ forimap :: (Int -> a -> a) -> v s a -> m ()
/ iformodifyM :: v s a -> (a -> m a) -> Int -> m ()
previousPermutation :: (PrimMonad m, Ord e, MVector v e) => v (PrimState m) e -> m Bool
(to complement nextPermutation)reverse
Creation
generate :: Int -> (Int -> a) -> m (v s a)
generateM :: Int -> (Int -> m a) -> m (v s a)
Comments? Objections? Proposals?
The text was updated successfully, but these errors were encountered: