[Haskell-cafe] Statically tracking "validity" - suggestions?
Erik Hesselink
hesselink at gmail.com
Tue Aug 31 05:21:13 EDT 2010
On Tue, Aug 31, 2010 at 08:24, strejon <strejony at yahoo.com> 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?
If you want to use types instead of modules and hiding as Chris
suggested, you can use a type index like this:
{-# LANGUAGE EmptyDataDecls, GADTs, KindSignatures #-}
data Nothing
data Just a
data Subject :: * -> * where
NoName :: Subject Nothing
Name :: String -> Subject (Just String)
data Certificate a = Certificate
{ subject :: Subject a }
validate :: Certificate a -> Maybe (Certificate (Just String))
validate c =
case subject c of
NoName -> Nothing
Name n -> Just c
A "Certificate (Just String)" now always holds a name, and a
"Certificate Nothing" doesn't. A "Certificate a" can hold either.
Erik
More information about the Haskell-Cafe
mailing list