[Haskell-cafe] Synthetic values?

Steffen Schuldenzucker sschuldenzucker at uni-bonn.de
Wed Feb 9 18:43:42 CET 2011


In ghci I get

 >>> let evil = appendLog "Foo" "Bar"
<interactive>:1:11:
     Ambiguous type variable `p' in the constraints:
       `PRead p'
         arising from a use of `appendLog' at <interactive>:1:11-31
       `PWrite p'
         arising from a use of `appendLog' at <interactive>:1:11-31
     Probable fix: add a type signature that fixes these type variable(s)

And then, specializing evil's type:

 >>> let good = appendLog "Foo" "Bar" :: Sealed Admin String
 >>> unseal (undefined :: Admin) good
"FooBar"

-- Steffen

On 02/09/2011 06:15 PM, Cristiano Paris wrote:
> Hi all,
>
> I've a type problem that I cannot solve and, before I keep banging my
> head against an unbreakable wall, I'd like to discuss it with the
> list.
>
> Consider the following code:
>
> ------------
> module Main where
>
> class PRead p where {}
> class PWrite p where {}
>
> newtype Sealed p a = Sealed a
>
> instance Monad (Sealed p) where
> 	return = Sealed
> 	(Sealed x)>>= f = f x
>
> mustRead :: PRead p =>  a ->  Sealed p a
> mustRead = Sealed
>
> mustWrite :: PWrite p =>  a ->  Sealed p a
> mustWrite = Sealed
>
> readLog :: PRead p =>  String ->  Sealed p String
> readLog = mustRead . id
>
> writeLog :: PWrite p =>  String ->  Sealed p String
> writeLog = mustWrite . id
>
> appendLog l e = do l<- readLog l
>                     writeLog $ l ++ e
> ------------
>
> The central type of this code is Sealed, which boxes a value inside a
> newtype with a phantom type which represents a "set of permissions".
>
> This set of permissions is implemented through a series of type
> classes (PRead and PWrite in this case) which are attached to the
> permission value p of the Sealed newtype.
>
> This way I can define which set of permissions I expect to be enforced
> when trying to peel off the Sealed value. The use of the Monad class
> and type classes as permissions behaves nicely when combining
> functions with different permission constraints, as it's the case of
> appendLog, whose type signature is:
>
> appendLog  :: (PRead p, PWrite p) =>  String ->  [Char] ->  Sealed p String
>
> Very nice, the permissions accumulates as constraints over the p type.
> Now for the "peel-off" part:
>
> ------------
> unseal :: p ->  Sealed p a ->  a
> unseal _ (Sealed x) = x
> ------------
>
> Basically this function requires a witness value of the type p to
> peel-off the Sealed value. Notice that:
>
> ------------
> unseal undefined $ appendLog "Foo" "Bar"
> ------------
>
> won't work as the undefined value is unconstrained. That's good,
> because otherwise it'd very easy to circumvent the enforcing
> mechanism. So, I defined some "roles":
>
> ------------
> data User = User
> data Admin = Admin
>
> instance PRead User where {}
>
> instance PRead Admin where {}
> instance PWrite Admin where {}
> ------------
>
> If I try to unseal the Sealed value passing User, it won't succeed, as
> the type checker is expecting the value of a type which is also an
> instance of the PWrite class:
>
> ------------
> *Main>  unseal User $ appendLog "Foo" "Bar"
>
> <interactive>:1:14:
>      No instance for (PWrite User)
>        arising from a use of `appendLog' at<interactive>:1:14-34
> ------------
>
> while works perfectly if I pass Admin as a value:
>
> ------------
> *Main>  unseal Admin $ appendLog "Foo" "Bar"
> "FooBar"
> ------------
>
> The idea is to hide the Admin and User constructor from the programmer
> and having two factory functions, checkAdmin and checkUser, which
> checks whether the current user has the named role, something like:
>
> ------------
> checkAdmin :: IO Admin
> checkUser :: IO User
> ------------
>
> where role checking happens in the IO Monad (or something similar),
> a-là trusted kernel. So far so good and I'm very happy with that.
>
> Now the problem.
>
> I would like to enforce permissions not at the role level, but at the
> permissions level. Let's say that I want to leave "unseal" unchanged,
> I'd like to "construct" a p-value for unseal "combining" functions
> checking for single permissions, that is, in pseudo-code:
>
> unseal (checkPRead .*. checkPWrite) $ appendLog "Foo" "Bar"
>
> where .*. is some kind of "type aggregation" operator.
>
> Or maybe something like:
>
> (checkPRead .*. checkPWrite) $ appendLog "Foo" "Bar"
>
> So far I got only frustration. In principle it seems possible to
> achieve this result because everything is known at compile time and
> the type-checked should have all the information available to enforce
> the security constraints.
>
> Anyhow, I couldn't write any usable code.
>
> Any help would be appreciated, even pointers to papers discussing this approach.
>
> Thank you,
>




More information about the Haskell-Cafe mailing list