-- | Defines classes for use with Drasil's expression language.
module Language.Drasil.ExprClasses (Express(..)) where

import qualified Data.List.NonEmpty as NE

import Language.Drasil.Expr.Lang (Expr)
import Language.Drasil.ModelExpr.Lang (ModelExpr(Lit))
import Language.Drasil.ModelExpr.Convert (expr)
import Language.Drasil.Literal.Lang (Literal)
import Language.Drasil.Expr.Class (($&&))

-- | Express something axiomatically.
class Express c where
  -- | Express something as a single fact.
  express :: c -> ModelExpr
  -- | Express something as a series of facts.
  mexpress :: c -> NE.NonEmpty ModelExpr

  express = (ModelExpr -> ModelExpr -> ModelExpr)
-> NonEmpty ModelExpr -> ModelExpr
forall a. (a -> a -> a) -> NonEmpty a -> a
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 ModelExpr -> ModelExpr -> ModelExpr
forall r. ExprC r => r -> r -> r
($&&) (NonEmpty ModelExpr -> ModelExpr)
-> (c -> NonEmpty ModelExpr) -> c -> ModelExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. c -> NonEmpty ModelExpr
forall c. Express c => c -> NonEmpty ModelExpr
mexpress
  mexpress = ModelExpr -> NonEmpty ModelExpr
forall a. a -> NonEmpty a
NE.singleton (ModelExpr -> NonEmpty ModelExpr)
-> (c -> ModelExpr) -> c -> NonEmpty ModelExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. c -> ModelExpr
forall c. Express c => c -> ModelExpr
express
  {-# MINIMAL express | mexpress #-}

instance Express Literal where
  express :: Literal -> ModelExpr
express = Literal -> ModelExpr
Lit

-- | Rewriting 'Expr's using the 'ModelExpr' language.
instance Express Expr where
  express :: Expr -> ModelExpr
express = Expr -> ModelExpr
expr

-- | No change, it's already a 'ModelExpr'.
instance Express ModelExpr where
  express :: ModelExpr -> ModelExpr
express = ModelExpr -> ModelExpr
forall a. a -> a
id

instance Express t => Express [t] where
  mexpress :: [t] -> NonEmpty ModelExpr
mexpress = (t -> ModelExpr
forall c. Express c => c -> ModelExpr
express (t -> ModelExpr) -> NonEmpty t -> NonEmpty ModelExpr
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>) (NonEmpty t -> NonEmpty ModelExpr)
-> ([t] -> NonEmpty t) -> [t] -> NonEmpty ModelExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [t] -> NonEmpty t
forall a. HasCallStack => [a] -> NonEmpty a
NE.fromList

instance Express t => Express (NE.NonEmpty t) where
  mexpress :: NonEmpty t -> NonEmpty ModelExpr
mexpress = (t -> ModelExpr
forall c. Express c => c -> ModelExpr
express (t -> ModelExpr) -> NonEmpty t -> NonEmpty ModelExpr
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>)