I was hoping to get some helpful input on understanding Monad Transformers, and in relation to that, what happens using do notation. The example I am trying to understand is the following:
data ProtectedData a = ProtectedData String a
accessData :: String -> ProtectedData a -> Maybe a
accessData s (ProtectedData pass v) =
if s == pass then Just v else Nothing
type Protected s a = MaybeT (Reader (ProtectedData s)) a
-- untangles the monad construction
run :: ProtectedData s -> Protected s a -> Maybe a
run ps psa = runReader (runMaybeT psa) ps
access :: String -> Protected a a
access pass = do
-- ask :: ReaderT (ProtectedData a) Identity (ProtectedData a)
-- lift :: ... -> MaybeT (ReaderT (ProtectedData a) Identity) (ProtectedData a)
pd <- lift ask
-- as i understand it: ask returns the value inside the thing.
-- the left arrow actually applies the monad
let v = accessData pass pd
-- return :: Maybe a -> Reader (ProtectedData a) (Maybe a)
MaybeT $ return v
As I understand it the Protected type describes some protected data, that is stored in a shared environment (Reader) and is of type Maybe (MaybeT).
I am having problems with the type-variables s and a:
- Does
sdescribe the string (password) of the protected data, andathe type of the protected data? - Does
sdescribe the type of the protected data, and if so, what doesadescribe?
In the function run:
run :: ProtectedData s -> Protected s a -> Maybe a
run ps psa = runReader (runMaybeT psa) ps
As I understand it, the Reader from inside Protected is run on the ProtectedData, to return the value.
This leaves only the function access:
access :: String -> Protected a a
access pass = do
pd <- lift ask
let v = accessData pass pd
MaybeT $ return v
Which is the one providing the most headache for me. First I am having issues grasping the effect and result.
- Is this function used to inject passwords and data into the
Reader? - Is it used to access data and fail if the wrong password is given?
Secondly I am having trouble understanding the first line
pd <- lift ask
- I understand, that
askis used to get access to the shared environment through theReader, but why do I have toliftit to aMaybeTto get the actual value inside of it?
1 Answer 1
As i understand it the Protected type describes some "protected" Data
No. Protected s a should be seen as the type of a program that returns a value of type a. During the computation the program has read-only access to a secret value of type s, and only if it "knows" the proper password.
Such secret value, paired with its password, has type ProtectedData s.
does s describe the type of the protected Data, and if so, what does a describe?
Yes. Here a is the, generic, type of the result of the program.
An an example, you can consider the case where the password is a String (it has to be, in your code the string type is hard-coded) and the secret value has type s = Int. Then you write a program which accesses the secret integer, and checks whether it's positive, returning a Bool. Here, a = Bool.
Note that I simplified the scenario a bit. Since we also use MaybeT, we are modelling a program that does not always return a value of type a, but that can also fail. A possible failure could be caused by using the wrong password. In such case, MaybeT roughly aborts the program in the middle of its execution.
The signature
access :: String -> Protected a a
is perhaps better understood if we write it as
access :: String -> Protected s s
showing that it is a helper function to access the secret value (or failing), given a password attempt. It is used as follows:
myProg :: Protected Int Bool
myProg = do
v <- access "123456" -- try accessing the protected int
return (v > 0)
If the password is wrong, the above code will cause a failure (run will return Nothing)
> run (ProtectedData "actual password" 42) myProg
Nothing
If the password is correct, instead it will produce the right boolean:
> run (ProtectedData "123456" 42) myProg
Just True
Here Just means that the password was correct, and True indicates that the protected Int was positive.
2 Comments
Protected s a as a Program with return type a accessing protected Data of type s really helped me understand the concept. If i understand correctly this means i can even use other Monads in my Program: giveEven :: String -> Protected [Int] [Int] giveEven s = do v <- access s return (filter even v)[] as a monad there. You could, though, but it would be "orthogonal", so to speak. s can be any type, and that includes monadic ones, but those do not interfere with the main monad Protected s.Explore related questions
See similar questions with these tags.