我希望你能帮助我。经过多年的命令式语言,我是一名Haskell noob,所以如果我犯了一个愚蠢的错误,请解释一下,这样我才能学习。
我有以下数据类型:
data DicomSopInstance = DicomSopInstance {
sopInstancePath :: String,
sopInstanceUid :: String,
sopInstancePk :: Int64,
seriesFk :: Int64,
sopInstanceFrameCount :: Int32,
sourceDicom :: Maybe EncapDicomObject
}
我从数据库查询的结果中构造此类型的实例。当结果进入sourceDicom字段时,不能有任何值,所以我把它作为一个Maybe值。当我尝试加载EncapDicomObject并使用结果更新数据类型时出现问题,因此我不必每次都要从磁盘加载EncapDicomObject。
以下是导致问题的代码。我的目的是测试是否已从磁盘读取EncapDicomObject,如果已加载,则使用现有(Just)值,否则(未检测到任何内容)然后加载它并将Nothing更改为Just。麻烦的行标有“ * *”
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 ()
如果我注释掉标记的行,那么代码会编译,但每次都会加载文件,因为它总是遇到Nothing。如果我取消注释,我会收到以下错误:
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}
我将此解释为stmt返回DicomSopInstance而不是IO(),但我创建函数以更新sopInst并返回IO()的所有尝试都失败了。
我错过了什么?当Haskell的非严格行为对我这样做或者我的设计错误时,我是否尝试按需加载?我将sourceDicom转换为可变变量的尝试也一无所获:(
欢呼声
詹姆斯
答案 0 :(得分:4)
你不太了解功能范例。 sopInst
定义在函数的顶部。它里面没有可变引用 - 它的值是固定的。您以后无法更改该值。相反,您可以为其他事物指定名称,这是原始名称的更改版本。例如,请尝试以下操作。
Right encapDicom -> do
let newSopInst = sopInst { sourceDicom = Just encapDicom }
showEncapDicomObject textCtl encapDicom (sopInstancePath newSopInst)
return ()
请注意,由于事物是不可变的,因此会有大量的共享。想象一下,你的SopInst
类型是C中的记录。从概念上讲,它有指向其所有成员的指针。构造newSopInst
时,您只需获得该指针记录的副本,其中一个指针现在指向sourceDicom
的新值 - 其他字段指向的值将被共享。这意味着这种编程风格(以更多的间接性为代价 - 无论如何都需要懒惰)的效率远远低于你可能的恐惧,作为奖励,如果你需要的话,你仍然会有旧的sopInst
。在其他地方。 (如果你不这样做,它会收集垃圾)。