Discussion: New atomic IORef functions

David Feuer david.feuer at gmail.com
Wed Jul 11 18:31:00 UTC 2018


I'm not committed to changing atomicModifyIORef. I'm much more
interested in adding atomicModifyIORef2, atomicModifyIORef'_, and
atomicSwapIORef. I do think it would be helpful to get a pair-strict
version of atomicModifyIORef (atomicModifyIORefP?), but I guess it's
not horrible if users have to write their own with atomicModifyIORef2.
The lazy atomicModifyIORef2Lazy and atomicModifyIORefLazy_ are pretty
optional: I see their laziness as more an implementation detail than
an essential feature.

On Wed, Jul 11, 2018 at 2:22 PM, Dr.Koster <drkoster at qq.com> wrote:
> But if you force the pair result, you have to evaluate modifying function
> isn't it? That's sometime unwanted when you have an very expensive f here,
> let's say a deep binary search which might not depend previous result.
>
> I think that is also why current atomicModifyIORef is designed this way, so
> I'd rather keep it the old way.
>
> 发自我的iPhone
>
>
> ------------------ Original ------------------
> From: David Feuer <david.feuer at gmail.com>
> Date: Thu,Jul 12,2018 0:51 AM
> To: winter <drkoster at qq.com>
> Cc: Haskell Libraries <libraries at haskell.org>
> Subject: Re: Discussion: New atomic IORef functions
>
> All the fundamental functions I've defined can be usefully used in the base
> library. I think that's one good reason to put them there. Another is that
> while the basic array operations have traditionally been exposed through
> array, vector, and primitive, and the basic TVar operations have been
> exposed through stm, the basic IORef and MVar operations have been exposed
> through base (except, for some reason, casMutVar#). I see no reason to
> change that.
>
> I never proposed a function that forces the previous value unnecessarily, so
> I don't know why you're complaining about that. The extra laziness I don't
> like is in the pair result; none of the uses I've seen thus can far make
> intentional use of that. That's why I tend to think atomicModifyIORef (as it
> exists today) is almost never what people actually want.
>
> Squeezing into a second component leads to extra allocation in what may be a
> performance-critical function; that said, I'm willing to hold off on higher
> tuples for now.
>
> On Sun, Jul 8, 2018, 3:36 AM winter <drkoster at qq.com> wrote:
>>
>> I believe new variations should always be motivated by use-case if
>> there're too many choices, the lazy behavior of old `atomicModifyIORef` is
>> justified by some cases the modifying functions are lazy in its argument,
>> thus a lazy version could win by not forcing previous thunks, we'd want to
>> keep its behavior as how it's documented.
>>
>> As for tuples more than pairs, they're not really needed, user can always
>> squeeze their product into `b` component.
>>
>> IMHO only the addition of `atomicModifyIORef_` is sensible in the context
>> of base, other APIs may go to package like primitives. But if you have a
>> motivated use case with `atomicModifyIORef2`, etc. Please tell me.
>>
>>
>> On 2018年07月08日 03:09, David Feuer wrote:
>>
>> Whoops! I left out the proposal link:
>>
>> https://github.com/ghc-proposals/ghc-proposals/pull/149
>>
>> Also, what I called atomicModifyIORef_ below should really be called
>> something like atomicModifyIORef'_, since it forces a polymorphic value.
>>
>> Another thing to note: the underlying atomicModifyMutVar2# primop actually
>> supports more than just pairs. It can handle triples, solos, and any other
>> record types whose first components are lifted:
>>
>> atomicModifyIORefSoloLazy
>>   :: IORef a -> (a -> Solo a) -> IO (Solo a)
>>
>> atomicModifyIORefSolo
>>   :: IORef a -> (a -> Solo a) -> IO a
>>
>> atomicModifyIORef3, atomicModifyIORef3Lazy
>>   :: IORef a -> (a -> (a, b, c)) -> IO (a, b, c)
>>
>> etc.
>>
>> Should we add any such?
>>
>> On Sat, Jul 7, 2018, 2:35 PM David Feuer <david.feuer at gmail.com> wrote:
>>>
>>> I have proposed[1] the replacement of the atomicModifyMutVar# primop, and
>>> the addition of two cheaper but less capable ones. It seems likely that the
>>> proposal will succeed, but that the GHC steering committee will leave the
>>> question of user interface changes to the libraries list. I would like to
>>> open the discussion here.
>>>
>>> The new primops lead naturally to several thin wrappers:
>>>
>>> -- Atomically replace the IORef contents
>>> -- with the first component of the result of
>>> -- applying the function to the old contents.
>>> -- Return the old value and the result of
>>> -- applying the function, without forcing the latter.
>>> --
>>> -- atomicModifyIORef ref f = do
>>> --   (_old, ~(_new, res)) <- atomicModifyIORef2Lazy ref f
>>> --   return res
>>> atomicModifyIORef2Lazy
>>>   :: IORef a -> (a -> (a, b)) -> IO (a, (a, b))
>>>
>>> -- Atomically replace the IORef contents
>>> -- with the result of applying the function
>>> -- to the old contents. Return the old and
>>> -- new contents without forcing the latter.
>>> atomicModifyIORefLazy_
>>>   :: IORef a -> (a -> a) -> IO (a, a)
>>>
>>> -- Atomically replace the IORef contents
>>> -- with the given value and return the old
>>> -- contents.
>>> --
>>> -- atomicWriteIORef ref x = void (atomicSwapIORef ref x)
>>> atomicSwapIORef
>>>   :: IORef a -> a -> IO a
>>>
>>> Based on the code I've read that uses atomicModifyIORef, I believe that
>>> the complete laziness of atomicModifyIORef2Lazy and atomicModifyIORefLazy_
>>> is very rarely desirable. I therefore believe we should also (or perhaps
>>> instead?) offer stricter versions:
>>>
>>> atomicModifyIORef2
>>>   :: IORef a -> (a -> (a, b)) -> IO (a, (a, b))
>>> atomicModifyIORef2 ref f = do
>>>   r@(_old, (_new, _res)) <- atomicModifyIORef2Lazy ref f
>>>   return r
>>>
>>> atomicModifyIORef_
>>>   :: IORef a -> (a -> a) -> IO (a, a)
>>> atomicModifyIORef_ ref f = do
>>>   r@(_old, !_new) <- atomicModifyIORefLazy_ ref f
>>>   return r
>>>
>>> The classic atomicModifyIORef also admits a less gratuitously lazy
>>> version:
>>>
>>> atomicModifyIORefNGL
>>>   :: IORef a -> (a -> (a,b)) -> IO b
>>> atomicModifyIORefNGL ref f = do
>>>   (_old, (_new, res)) <- atomicModifyIORef2 ref f
>>>   return res
>>>
>>> Should we add that as well (with a better name)? Should we even consider
>>> *replacing* the current atomicModifyIORef with that version? That could
>>> theoretically break existing code, but I suspect it would do so very rarely.
>>> If we don't change the existing atomicModifyIORef now, I think we should
>>> consider deprecating it: it's very easy to accidentally use it too lazily.
>>
>>
>>
>> _______________________________________________
>> Libraries mailing list
>> Libraries at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>>
>>
>> _______________________________________________
>> Libraries mailing list
>> Libraries at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


More information about the Libraries mailing list