3

I am trying to write something that is equivalent to a relation in database theory.

-- A Relation has a name and a series of attributes. 
-- Each attribute might have a different type.
data Relation = Relation String [Attribute]

-- An Attribute has a name and a type
data Attribute = Attribute String Type

-- These three look like enough
data Type = String | Integer | Fixed Int

So, far so good.

Now we want to redefine our attribute to have assertions. Properties that must be enforced

data Attribute a = Attribute String Type [Assersion a]

data Assertion a where --Using GADTs
  LessThan :: Num a => a -> Assertion a
  Element :: Eq a => [a] -> Assertion a

Now I need to add the type parameter to the Relation, but that would force all attributes to have the same type parameter on assertions. Thus, I could not have an integer attribute and a string attribute.

How do I solve this problem?

6
  • 2
    Typed embeddings of relation algebra is an open research problem. Commented Jul 17, 2013 at 14:40
  • 1
    Try using an existential type to contain that type parameter inside Attribute. Commented Jul 17, 2013 at 15:14
  • 1
    Something like data Attribute = forall a . Attribute String a [Assertion a]. Note how there's no Type any more, your Attribute can hold any type. You can add a constraint so that only a specific class of types may be put in an Attribute. Actually you have to, because you can access a only through methods of the class you constrain it with. Commented Jul 17, 2013 at 15:30
  • Hm, perhaps I misunderstood. It seems Attribute describes a type of an attribute, not a value. If that's the case just forget about most of my previous comment and put back Type in place of the first a. Commented Jul 17, 2013 at 15:45
  • 2
    @DonStewart , Are there any resources available on this problem? Commented Jul 17, 2013 at 20:48

1 Answer 1

3

n.m. has the right idea, provided a Relation is supposed to be the schema of a relation, not the relation itself. Here's an example, using Data.Typeable as the class of types, and TypeRep as the representation of a type. Additionally, I want all the types to be able to be shown for the example.

-- Use Data.Typeable as our class of types
import Data.Typeable

data Relation = Relation String [Attribute]

data Attribute = forall a. (Typeable a, Show a) => Attribute String (Proxy a) [Assertion a]
    -- Requires RankNTypes

data Proxy a = Proxy

data Assertion a where
    LessThan :: Ord a => a -> Assertion a
    Element :: Eq a => [a] -> Assertion a
    -- Requires GADTs

-- Relation
deriving instance Show Relation

attributes (Relation _ x) = x

-- Attribute
deriving instance Show Attribute
    -- Requires StandaloneDeriving

name (Attribute x _ _) = x

validator :: Attribute -> forall a. (Typeable a) => a -> Maybe Bool
validator (Attribute _ proxy assertions) value =
    (cast value) >>= \x -> Just $ all ((flip implementation) x) assertions

dataType :: Attribute -> TypeRep
dataType (Attribute _ proxy _) = getType proxy

-- Proxy
instance (Typeable a) => Show (Proxy a) where
    show = show . getType

getType :: (Typeable a) => Proxy a -> TypeRep
getType (_ :: Proxy a) = typeOf (undefined :: a)
    -- Requires ScopedTypeVariables

-- Assertion
deriving instance (Show a) => Show (Assertion a)

implementation :: Assertion a -> a -> Bool
implementation (LessThan y) x = x < y
implementation (Element y) x = any ((==) x) y

The example checks to see if a couple values would be allowed for the various attributes of a relation

-- Example
explain :: (Typeable a, Show a) => a -> Attribute -> String
explain value attribute = 
    case validator attribute value of
        Nothing -> (show value) ++ " can't be a " ++ (name attribute) ++ " because it isn't a " ++ (show (dataType attribute))
        Just False -> (show value) ++ " can't be a " ++ (name attribute) ++ " because it fails an assertion"
        Just True -> (show value) ++ " can be a " ++ (name attribute)

main =
    do
    putStrLn $ show people
    sequence [ putStrLn (explain value attribute) | value <- [150 :: Int, 700], attribute <- attributes people ]
    sequence [ putStrLn (explain value attribute) | value <- ["Alice", "George"], attribute <- attributes people ]
    where
         people = Relation "People"
            [
                Attribute "Name" (Proxy :: Proxy String) [Element ["Alice", "Bob", "Eve"] ],
                Attribute "Height" (Proxy :: Proxy Int) [LessThan 300] ]

Here's the output of the program:

Relation "People" [Attribute "Name" [Char] [Element ["Alice","Bob","Eve"]],Attribute "Height" Int [LessThan 300]]

150 can't be a Name because it isn't a [Char]

150 can be a Height

700 can't be a Name because it isn't a [Char]

700 can't be a Height because it fails an assertion

"Alice" can be a Name

"Alice" can't be a Height because it isn't a Int

"George" can't be a Name because it fails an assertion

"George" can't be a Height because it isn't a Int

You could make your own class, using your own representation of a smaller set of types, like your data Type, and providing a different method of casting.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.