diff --git a/src/Language/CQL/Mapping.hs b/src/Language/CQL/Mapping.hs index bff3c96..5a44146 100644 --- a/src/Language/CQL/Mapping.hs +++ b/src/Language/CQL/Mapping.hs @@ -38,17 +38,19 @@ along with this program. If not, see . {-# LANGUAGE UndecidableInstances #-} module Language.CQL.Mapping where + import Control.DeepSeq -import Data.Map.Strict (Map) -import Data.Map.Strict as Map +import Data.Map.Strict (Map) +import Data.Map.Strict as Map import Data.Maybe -import qualified Data.Set as Set +import qualified Data.Set as Set import Data.Typeable import Data.Void import Language.CQL.Common -import Language.CQL.Schema as Schema +import Language.CQL.Morphism +import Language.CQL.Schema as Schema import Language.CQL.Term -import Prelude hiding (EQ) +import Prelude hiding (EQ) -- | Morphism of schemas. data Mapping var ty sym en fk att en' fk' att' diff --git a/src/Language/CQL/Morphism.hs b/src/Language/CQL/Morphism.hs new file mode 100644 index 0000000..118e238 --- /dev/null +++ b/src/Language/CQL/Morphism.hs @@ -0,0 +1,207 @@ +{- +SPDX-License-Identifier: AGPL-3.0-only + +This file is part of `statebox/cql`, the categorical query language. + +Copyright (C) 2019 Stichting Statebox + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +-} +{-# LANGUAGE AllowAmbiguousTypes #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE ExplicitForAll #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ImpredicativeTypes #-} +{-# LANGUAGE IncoherentInstances #-} +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE LiberalTypeSynonyms #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE UndecidableInstances #-} + +module Language.CQL.Morphism where + +import Control.DeepSeq +import Data.Map.Strict as Map hiding (foldr, size) +import Data.Maybe +import Data.Set as Set hiding (foldr, size) +import Data.Void +import Language.CQL.Common +import Language.CQL.Term (Collage(..), Ctx, Term(..), EQ(..), subst, upp) +import Prelude hiding (EQ) + + +data Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' + = Morphism { + m_src :: Collage (()+var) ty sym en fk att gen sk + , m_dst :: Collage (()+var) ty sym en' fk' att' gen' sk' + , m_ens :: Map en en' + , m_fks :: Map fk (Term () Void Void en' fk' Void Void Void) + , m_atts :: Map att (Term () ty sym en' fk' att' Void Void) + , m_gens :: Map gen (Term Void Void Void en' fk' Void gen' Void) + , m_sks :: Map sk (Term Void ty sym en' fk' att' gen' sk') +} + +-- | Checks totality of the morphism mappings. +checkDoms' + :: (MultiTyMap '[Show, Ord, NFData] '[var, ty, sym, en, fk, att, gen, sk, en', fk', att', gen', sk']) + => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' + -> Err () +checkDoms' mor = do + mapM_ e $ Set.toList $ cens $ m_src mor + mapM_ f $ Map.keys $ cfks $ m_src mor + mapM_ a $ Map.keys $ catts $ m_src mor + mapM_ g $ Map.keys $ cgens $ m_src mor + mapM_ s $ Map.keys $ csks $ m_src mor + where + e en = if Map.member en $ m_ens mor then pure () else Left $ "No entity mapping for " ++ show en + f fk = if Map.member fk $ m_fks mor then pure () else Left $ "No fk mapping for " ++ show fk + a at = if Map.member at $ m_atts mor then pure () else Left $ "No att mapping for " ++ show at + g gn = if Map.member gn $ m_gens mor then pure () else Left $ "No gen mapping for " ++ show gn + s sk = if Map.member sk $ m_sks mor then pure () else Left $ "No sk mapping for " ++ show sk + +-- | Translates a term along a morphism. +trans' + :: forall var var' ty sym en fk att gen sk en' fk' att' gen' sk' + . TyMap Ord '[gen, sk, fk, var, att, var'] + => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' + -> Term var' Void Void en fk Void gen Void + -> Term var' Void Void en' fk' Void gen' Void +trans' _ (Var x) = Var x +trans' mor (Fk f a) = let + x = trans' mor a :: Term var' Void Void en' fk' Void gen' Void + y = upp (m_fks mor ! f) :: Term () Void Void en' fk' Void gen' Void + in subst y x +trans' mor (Gen g) = upp $ m_gens mor ! g +trans' _ (Sym _ _) = undefined +trans' _ (Att _ _) = undefined +trans' _ (Sk _ ) = undefined + +-- | Translates a term along a morphism. +trans + :: forall var var' ty sym en fk att gen sk en' fk' att' gen' sk' + . TyMap Ord '[gen, sk, fk, var, att, var'] + => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' + -> Term var' ty sym en fk att gen sk + -> Term var' ty sym en' fk' att' gen' sk' +trans mor term = case term of + Var x -> Var x + Sym f xs -> Sym f $ Prelude.map (trans mor) xs + Gen g -> upp $ m_gens mor ! g + Sk s -> upp $ m_sks mor ! s + Att f a -> subst (upp $ (m_atts mor) ! f) $ trans mor a + Fk f a -> subst (upp y) x + where + x = trans mor a :: Term var' ty sym en' fk' att' gen' sk' + y = m_fks mor ! f :: Term () Void Void en' fk' Void Void Void + + +typeOfMor + :: forall var ty sym en fk att gen sk en' fk' att' gen' sk' + . (MultiTyMap '[Show, Ord, NFData] '[var, ty, sym, en, fk, att, gen, sk, en', fk', att', gen', sk']) + => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' + -> Err () +typeOfMor mor = do + checkDoms' mor + mapM_ typeOfMorEns $ Map.toList $ m_ens mor + mapM_ typeOfMorFks $ Map.toList $ m_fks mor + mapM_ typeOfMorAtts $ Map.toList $ m_atts mor + mapM_ typeOfMorGens $ Map.toList $ m_gens mor + mapM_ typeOfMorSks $ Map.toList $ m_sks mor + where + transE en = case (Map.lookup en (m_ens mor)) of + Just x -> x + Nothing -> undefined + typeOfMorEns (e,e') | elem e (cens $ m_src mor) && elem e' (cens $ m_dst mor) = pure () + typeOfMorEns (e,e') = Left $ "Bad entity mapping " ++ show e ++ " -> " ++ show e' + typeOfMorFks :: (fk, Term () Void Void en' fk' Void Void Void) -> Err () + typeOfMorFks (fk,e) | Map.member fk (cfks $ m_src mor) + = let (s,t) = fromJust $ Map.lookup fk $ cfks $ m_src mor + (s',t') = (transE s, transE t) + in do t0 <- typeOf' (m_dst mor) (Map.fromList [(Left (), Right s')]) $ upp e + if t0 == Right t' then pure () else Left $ "1Ill typed in " ++ show fk ++ ": " ++ show e + typeOfMorFks (e,e') = Left $ "Bad fk mapping " ++ show e ++ " -> " ++ show e' + typeOfMorAtts (att,e) | Map.member att (catts $ m_src mor) + = let (s,t) = fromJust $ Map.lookup att $ catts $ m_src mor + s' = transE s + in do t0 <- typeOf' (m_dst mor) (Map.fromList [(Left (),Right s')]) $ upp e + if t0 == Left t then pure () else Left $ "2Ill typed attribute, " ++ show att ++ " expression " ++ show e + ++ ", computed type " ++ show t0 ++ " and required type " ++ show t + typeOfMorAtts (e,e') = Left $ "Bad att mapping " ++ show e ++ " -> " ++ show e' + typeOfMorGens (gen,e) | Map.member gen (cgens $ m_src mor) + = let t = fromJust $ Map.lookup gen $ cgens $ m_src mor + t' = transE t + in do t0 <- typeOf' (m_dst mor) (Map.fromList []) $ upp e + if t0 == Right t' then pure () else Left $ "3Ill typed in " ++ show gen ++ ": " ++ show e + typeOfMorGens (e,e') = Left $ "Bad gen mapping " ++ show e ++ " -> " ++ show e' + typeOfMorSks (sk,e) | Map.member sk (csks $ m_src mor) + = let t = fromJust $ Map.lookup sk $ csks $ m_src mor + in do t0 <- typeOf' (m_dst mor) (Map.fromList []) $ upp e + if t0 == Left t then pure () else Left $ "4Ill typed in " ++ show sk ++ ": " ++ show e + typeOfMorSks (e,e') = Left $ "Bad null mapping " ++ show e ++ " -> " ++ show e' + + +-- I've given up on non string based error handling for now +typeOf' + :: (MultiTyMap '[Show, Ord, NFData] '[var, ty, sym, en, fk, att, gen, sk]) + => Collage var ty sym en fk att gen sk + -> Ctx var (ty + en) + -> Term var ty sym en fk att gen sk + -> Err (ty + en) +typeOf' _ ctx (Var v) = note ("Unbound variable: " ++ show v) $ Map.lookup v ctx +typeOf' col _ (Gen g) = case Map.lookup g $ cgens col of + Nothing -> Left $ "Unknown generator: " ++ show g + Just t -> Right $ Right t +typeOf' col _ (Sk s) = case Map.lookup s $ csks col of + Nothing -> Left $ "Unknown labelled null: " ++ show s + Just t -> Right $ Left t +typeOf' col ctx xx@(Fk f a) = case Map.lookup f $ cfks col of + Nothing -> Left $ "Unknown foreign key: " ++ show f + Just (s, t) -> do s' <- typeOf' col ctx a + if (Right s) == s' then pure $ Right t else Left $ "Expected argument to have entity " ++ + show s ++ " but given " ++ show s' ++ " in " ++ (show xx) +typeOf' col ctx xx@(Att f a) = case Map.lookup f $ catts col of + Nothing -> Left $ "Unknown attribute: " ++ show f + Just (s, t) -> do s' <- typeOf' col ctx a + if (Right s) == s' then pure $ Left t else Left $ "Expected argument to have entity " ++ + show s ++ " but given " ++ show s' ++ " in " ++ (show xx) +typeOf' col ctx xx@(Sym f a) = case Map.lookup f $ csyms col of + Nothing -> Left $ "Unknown function symbol: " ++ show f + Just (s, t) -> do s' <- mapM (typeOf' col ctx) a + if length s' == length s + then if (Left <$> s) == s' + then pure $ Left t + else Left $ "Expected arguments to have types " ++ + show s ++ " but given " ++ show s' ++ " in " ++ (show $ xx) + else Left $ "Expected argument to have arity " ++ + show (length s) ++ " but given " ++ show (length s') ++ " in " ++ (show $ xx) + +typeOfEq' + :: (MultiTyMap '[Show, Ord, NFData] '[var, ty, sym, en, fk, att, gen, sk]) + => Collage var ty sym en fk att gen sk + -> (Ctx var (ty + en), EQ var ty sym en fk att gen sk) + -> Err (ty + en) +typeOfEq' col (ctx, EQ (lhs, rhs)) = do + lhs' <- typeOf' col ctx lhs + rhs' <- typeOf' col ctx rhs + if lhs' == rhs' + then Right lhs' + else Left $ "Equation lhs has type " ++ show lhs' ++ " but rhs has type " ++ show rhs' diff --git a/src/Language/CQL/Term.hs b/src/Language/CQL/Term.hs index e59d364..e8f2c90 100644 --- a/src/Language/CQL/Term.hs +++ b/src/Language/CQL/Term.hs @@ -507,118 +507,7 @@ allSortsInhabited col = t && f t = and $ Map.elems me f = and $ Map.elems mt ---------------------------------------------------------------------------------------------- --- Morphisms - -data Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' - = Morphism { - m_src :: Collage (()+var) ty sym en fk att gen sk - , m_dst :: Collage (()+var) ty sym en' fk' att' gen' sk' - , m_ens :: Map en en' - , m_fks :: Map fk (Term () Void Void en' fk' Void Void Void) - , m_atts :: Map att (Term () ty sym en' fk' att' Void Void) - , m_gens :: Map gen (Term Void Void Void en' fk' Void gen' Void) - , m_sks :: Map sk (Term Void ty sym en' fk' att' gen' sk') -} - --- | Checks totality of the morphism mappings. -checkDoms' - :: (MultiTyMap '[Show, Ord, NFData] '[var, ty, sym, en, fk, att, gen, sk, en', fk', att', gen', sk']) - => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' - -> Err () -checkDoms' mor = do - mapM_ e $ Set.toList $ cens $ m_src mor - mapM_ f $ Map.keys $ cfks $ m_src mor - mapM_ a $ Map.keys $ catts $ m_src mor - mapM_ g $ Map.keys $ cgens $ m_src mor - mapM_ s $ Map.keys $ csks $ m_src mor - where - e en = if Map.member en $ m_ens mor then pure () else Left $ "No entity mapping for " ++ show en - f fk = if Map.member fk $ m_fks mor then pure () else Left $ "No fk mapping for " ++ show fk - a at = if Map.member at $ m_atts mor then pure () else Left $ "No att mapping for " ++ show at - g gn = if Map.member gn $ m_gens mor then pure () else Left $ "No gen mapping for " ++ show gn - s sk = if Map.member sk $ m_sks mor then pure () else Left $ "No sk mapping for " ++ show sk - --- | Translates a term along a morphism. -trans' - :: forall var var' ty sym en fk att gen sk en' fk' att' gen' sk' - . TyMap Ord '[gen, sk, fk, var, att, var'] - => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' - -> Term var' Void Void en fk Void gen Void - -> Term var' Void Void en' fk' Void gen' Void -trans' _ (Var x) = Var x -trans' mor (Fk f a) = let - x = trans' mor a :: Term var' Void Void en' fk' Void gen' Void - y = upp (m_fks mor ! f) :: Term () Void Void en' fk' Void gen' Void - in subst y x -trans' mor (Gen g) = upp $ m_gens mor ! g -trans' _ (Sym _ _) = undefined -trans' _ (Att _ _) = undefined -trans' _ (Sk _ ) = undefined - --- | Translates a term along a morphism. -trans - :: forall var var' ty sym en fk att gen sk en' fk' att' gen' sk' - . TyMap Ord '[gen, sk, fk, var, att, var'] - => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' - -> Term var' ty sym en fk att gen sk - -> Term var' ty sym en' fk' att' gen' sk' -trans mor term = case term of - Var x -> Var x - Sym f xs -> Sym f $ Prelude.map (trans mor) xs - Gen g -> upp $ m_gens mor ! g - Sk s -> upp $ m_sks mor ! s - Att f a -> subst (upp $ (m_atts mor) ! f) $ trans mor a - Fk f a -> subst (upp y) x - where - x = trans mor a :: Term var' ty sym en' fk' att' gen' sk' - y = m_fks mor ! f :: Term () Void Void en' fk' Void Void Void - - -typeOfMor - :: forall var ty sym en fk att gen sk en' fk' att' gen' sk' - . (MultiTyMap '[Show, Ord, NFData] '[var, ty, sym, en, fk, att, gen, sk, en', fk', att', gen', sk']) - => Morphism var ty sym en fk att gen sk en' fk' att' gen' sk' - -> Err () -typeOfMor mor = do - checkDoms' mor - mapM_ typeOfMorEns $ Map.toList $ m_ens mor - mapM_ typeOfMorFks $ Map.toList $ m_fks mor - mapM_ typeOfMorAtts $ Map.toList $ m_atts mor - mapM_ typeOfMorGens $ Map.toList $ m_gens mor - mapM_ typeOfMorSks $ Map.toList $ m_sks mor - where - transE en = case (Map.lookup en (m_ens mor)) of - Just x -> x - Nothing -> undefined - typeOfMorEns (e,e') | elem e (cens $ m_src mor) && elem e' (cens $ m_dst mor) = pure () - typeOfMorEns (e,e') = Left $ "Bad entity mapping " ++ show e ++ " -> " ++ show e' - typeOfMorFks :: (fk, Term () Void Void en' fk' Void Void Void) -> Err () - typeOfMorFks (fk,e) | Map.member fk (cfks $ m_src mor) - = let (s,t) = fromJust $ Map.lookup fk $ cfks $ m_src mor - (s',t') = (transE s, transE t) - in do t0 <- typeOf' (m_dst mor) (Map.fromList [(Left (), Right s')]) $ upp e - if t0 == Right t' then pure () else Left $ "1Ill typed in " ++ show fk ++ ": " ++ show e - typeOfMorFks (e,e') = Left $ "Bad fk mapping " ++ show e ++ " -> " ++ show e' - typeOfMorAtts (att,e) | Map.member att (catts $ m_src mor) - = let (s,t) = fromJust $ Map.lookup att $ catts $ m_src mor - s' = transE s - in do t0 <- typeOf' (m_dst mor) (Map.fromList [(Left (),Right s')]) $ upp e - if t0 == Left t then pure () else Left $ "2Ill typed attribute, " ++ show att ++ " expression " ++ show e - ++ ", computed type " ++ show t0 ++ " and required type " ++ show t - typeOfMorAtts (e,e') = Left $ "Bad att mapping " ++ show e ++ " -> " ++ show e' - typeOfMorGens (gen,e) | Map.member gen (cgens $ m_src mor) - = let t = fromJust $ Map.lookup gen $ cgens $ m_src mor - t' = transE t - in do t0 <- typeOf' (m_dst mor) (Map.fromList []) $ upp e - if t0 == Right t' then pure () else Left $ "3Ill typed in " ++ show gen ++ ": " ++ show e - typeOfMorGens (e,e') = Left $ "Bad gen mapping " ++ show e ++ " -> " ++ show e' - typeOfMorSks (sk,e) | Map.member sk (csks $ m_src mor) - = let t = fromJust $ Map.lookup sk $ csks $ m_src mor - in do t0 <- typeOf' (m_dst mor) (Map.fromList []) $ upp e - if t0 == Left t then pure () else Left $ "4Ill typed in " ++ show sk ++ ": " ++ show e - typeOfMorSks (e,e') = Left $ "Bad null mapping " ++ show e ++ " -> " ++ show e' - +-------------------------------------------------------------------------------- -- I've given up on non string based error handling for now typeOf' diff --git a/src/Language/CQL/Transform.hs b/src/Language/CQL/Transform.hs index 93df80f..ac7b343 100644 --- a/src/Language/CQL/Transform.hs +++ b/src/Language/CQL/Transform.hs @@ -49,6 +49,7 @@ import Data.Void import Language.CQL.Common import Language.CQL.Instance as I import Language.CQL.Mapping as M +import Language.CQL.Morphism import Language.CQL.Options import Language.CQL.Query import Language.CQL.Schema as S