[Haskell-cafe] Statically tracking "validity" - suggestions?

Chris Eidhof chris at eidhof.nl
Tue Aug 31 02:45:11 EDT 2010


On 31 aug 2010, at 08:24, strejon wrote:

> 
> Hello. I'm using Haskell to write a specification for some software. The
> software uses certificates (standard X.509 certificates) and stores user
> name information in the Subject's CommonName field.
> 
> The X.509 standard doesn't actually require the presence of a CommonName
> field so the contents of the Subject section (with the rest of the fields
> omitted) are just represented by a Maybe User_Name.
> 
>> import Data.List (find, concat)
>> import Data.Maybe (fromJust, isJust)
>> 
>> type User_Name    = String
>> type Public_Key   = Integer
>> data Subject_Name = Subject_Name (Maybe User_Name) deriving (Show, Eq)
>> 
>> data Certificate = Certificate {
>>  certificate_subject :: Subject_Name,
>>  certificate_key     :: Public_Key,
>>  certificate_issuer  :: String,
>>  certificate_serial  :: Integer
>> } deriving (Show, Eq)
> 
> This is all well and good, but the problem is that the validity of
> certificates isn't tracked statically and I have to use the following
> as guards on all functions that only expect valid certificates (and
> inevitably handle cases that I know can't actually happen but
> have to be handled in pattern matching and the like, bloating
> the specification):
> 
>> user_name :: Subject_Name -> Maybe User_Name
>> user_name (Subject_Name name) = name
>> 
>> is_valid :: Certificate -> Bool
>> is_valid = isJust . user_name . certificate_subject
> 
> I'm aware of phantom types and the like, but I've been unable to
> work out how to use them (or another type system extension)
> to properly track "validity" on the type level. I'd want something
> like:
> 
> validate :: Certificate Possibly_Valid -> Maybe (Certificate Valid)
> 
> With later functions only accepting values of type "Certificate Valid".
> 
> Is there a simple way to do this?

Yes. Introduce a wrapper datatype, ValidCertificate. Create a module and export only the wrapper datatype and a way to construct ValidCertificates in a safe way:

> module ValidateCertificate 
>   ( ValidCertificate,
>     fromValidCertificate,
>     createValidCertificate
>   ) where
> 
> data ValidCertificate = ValidCertificate {fromValidCertificate :: Certificate}
> 
> createValidCertificate :: Certificate -> Maybe ValidCertificate
> createValidCertificate c | is_valid c = Just (ValidCertificate c)
>                          | otherwise  = Nothing
> 
> is_valid :: Certificate -> Bool
> is_valid = isJust . user_name . certificate_subject

The trick is not to export the constructor, but only a verified way to construct values of ValidCertificate.

-chris


More information about the Haskell-Cafe mailing list