2010-11-23 33 views
2

J'espère que vous pouvez m'aider. Je suis un Haskell Noob après des années de langues impératives, donc si je fais une erreur stupide, s'il vous plaît expliquez-le pour que je puisse apprendre.Echec de mise à jour d'enregistrement dans IO Monad?

J'ai le type de données suivantes:

data DicomSopInstance = DicomSopInstance { 
sopInstancePath :: String, 
sopInstanceUid :: String, 
sopInstancePk :: Int64, 
seriesFk :: Int64, 
sopInstanceFrameCount :: Int32, 
sourceDicom :: Maybe EncapDicomObject 
} 

Je construis des instances de ce type à partir des résultats d'une requête de base de données. Lorsque les résultats arrivent dans le champ sourceDicom ne peut avoir aucune valeur, j'en ai fait une valeur Maybe. Le problème survient lorsque j'essaie de charger EncapDicomObject et de mettre à jour le type de données avec le résultat, donc je n'ai pas besoin de charger le EncapDicomObject à partir du disque chaque fois que je veux y accéder.

Ce qui suit est le code à l'origine du problème. Mon intention est de tester si EncapDicomObject a été lu sur le disque, s'il a été chargé, alors utilisez la valeur (Just) existante, sinon (Nothing is detected), puis chargez-le et changez Nothing to Just. La ligne gênante est marquée par « * * »

showImage :: TextCtrl t -> DicomImage -> IO() 
showImage textCtl image = do 
    let sopInst = sopInstance image 
    let maybeEncapDicom = sourceDicom sopInst 
    case maybeEncapDicom of 
    Just encapDicom -> do 
    showEncapDicomObject textCtl encapDicom (sopInstancePath sopInst) 
    return() 
    Nothing   -> do 
    eitherDicom <- readDicomFile $ sopInstancePath sopInst 
    case eitherDicom of 
     Left errorMessage -> do 
     infoM "Hastur" $ "Error reading DICOM file: " ++ 
      (sopInstancePath sopInst) ++ " - " ++ errorMessage 
     textCtrlSetValue textCtl $ "*** DICOM: " ++ 
      (sopInstancePath sopInst) ++ " ***\n" 
     textCtrlAppendText textCtl errorMessage 
     textCtrlAppendText textCtl "\n*** [End] ***" 
     textCtrlShowPosition textCtl 0 
     return() 
     Right encapDicom -> do 
     sopInst { sourceDicom = Just encapDicom } -- **** 
     showEncapDicomObject textCtl encapDicom (sopInstancePath sopInst) 
     return() 

Si je commente la ligne marquée alors le code compile mais il charge le fichier à chaque fois qu'il rencontre depuis toujours rien. Si je décommenter j'obtiens l'erreur suivante:

src\Hastur.hs:382:10: 
    Couldn't match expected type `IO a' 
      against inferred type `DicomSopInstance' 
    In a stmt of a 'do' expression:<br> 
     sopInst {sourceDicom = Just encapDicom} 

J'interprète cela comme signifiant que le stmt retourne un DicomSopInstance au lieu de IO(), mais toutes mes tentatives pour créer une fonction pour mettre à jour le sopInst et retour IO() ont échoué.

Qu'est-ce qui me manque? Est-ce que j'essaie de faire une charge à la demande quand le comportement non-strict d'Haskell ferait cela pour moi ou est-ce que j'ai simplement eu le mauvais design? Mes tentatives pour convertir sourceDicom à une variable mutable sont réduits à néant aussi bien :(

acclamations

James

+0

Dans le futur, indentez votre code de quatre espaces ou utilisez le bouton de code (celui avec les uns et les zéros), afin que votre code soit correctement formaté. – sepp2k

+0

Acklock: AdBlock a caché beaucoup de l'interface de moi.Fixé j'espère –

Répondre

4

Vous n'êtes pas tout à fait comprendre le paradigme fonctionnel. sopInst est défini dans la partie supérieure de votre fonction Il n'a pas de références mutables à l'intérieur - sa valeur est figée dans le marbre, vous ne pouvez pas changer cette valeur plus tard, mais vous pouvez attribuer un nom à une autre chose, qui est une version modifiée de l'original. suivant, par exemple:

Right encapDicom -> do 
    let newSopInst = sopInst { sourceDicom = Just encapDicom } 
    showEncapDicomObject textCtl encapDicom (sopInstancePath newSopInst) 
    return() 

Notez que puisque les choses sont immuables, il y a beaucoup de partage en cours. Imaginez que votre type SopInst est un enregistrement en C. Conceptuellement, il a des pointeurs vers tous ses membres. Lorsque vous construisez le newSopInst alors vous obtenez juste une copie de cet enregistrement de pointeurs, avec un pointeur maintenant pointant vers une nouvelle valeur pour sourceDicom - les valeurs pointées par les autres champs sont partagées. Cela signifie que ce style de programmation (au prix de plus d'indirections - nécessité par la paresse de toute façon) est beaucoup moins inefficace que vous pourriez le craindre, et en prime, vous avez toujours le vieux sopInst traîner si vous en avez besoin ailleurs. (Si vous ne le faites pas, bien sûr, les ordures seront collectées).

+0

Merci pour la réponse.Je pense que vous avez probablement raison dans le sens général, je suis encore en train d'apprendre J'ai compris quand j'ai écrit le code que je remplacerais sourceDicom mais peut-être pas tellement que je remplacerais sopInst.J'étais à l'aise de remplacer une petite structure et des poubelles en récupérant l'ancienne. –

+0

* soupir * toujours le coup Peut-être que ce que j'aurais dû demander était: "J'ai besoin de mettre à jour ces objets lorsque je charge un fichier, comment puis-je faire cela?" Mon Haskell est limité, mais je me suis rendu compte que la mise à jour d'une valeur l'écrase, mais c'était mon intention. Je veux juste comprendre comment garder les changements –

+0

Oh! Je vois! Vous voulez mettre à jour le DicomImage que vous avez passé! Ne fais pas ça! Juste 'return (image {sopInstance = newSopInst})' et donnez à votre fonction un type de 'showImage :: TextCtrl t -> DicomImage -> IO DicomImage'. Vous pouvez toujours tricher et utiliser un état mutable réel avec un MVar/IORef, mais il est préférable de commencer très tôt et d'utiliser uniquement MVars/IORefs quand c'est vraiment nécessaire (en général, soit avec la concurrence, soit avec certains types amusants) de ffi). – sclv