Superschnitzel
Superschnitzel

Reputation: 31

Difficulties understanding Monad Transformers

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:

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.

Secondly I am having trouble understanding the first line

pd <- lift ask

Upvotes: 3

Views: 173

Answers (1)

chi
chi

Reputation: 116139

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.

Upvotes: 6

Related Questions