Perhaps it would be sensible to approach this from a slightly different angle.
data Entry = Gmail | Facebook | YouTube deriving (Enum,Bounded,Eq)
-- All of the entries as a list.
allEntries :: [Entry]
allEntries = [minBound..maxBound]
-- The compiler will warn us if we omit any entries here!
renderEditEntry :: Entry -> IO ()
renderEditEntry Gmail = [...]
renderEditEntry Facebook = [...]
renderEditEntry YouTube = [...]
-- The compiler will warn us if we omit any entries here!
renderEntry :: Entry -> IO ()
renderEntry Gmail = [...]
renderEntry Facebook = [...]
renderEntry Youtube = [...]
-- Since the activeEntry is passed in as a singular entity, it ensures that only one of them is active.
-- Generating and storing that "active" entry can be done however you like - this function doesn't care.
renderEntries :: Maybe Entry -> IO ()
renderEntries Nothing = forM allEntries renderEntry
renderEntries (Just activeEntry) = forM allEntries
$ \e -> if e == activeEntry then renderEditEntry e else renderEntry e
apologies if this is rendered poorly, I’ve not used Discourse before!
In the above, we firstly separate out the definitions of renderEntry and renderEditEntry. (If they are very similar, it would be possible to further abstract out some common aspects!)
We then write a very short renderEntries function which goes through all elements of the Entry datatype and spits out a text entry or input field for each, depending on whether the activeEntry field matches the value of the “current” one.