Last active
April 13, 2025 17:51
-
-
Save mroman42/3ac16ffd1c5f2f3a1056aaa5a48bcdf5 to your computer and use it in GitHub Desktop.
Taking clinical trial description seriously.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} | |
{-# HLINT ignore "Use newtype instead of data" #-} | |
{-# HLINT ignore "Use if" #-} | |
module ProtocolsRunOnTrials where | |
import Control.Monad.Free | |
import Control.Monad | |
import Control.Monad.Loops | |
import Data.List (isInfixOf) | |
import Data.Maybe (maybeToList) | |
-- Forms as a free monad. | |
type Form a = Free Question a | |
data Question y | |
= Consent { consent :: Maybe Bool -> y } | |
| DrugsTaken { nothing :: y , drug :: [Drug] -> y } | |
| AdverseEffectClass { aeMinor :: y, aeMajor :: y, aeNo :: y } | |
| AdverseEffectName { aeName :: AdverseEffect -> y } | |
| AdverseEffectNarrative { aeNarrative :: String -> y } | |
deriving (Functor) | |
data Drug = Loratadine | Montelukast | Cetirizine deriving Show | |
data AdverseEffectClass = AEMinor | AEMajor | AENone deriving Show | |
data AdverseEffect = Headache | Depression | Unclassified deriving Show | |
drugsForm :: Form [Drug] | |
drugsForm = ask $ DrugsTaken | |
{ drug = \d -> do | |
rest <- drugsForm | |
return (d ++ rest) | |
, nothing = return [] } | |
inclusionForm :: Form (Bool, [Drug]) | |
inclusionForm = do | |
c <- askConsent | |
case c of | |
(Just True) -> do { d <- drugsForm ; return (True, d) } | |
(Just False) -> do { return (False,[]) } | |
Nothing -> do { return (False,[]) } | |
askAdverseEffectClass :: Form AdverseEffectClass | |
askAdverseEffectClass = liftF $ AdverseEffectClass AEMinor AEMajor AENone | |
askAdverseEffectName :: Form AdverseEffect | |
askAdverseEffectName = liftF $ AdverseEffectName id | |
askAdverseEffectNarrative :: Form String | |
askAdverseEffectNarrative = liftF $ AdverseEffectNarrative id | |
type Narrative = String | |
adverseEffectForm :: Form (AdverseEffectClass, Maybe (AdverseEffect, Narrative)) | |
adverseEffectForm = do | |
aeclass <- askAdverseEffectClass | |
case aeclass of | |
AEMajor -> pure (AEMajor, Nothing) | |
AEMinor -> do | |
aename <- askAdverseEffectName | |
aenarrative <- askAdverseEffectNarrative | |
return (aeclass, Just (aename, aenarrative)) | |
AENone -> pure (AENone, Nothing) | |
-- Clinical trial as a free monad | |
data Clinical y = InvalidBool (Bool -> y) | InvalidDrug ([Drug] -> y) | InvalidAdverseEffect (AdverseEffect -> y) | Concern String y | PutString String y | GetString (String -> y) deriving (Functor) | |
type ClinicalTrial a = Free Clinical a | |
-- Handling errors from the form during design of the clinical trial. | |
questionYesNo :: String -> ClinicalTrial (Maybe Bool) | |
questionYesNo questionString = do | |
putString questionString | |
s <- getString | |
case s of | |
"Yes" -> return (Just True) | |
"No" -> return (Just False) | |
_ -> return Nothing | |
runsForm :: Form a -> ClinicalTrial a | |
runsForm (Pure a) = pure a | |
runsForm (Free (Consent cmaybebool)) = do | |
b <- questionYesNo "Do you consent to participate in this study?" | |
runsForm (cmaybebool b) | |
runsForm (Free (DrugsTaken dtno dtls)) = do | |
putString "Are you taking any other drugs? If so, give the name." | |
drug <- getString | |
case drug of | |
"No" -> runsForm dtno | |
"Loratadine" -> runsForm (dtls [Loratadine]) | |
"Montelukast" -> runsForm (dtls [Montelukast]) | |
"Cetirizine" -> runsForm (dtls [Cetirizine]) | |
_ -> do | |
d <- invalidDrug | |
runsForm (dtls d) | |
runsForm (Free (AdverseEffectClass aeminor aemajor aeno)) = do | |
b <- questionYesNo "Are you reporting an adverse event?" | |
case b of | |
Just True -> do | |
b <- iterateWhile (== Nothing) $ questionYesNo "Are you reporting a MAJOR/unexpected adverse event?" | |
case b of | |
Just True -> runsForm aemajor | |
Just False -> runsForm aeminor | |
Nothing -> do | |
concern "Possible adverse event was not reported. Question was not answered." | |
runsForm aeno | |
Just False -> runsForm aeno | |
Nothing -> runsForm aeno | |
runsForm (Free (AdverseEffectName aename)) = do | |
putString "What is the classification of the adverse event?" | |
classification <- getString | |
case classification of | |
"Headache" -> runsForm (aename Headache) | |
"Depression" -> runsForm (aename Depression) | |
_ -> runsForm (aename Unclassified) | |
runsForm (Free (AdverseEffectNarrative aenarrative)) = do | |
putString "Please describe the adverse event." | |
narrative <- getString | |
runsForm (aenarrative narrative) | |
exampleClinicalTrial :: ClinicalTrial (Bool, [Drug], [(AdverseEffect, String)]) | |
exampleClinicalTrial = do | |
(consent, concomitantMedication) <- runsForm inclusionForm | |
if consent then do | |
(aeclass, maybeAdverseEffectAndNarrative) <- runsForm adverseEffectForm | |
aeList <- maybeToList <$> clinicalAdverseEventCollection | |
return (consent, concomitantMedication, aeList) | |
else | |
return (False, [], []) | |
clinicalAdverseEventCollection :: ClinicalTrial (Maybe (AdverseEffect, String)) | |
clinicalAdverseEventCollection = do | |
(aeclass, maybeAdverseEffectAndNarrative) <- runsForm adverseEffectForm | |
case (aeclass, maybeAdverseEffectAndNarrative) of | |
(AEMajor, _) -> do | |
concern "Major adverse effect has been raised" | |
return $ Just (Unclassified, "") | |
(AEMinor, Just (Unclassified, aenarrative)) -> | |
if "Headache" `isInfixOf` aenarrative || "headache" `isInfixOf` aenarrative | |
then return $ Just (Headache, aenarrative) | |
else return $ Just (Unclassified, aenarrative) | |
(AEMinor, Just (aename, aenarrative)) -> do | |
return $ Just (aename, aenarrative) | |
(AEMinor, Nothing) -> do | |
return $ Just (Unclassified, "No description provided.") | |
(AENone, _) -> return Nothing | |
-- Handling errors from the form on execution. | |
interpretTrial :: ClinicalTrial a -> IO a | |
interpretTrial (Free (PutString s y)) = do | |
putStrLn s | |
interpretTrial y | |
interpretTrial (Free (GetString f)) = do | |
s <- getLine | |
interpretTrial (f s) | |
interpretTrial (Free (InvalidBool g)) = do | |
raise "missing boolean, set to False." | |
interpretTrial (g True) | |
interpretTrial (Free (InvalidDrug d)) = do | |
raise "missing drug, not registered in the list. Please ask the participant again." | |
interpretTrial (d []) | |
interpretTrial (Free (InvalidAdverseEffect a)) = do | |
raise "missing adverse effect. Recorded as unclassified. Please ask nurses for report." | |
interpretTrial (a Unclassified) | |
interpretTrial (Free (Concern s y)) = do | |
putStrLn $ "Clinical concern raised: " ++ s | |
interpretTrial y | |
interpretTrial (Pure a) = pure a | |
--Boilerplate | |
putString :: String -> ClinicalTrial () | |
putString xs = liftF $ PutString xs () | |
getString :: ClinicalTrial String | |
getString = liftF $ GetString id | |
invalidBool :: ClinicalTrial Bool | |
invalidBool = liftF $ InvalidBool id | |
invalidAdverseEffect :: ClinicalTrial AdverseEffect | |
invalidAdverseEffect = liftF $ InvalidAdverseEffect id | |
invalidDrug :: ClinicalTrial [Drug] | |
invalidDrug = liftF $ InvalidDrug id | |
askConsent :: Form (Maybe Bool) | |
askConsent = liftF $ Consent id | |
concern :: String -> ClinicalTrial () | |
concern s = liftF $ Concern s () | |
ask :: f (Free f y) -> Free f y | |
ask = Free | |
end :: Free f () | |
end = Pure () | |
raise :: String -> IO () | |
raise s = putStrLn $ "#WARNING RAISED: " ++ s | |
------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment