I have a Transaction monad that looks like:
newtype Transaction t m a = .. my monad stack here ..
t
is a phantom type I use to make sure the transactions I chain up apply to the same backend store.
My main loop's iteration uses a type like:
Transaction DB IO (Widget (Transaction DB IO ()))
The above type means: "A transaction that generates a UI Widget whose user inputs translate to transactions to execute".
Additionally, I have:
data Property m a = Property { get :: m a, set :: a -> m () }
which is extremely useful (especially due to its ability to compose with FCLabels.
I make an awful lot of use of Property (Transaction t m)
to generate my transactions.
Now, I want the type system to guarantee that the Transaction that generates the Widget is a read-only transaction, and the transactions the widget generates are allowed to be read-write transactions.
Apparently, I could just add another phantom type to the transaction, but this has two problems:
It would require explicit conversions from
Transaction ReadOnly
toTransaction ReadWrite
or type-class hackery to allow monadic composition between read and write primitives. This I think I can solve.The Property data constructor, in order to contain the writer, always requires a read/write operation which would force the "m" to be a ReadWrite transaction. I would not be able to re-use a property in the context of both a read-write and a read-only transaction.
I am looking for a good way to get the above-mentioned type-safety without losing the above traits of the design.