{-# OPTIONS_GHC -fglasgow-exts -funbox-strict-fields #-}

module MO.Compile where

import MO.Base
import MO.Util

-- A Method must have name and an implementation encapsulated in a
-- MethodCompile type. This abstraction allows having different
-- types of methods, but for now we have only SimpleMethod, which is
-- the minimal sane instance of Method.

type MethodName = Atom

class Monad m => Method m a | a -> m where
    methodName      :: a -> MethodName
    methodCompile   :: a -> MethodCompiled m

instance Monad m => Method m (AnyMethod m) where
    methodName (MkMethod x)    = methodName x
    methodCompile (MkMethod x) = methodCompile x

data SimpleMethod m
    = MkSimpleMethod
        { sm_name        :: MethodName
        , sm_definition  :: MethodCompiled m
        }

instance Monad m => Method m (SimpleMethod m) where
    methodName = sm_name
    methodCompile = sm_definition


-- AnyMethod may contain any type of the Method class. This makes
-- other functions life easier. And is a common pattern in MO code.

data AnyMethod m = forall a. Method m a => MkMethod !a

-- FIXME: Its not ok to use this since we can define method with
-- same name which are different. 
instance Eq (AnyMethod m) where
    MkMethod a == MkMethod b = methodName a == methodName b

instance Ord (AnyMethod m) where
    MkMethod a `compare` MkMethod b = methodName a `compare` (methodName b)

instance Show (AnyMethod m) where
    show (MkMethod m) = show (methodName m)


-- MethodCompiled represent a method that maybe called (via runMC) with
-- an Arguments and return an Invocant. It may in the future use the
-- Codeable abstraction (see Base.hs) but for now is "just" a Haskell method.

newtype MethodCompiled m = MkMethodCompiled { runMC :: Arguments m -> m (Invocant m) }