packages feed

ideas-0.7: src/Service/Diagnose.hs

-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-- Diagnose a term submitted by a student
--
-----------------------------------------------------------------------------
module Service.Diagnose 
   ( Diagnosis(..), diagnose, restartIfNeeded
   , diagnosisType, diagnosisTypeSynonym
   ) where 

import Common.Library
import Common.Utils (safeHead)
import Data.List (sortBy)
import Data.Maybe
import Service.ExercisePackage
import Service.State
import Service.BasicServices
import Service.Types

----------------------------------------------------------------
-- Result types for diagnose service

data Diagnosis a
   = Buggy          (Rule (Context a))
   | NotEquivalent  
   | Similar        Bool (State a)
   | Expected       Bool (State a) (Rule (Context a))
   | Detour         Bool (State a) (Rule (Context a))
   | Correct        Bool (State a)

instance Show (Diagnosis a) where
   show diagnosis = 
      case diagnosis of
         Buggy r        -> "Buggy rule " ++ show (show r)
         NotEquivalent  -> "Unknown mistake" 
         Similar _ _    -> "Very similar"
         Expected _ _ r -> "Rule " ++ show (show r) ++ ", expected by strategy"
         Detour _ _ r   -> "Rule " ++ show (show r) ++ ", not following strategy"
         Correct _ _    -> "Unknown step"

----------------------------------------------------------------
-- The diagnose service

diagnose :: State a -> a -> Diagnosis a
diagnose state new
   -- Is the submitted term equivalent?
   | not (equivalenceContext ex (stateContext state) newc) =
        -- Is the rule used discoverable by trying all known buggy rules?
        case discovered True of
           Just r -> -- report the buggy rule
              Buggy r
           Nothing -> -- unknown mistake
              NotEquivalent
              
   -- Is the submitted term (very) similar to the previous one? 
   | similarity ex (stateTerm state) new =
        -- If yes, report this
        Similar (ready state) state
        
   -- Was the submitted term expected by the strategy?
   | isJust expected =
        -- If yes, return new state and rule
        let (r, _, ns) = fromJust expected  
        in Expected (ready ns) ns r

   -- Is the rule used discoverable by trying all known rules?
   | otherwise =
        let ns = restartIfNeeded (makeState pkg Nothing newc)
        in case discovered False of
              Just r ->  -- If yes, report the found rule as a detour
                 Detour (ready ns) ns r
              Nothing -> -- If not, we give up
                 Correct (ready ns) ns
 where
   pkg  = exercisePkg state
   ex   = exercise pkg
   newc = inContext ex new
   
   expected = do
      xs <- allfirsts (restartIfNeeded state)
      let p (_, _, ns) = similarity ex new (stateTerm ns)
      safeHead (filter p xs)

   discovered searchForBuggy = safeHead
      [ r
      | r <- sortBy (ruleOrdering ex) (ruleset ex)
      , isBuggyRule r == searchForBuggy
      , ruleIsRecognized ex r sub1 sub2
      ]
    where 
      (sub1, sub2) = 
         case difference ex (not searchForBuggy) (stateTerm state) new of 
            Just (a, b) -> (inContext ex a, inContext ex b) 
            Nothing     -> (stateContext state, newc)
 
----------------------------------------------------------------
-- Helpers

-- If possible (and if needed), restart the strategy
-- Make sure that the new state has a prefix
-- When resetting the prefix, also make sure that the context is refreshed
restartIfNeeded :: State a -> State a
restartIfNeeded state 
   | isNothing (statePrefix state) && canBeRestarted (exercise pkg) = 
        emptyState pkg (stateTerm state)
   | otherwise = state
 where
   pkg = exercisePkg state
   
diagnosisType :: Type a (Diagnosis a)
diagnosisType = useSynonym diagnosisTypeSynonym

diagnosisTypeSynonym :: TypeSynonym a (Diagnosis a)
diagnosisTypeSynonym = typeSynonym "Diagnosis" to from tp
 where
   to (Left r) = Buggy r
   to (Right (Left ())) = NotEquivalent
   to (Right (Right (Left (b, s)))) = Similar b s
   to (Right (Right (Right (Left (b, s, r))))) = Expected b s r
   to (Right (Right (Right (Right (Left (b, s, r)))))) = Detour b s r
   to (Right (Right (Right (Right (Right (b, s)))))) = Correct b s
   
   from (Buggy r)        = Left r
   from (NotEquivalent)  = Right (Left ())
   from (Similar b s)    = Right (Right (Left (b, s)))
   from (Expected b s r) = Right (Right (Right (Left (b, s, r))))
   from (Detour b s r)   = Right (Right (Right (Right (Left (b, s, r)))))
   from (Correct b s)    = Right (Right (Right (Right (Right (b, s)))))
   
   tp  =  Rule
      :|: Unit
      :|: Pair   Bool stateTp
      :|: tuple3 Bool stateTp Rule
      :|: tuple3 Bool stateTp Rule
      :|: Pair   Bool stateTp