[Haskell-cafe] RFC: Top level mutable state in terms of pure-function initialization?
roma at ro-che.info
Sun Dec 15 14:47:58 UTC 2013
Ok, it could work, but it can lead to very subtle bugs. You change the
type of IORef during refactoring (and forget to change it in a different
place), and your program now works wrong (but silently so), because now
it's creating two IORefs instead of one.
A better way would be to have phantomly-typed identifiers, like
class IsRefId c where
initialContents :: c a -> a
toInt :: c a -> Int
data MyRefId a where
Ref1 :: RefId Int
Ref2 :: RefId Bool
instance IsRefId MyRefId where ...
idToIORef :: IsRefId c => c a -> IORef a
Still can be abused (by not constraining phantom types properly), but
can also provide safety if used right.
(And no, Eq is not strictly necessary, because you convert to integers
* EatsKittens <temporalabstraction at gmail.com> [2013-12-15 15:05:40+0100]
> 1. Well, since x and y are not of the same type they would in this case
> contain a different IORef. The read in this case would read False since y
> is initialized to false as the default of Bool.
> 2. In this example it would again be two different IO refs since the type
> is different. It would again on demand create a different IORef for Int and
> 3. No, they refer to a different one as I said, the default-less version
> creates the IORef to be unique if and only if both the seed and
> initialization are the same.
> 4. Agreed, I overlooked that not all enum instances are typable. (I believe
> they also need to be an instance of EQ?)
> On 15 December 2013 14:38, Roman Cheplyaka <roma at ro-che.info> wrote:
> > 1. Well, that creates a loophole in the type system:
> > let
> > x :: IORef Int
> > x = enumToIORef 0
> > y :: IORef Bool
> > y = enumToIORef 0
> > in writeIORef x (5 :: Int) >> (readIORef y :: IO Bool)
> > 2. There's also a less obvious loophole with your Defaultable version:
> > let
> > x :: Defaultable a => IORef a
> > x = enumToIORef 0
> > in writeIORef x (5 :: Int) >> (readIORef x :: IO Bool)
> > 3. In your default-less version there's a problem of doing this:
> > x = enumToIORef 0 True
> > y = enumToIORef 0 False
> > Now, x and y presumably refer to the same IORef, but the initial
> > contents of that IORef will depend on whether you access it through x or
> > y.
> > 4. Finally, the implementation of enumToIORef can only inspect it by
> > converting to Int, and cannot distinguish different enums that
> > correspond to the same number.
> > This last problem can be solved by adding a Typeable constraint,
> > because TypeReps carry the information about where (module, package) the
> > type was defined.
> > Roman
> > * EatsKittens <temporalabstraction at gmail.com> [2013-12-15 12:39:20+0100]
> > > A pure function (enumToIORef :: (Enum a, Defaultable b) => a -> IORef b).
> > > This function returns referentially transparently an IORef as a function
> > of
> > > its "seed" with the guarantee that the IORef returned is identical if and
> > > only if the seed is. This function can be used to implement top level
> > > mutable state in a module. The module can specifically create an
> > enumerated
> > > type for this and not export it, thereby removing any possibility of
> > > another module passing the seed type and conflicting.
> > >
> > > The Defaultable class is added in this case to implement only a single
> > > method (defaultValue :: Defaultable a => a -> a). This is conceptionally
> > > the 'simplest' value of the type, such as the empty list, the number 0,
> > > False, the null character &c. The IORef returned by enumToIORef would be
> > > initialized before being written to to this specific default value of its
> > > type. This approach is chosen because it is impossible to initialize it
> > to
> > > user specified value because enumToIORef can be called twice with the
> > same
> > > seed but a different initial value.
> > >
> > > In the alternative it is also possible to do without the default value
> > and
> > > say the IORef returned is the same if and only if the seed and the
> > initial
> > > value given are the same. Allowing the function to remain referentially
> > > transparent as well. This would probably require for good semantics the
> > > underlying type of the IORef to be a member of EQ...?
> > >
> > > All this would of course require that newIORef and enumToIORef never
> > > produce the same IORef.
> > >
> > > Aside its limitations of the type IORef's initialized with this method
> > can
> > > carry, I do believe they cover the vast majority of use cases of top
> > level
> > > mutable state?
> > >
> > > Caveats?
> > > _______________________________________________
> > > Haskell-Cafe mailing list
> > > Haskell-Cafe at haskell.org
> > > http://www.haskell.org/mailman/listinfo/haskell-cafe
-------------- next part --------------
A non-text attachment was scrubbed...
Size: 836 bytes
Desc: Digital signature
More information about the Haskell-Cafe