I was working on creating my own custom mutable/frozen data type that internally contains an MVector
/ Vector
. It needs to be mutable for performance reasons so switching to an immutable data structure is not something I am considering.
It seems like implementing an observer function for one of the two versions should allow me to just steal that implementation for the other type. Here are the two options I am considering:
render :: Show a => MCustom s a -> ST s String
render mc = ...non trivial implementation...
show :: Show a => Custom a -> a
show c = runST $ render =<< unsafeThaw c
Where unsafeThaw
calls Vector.unsafeThaw
under the covers, which should be safe as that thawed vector is never mutated, only read. This approach feels the cleanest, the only downside is that render
is strict, which forces show
to be strict whereas a duplicate implementation could correctly stream the String without forcing it all at once.
The other option, which feels much more dirty but that I think is safe is to do this:
show :: Show a => Custom a -> a
show c = ...non trivial implementation that allows lazy streaming...
render :: Show a => MCustom s a -> ST s String
render mc = do
s <- show <$> unsafeFreeze mc
s `deepseq` pure s
Are either of these my best option? If not what else should I do?
To me it seemed most intuitive to build one version off of the other. But it seems like if I make the mutable version the base version then I will end up with a lot more strictness then I want, even if the implementations seem fairly clean and logical, just because ST
necessitates strictness unless I throw in some unsafeInterleaveST
calls, but these would only be safe when the mutable observer was called via an immutable object.
On the other hand if I make the immutable version the base version then I will end up with more dirty, deepseq
code, and sometimes I would just have to reimplement things. For example all in place editing functions can be done on a frozen object pretty easily by just copying the frozen object and then calling unsafeThaw
on it and modifying the copy in place before calling unsafeFreeze
and returning it. But doing the opposite isn't really doable, as a copy modification that is used for the immutable version cannot be converted to an in place modification.
Should I perhaps write all modification functions alongside the mutable implementation, and all observer functions alongside the immutable implementation. And then have a file that depends on both that unifies everything via unsafeThaw
and unsafeFreeze
?
How about having a pure function
show :: (StringLike s, Show a) => Custom a -> s
You can get both lazy and strict output with different instantiations of s
, in which cons
is either lazy or strict; eg String
or Text
:
class StringLike s where
cons :: Char -> s -> s
nil :: s
uncons :: s -> Maybe (Char, s)
instance StringLike String where ...
instance StringLike Text where ...
You could use other methods, eg phantom types, or simply having two functions ( showString
and showText
), to distinguish between lazy and strict output if you like. But if you look at types as specifications of a function's semantics, then the place to indicate laziness or strictness is in the return type of that operation. This removes the need for some sort of strict show
for Custom
inside of ST
.
For the MCustom
version, you probably do not export the String
version, eg:
render :: MCustom s a -> ST s Text
render a = show <$> unsafeFreeze a
You can throw in a seq
to force the result when the function runs but the entire Text
would be forced when any character is used anyways.
But the simplest solution seems to just abstract the pattern of using a mutable structure in an immutable fashion, eg
atomically :: (NFData a) => (Custom x -> a) -> MCustom s x -> ST s a
atomically f v = do
r <- f <$> unsafeFreeze v
r `deepseq` pure r
This saves you from using unsafeFreeze/deepseq
all over your code, just as you have modify
to do immutable operations on mutable vectors.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.