packages feed

ideas-1.9.1: src/Ideas/Common/Constraint.hs

-----------------------------------------------------------------------------
-- Copyright 2019, Ideas project team. This file is distributed under the
-- terms of the Apache License 2.0. For more information, see the files
-- "LICENSE.txt" and "NOTICE.txt", which are included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-- Constraints for constraint-based tutors
--
-----------------------------------------------------------------------------

module Ideas.Common.Constraint
  ( Constraint, makeConstraint
  , isRelevant, isSatisfied, isViolated, getResult
  , Result(..), violation, relevance
  , subConstraints
  ) where

import Data.List
import Ideas.Common.Id
import Ideas.Common.View

---------------------------------------------------------------------------
-- Constraint

data Constraint a = C
  { constraintId :: Id
  , getResult    :: a -> Result ()
  }

instance Show (Constraint a) where
  show = showId

instance Eq (Constraint a) where
  r1 == r2 = constraintId r1 == constraintId r2

instance Ord (Constraint a) where
  compare = compareId

instance HasId (Constraint a) where
  getId        = constraintId
  changeId f r = r { constraintId = f (constraintId r) }

instance Lift Constraint where
   liftWithM f (C n p) = C n (maybe Irrelevant (p . fst) . f)

makeConstraint :: IsId n => n -> (a -> Result ()) -> Constraint a
makeConstraint = C . newId

--  | Relevance condition
isRelevant :: Constraint a -> a -> Bool
isRelevant p a =
   case getResult p a of
      Irrelevant -> False
      _          -> True

-- | Satisfaction condition
isSatisfied :: Constraint a -> a -> Bool
isSatisfied p a =
   case getResult p a of
      Ok _ -> True
      _    -> False

-- | Satisfaction condition
isViolated :: Constraint a -> a -> Maybe String
isViolated p a =
   case getResult p a of
      Violation s -> Just s
      _           -> Nothing

---------------------------------------------------------------------------
-- Result

data Result a = Irrelevant | Violation String | Ok a
   deriving Show

instance Functor Result where
   fmap _ Irrelevant      = Irrelevant
   fmap _ (Violation msg) = Violation msg
   fmap f (Ok a)          = Ok (f a)

instance Applicative Result where
   pure = Ok
   Irrelevant     <*> _             = Irrelevant
   Violation msg  <*> _             = Violation msg
   Ok _           <*> Irrelevant    = Irrelevant
   Ok _           <*> Violation msg = Violation msg
   Ok f           <*> Ok a          = Ok (f a)

instance Monad Result where
   Irrelevant    >>= _ = Irrelevant
   Violation msg >>= _ = Violation msg
   Ok a          >>= f = f a

violation :: String -> Result a
violation = Violation

-- | Turn errors into irrelevant results
relevance :: Result a -> Result a
relevance (Violation _) = Irrelevant
relevance r = r

-- to do:
-- * alle errors teruggeven
-- * locatie van error bijhouden
subConstraints :: IsId n => (b -> [(String, a)]) -> n -> Constraint a -> Constraint b
subConstraints f n c = makeConstraint n $ \p -> do
   let results = [ (loc, getResult c a) | (loc, a) <- f p ]
   case filter isError results of
      [] | any isOk results -> Ok ()
         | otherwise        -> Irrelevant
      errs -> violation $ intercalate "," [ showId c ++ "." ++ loc ++ ":" ++ msg | (loc, Violation msg) <- errs ]
 where
   isError (_, Violation _) = True
   isError _ = False

   isOk (_, Ok _) = True
   isOk _ = False