I would represent the data structure in a pure way, and restrict the IO monad to the operations that actually do IO. If you need some kind of mutable graph, I suggest representing that graph as a map (Data.Map) from node names to neighbors. The "mutation" is then just updating the map. An extra benefit from this is that you get really simple undo in your editor.
-- Lennart On Thu, Sep 4, 2008 at 8:53 AM, minh thu <[EMAIL PROTECTED]> wrote: > 2008/9/4 Timothy Goddard <[EMAIL PROTECTED]>: >> It looks like this code isn't really in fitting with normal Haskell idioms. > > I guess you mean by Haskell idiom : "pure". But Haskell allows you to > do IO, etc. > >> Emulating C in Haskell will only give you a harder way of writing C. Don't >> think about emulating a potentially null pointer with Maybe (IORef a) and >> passing this to a function to read content unless you also want to implement >> the function "segfault :: IO ()". > >> You really need to ask yourself whether it makes sense to read a NULL / >> Nothing field. In C this would cause a segfault. The idea in Haskell is that >> you don't allow invalid values to be passed in to a function at all. Each >> function should be pure and accept only sensible inputs, allowing you to >> analyse what each function does in isolation from the rest of the program. >> >> In Haskell, functions should use the type system to only accept arguments >> which make sense. The caller should handle the possibility that a function it >> calls returns nothing, not expect every other callee to do so. The Maybe >> monad helps with this for many cases: >> >> lookupEmployee :: Integer -> Maybe Employee >> lookupPassportNo :: Employee -> PassportNo >> lookupMarriageCertificate :: PassportNo -> Maybe MarriageCert >> getPassportNumbers :: MarriageCert -> (PassportNo, PassportNo) >> getNameFromPassport :: PassportNo -> Maybe String >> >> lookupSpouse :: Integer -> Maybe String >> lookupSpouse employee_no = do >> employee <- lookupEmployee employee_no >> let passport = lookupPassportNo employee >> cert <- lookupMarriageCertificate >> let (p1, p2) = getPassportNumbers cert >> let partner = if p1 == passport then p2 else p1 >> getNameFromPassport partner >> >> In this example, if any lookup which can fail does, the result is Nothing. >> Each lookup function can assume that a valid argument is present, though some >> types of lookup may still give no result. The caller chooses how to account >> for this inability to find a match, in this case by itself having no result. > > Thanks for the exemple, but I'm aware of monads (even if I can't still > use them easily when > many of them are mixed). > Here my problem is more about interactively editable data structure > (at least from the > point of view of the user). I would be very happy to do it with pure > functions. > >> The thing I'm more concerned about here is the use of IORefs inside data >> structures at all. A data structure containing IORefs is mutable and can only >> be manipulated in the IO monad, which defeats the point of Haskell. There is >> a use case for using mutable structures for some resource-intensive >> operations, but even then it's often trading short-term speed for long term >> difficulties. If you think immutable structures imply poor performance, take >> a look at projects such as uvector and Data Parallel Haskell - immutable data >> structures which beat the hell out traditional, C-like techniques. > > I'm looking at the FGL package, it seems very intersting. I don't > think immutable data > structures imply poor perfromance, but I think designing the examples you give > is quite complicated and done by really experienced Haskell programmers. > Please, don't answer to my problem (even if I haven't really explain > it) by exposing > state-of-the-art Haskell goodness. > >> If you must use IORefs, consider only using them to hold the whole structure, >> which is modified by normal, pure functions. If you don't think you can make >> do with this, you're probably still thinking about the program in an >> imperative manner. You will probably be better off either rethinking how >> you're doing things or, if you cannot translate the concepts to a functional >> form, using an imperative language. > > Overall, I agree I have to look for a more pure approach. Although, I > think something like > FGL required a lot of work. But raising, say, uvector, in face of my > use of IORef seems a bit > quick. > >> Good luck, > > Thank you, > Thu > >> >> Tim >> >> On Wed, 03 Sep 2008 22:09:38 minh thu wrote: >>> Hi, >>> >>> I'd like to write a data structure to be used inside the IO monad. >>> The structure has some handles of type Maybe (IORef a), >>> i.e. IORef are pointers and the Maybe is like null pointers. >>> >>> So I came up with the following functions : >>> >>> readHandle :: Maybe (IORef a) -> IO (Maybe a) >>> readField :: (a -> b) -> Maybe (IORef a) -> IO (Maybe b) >>> >>> readHandle Nothing = do >>> return Nothing >>> readHandle (Just r) = do >>> v <- readIORef r >>> return $ Just v >>> >>> readField f h = do >>> m <- readHandle h >>> return $ fmap f m >>> >>> Is it something usual ? >>> Are there any related functions in the standard libraries ? >>> >>> Thanks, >>> Thu >>> _______________________________________________ >>> Haskell-Cafe mailing list >>> Haskell-Cafe@haskell.org >>> http://www.haskell.org/mailman/listinfo/haskell-cafe >> _______________________________________________ >> Haskell-Cafe mailing list >> Haskell-Cafe@haskell.org >> http://www.haskell.org/mailman/listinfo/haskell-cafe >> > _______________________________________________ > Haskell-Cafe mailing list > Haskell-Cafe@haskell.org > http://www.haskell.org/mailman/listinfo/haskell-cafe > _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe