Fwd: [Haskell-beginners] More Deserialization Woes

Daniel Fischer daniel.is.fischer at web.de
Tue Jul 6 07:01:59 EDT 2010


On Tuesday 06 July 2010 12:06:53, Tom Hobbs wrote:
> On Tue, Jul 6, 2010 at 10:42 AM, Daniel Fischer 
<daniel.is.fischer at web.de>wrote:
> > Nevertheless, IO (Maybe [String]) is, I believe, the appropriate type.
>
> I accept that you guys have most likely forgotten more about Haskell
> than I know,

Well, we're here to change that :)

> but can you explain to me why this is the appropriate type?

For one thing, because main's type is IO (), so ultimately you will have IO 
as the outermost Monad.
If you must seriously intertwine I/O with pure computations, using a Monad-
transformer with IO as the innermost Monad would be appropriate, but 
(admittedly I took just a cursory glance at what you try to do) that didn't 
seem the case here.
If I/O needn't be woven into the computation that strongly, it's better to 
keep I/O and computations as separate as possible (makes refactoring 
easier, for example), that usually means you have a small IO-wrapper 
calling pure functions to work with the data gotten from IO.

But as Yitz pointed out, deserialisation throws exceptions if things go 
wrong, so

IO [String]

could be more appropriate.

>
> Note to self: I think I've got the answer to this at the end of this
> email!
>
> > > However, the result of "IO [Just "a", Just "b", Nothing, Nothing]"
> > > would signify that communication failed halfway through and would
> > > not make sense in my context.  This is what the advice seems to be
> > > suggesting I write.  But in this case, I'd prefer to return
> > > "Nothing" to signify that a problem occurred.
> >
> > You can do that by applying sequence (at the type [Maybe String] ->
> > Maybe [String]).
> >
> > Say you have
> >
> > ping0 :: args -> IO [Maybe String]
> >
> > then you'd use
> >
> > ping :: args -> IO (Maybe [String])
> >  ping = fmap sequence ping0
>
> Okay, I've looked at the definition of sequence in Prelude and I think I
> understand what's going on.  I need to experiment with it first before I
> fully get it.

That's normal. The definition of sequence isn't immediately accessible.

>
> So if ping0 returns IO [Just "a", Just "b", Nothing] then then what
> would ping return?  IO (Just ["a", "b", ?]).  Nevermind, I can play with
> that and work it out later.

Prelude> sequence $ [Just (1 :: Int), Just 2, Just 3]
Just [1,2,3]
Prelude> sequence $ [Just (1 :: Int), Just 2, Nothing, Just 3]
Nothing

And sequence short-cuts when there's a Nothing:
Prelude> sequence $ [Just (1 :: Int), Just 2, Nothing, Just 3] ++ replicate 
1000000000 (Just 4)
Nothing
(0.00 secs, 0 bytes)
Prelude> sequence $ [Just (1 :: Int), Just 2, Nothing, Just 3] ++ repeat 
(Just 4)
Nothing
(0.01 secs, 0 bytes)

>
> Thinking to my requirement I think I'd want something more like;
>
> ping :: args -> IO (Maybe [String])
> ping args = fmap f ps
>                  where
>                  ps = ping0 args
>                  f | Nothing `elem` ps = Nothing
>
>                    | otherwise             = fmap sequence ps
>
> Or would I get this behaviour for free straight from the use of
> sequence?
>

Yes, sequence does that automatically for you :)
The problem is that sequence must traverse the entire list to know whether 
it'll return Nothing or (Just list) if there's no Nothing, so it can take a 
while if the list is long (and forever, if the list is infinite and doesn't 
contain a Nothing).

> > > Is it possible, to extract the values out of the IO monad so it can
> > > be used in pure functions.
> >
> > Yes, but you needn't (and shouldn't in general).
>
> Again, what is the why?
>
> My theory is that I want to get [String] from ping, and then (possibly)
> do lots of other things with it that don't require any kind of IO.  But
> maybe that's not true and I need to think a bit harder about what the
> caller is likely to do with the result from ping.  In terms of
> performance (memory footprint, speed of execution, etc) does "carrying
> around the IO monad" make much of a difference?
>
> <insert five minute reflective pause here>
>
> You're right.  I don't need to pull the [String] out of the IO monad.
>  Suprise, suprise, the general pattern is appropriate in my case. 
> Something clicked in my head when I re-read "pureStuff someValues" -
> this time is was a good click though!
>
> Please understand, I'm challenging you on the answers because I don't
> understand them, not because I think they're wrong!  :-)

I understand. I would've gone into more detail, but I was in a hurry :)

>
> Thanks again for the very quick and helpful answers!
>
> Tom


More information about the Beginners mailing list