Похоже, ваша проблема в том, что вы не понимаете, что такое тип и значение единицы измерения.
Тип модуля - это специальный встроенный тип, называемый «()», который имеет ровно одно значение, также называемое «()».
Так, например, я могу создать список из 4 единиц, который имеет тип "список единиц".
fourUnits :: [()]
fourUnits = [(), (), (), ()]
Тип единицы измерения используется там, где мы не хотим иметь никакой другой информации. Технически, тип «IO ()» - это тип действия IO, которое дает значение единицы.
Предложение "do" unsugars в цепочке вызовов ">> =". ">> =" имеет тип
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
Другими словами, тип предложения "do" - это тип, возвращаемый его последним действием.
Так, где вы говорите
savePerson p
return ()
«return ()» является ложным, потому что имеет тот же тип, что и «savePerson p».
Помните, что return не имеет ничего общего с потоком управления: это просто функция с типом
return :: (Monad m) => a -> m a
Во избежание этой путаницы было бы лучше назвать «обернуть» или «впрыснуть» или что-то подобное