packages feed

worldturtle-0.1.0.0: Graphics/WorldTurtle/Internal/Commands.hs

{-# OPTIONS_HADDOCK hide #-}
module Graphics.WorldTurtle.Internal.Commands
  ( SeqC
  , TurtleCommand (..)
  ) where

import Control.Applicative
import Control.Monad

import Graphics.Gloss.Data.Picture (text)

import Graphics.WorldTurtle.Internal.Sequence

type SeqC a = SequenceCommand (AlmostVal ()) a

{-| A `TurtleCommand` represents an instruction to execute. It could be as
    simple as "draw a line" or more complicated like "draw 300 circles."
    
    `TurtleCommand`s can be executed in order by combining them using
    the monadic operator `(>>)`.

    Here is an example of how to write a function that when given a
    @size@ and a @turtle@, will return a new `TurtleCommand` which
    will draw a square with a length and breadth of @size@ using @turtle@.

   @
      drawSquare :: Float -> Turtle -> TurtleCommand ()
      drawSquare size t = replicateM_ 4 $ forward size t >> right 90 t
   @

   This draws a square by doing the following in order:
   
   [@(1/4)@]: 

          * Move forward by @size@ amount. 

          * Turn right by @90@ degrees

     [@(2/4)@]:

          * Move forward by @size@ amount. 

          * Turn right by @90@ degrees

     [@(3/4)@]:

          * Move forward by @size@ amount. 

          * Turn right by @90@ degrees

     [@(4/4)@]:

          * Move forward by @size@ amount. 

          * Turn right by @90@ degrees
-}
newtype TurtleCommand a = TurtleCommand 
  { 
    seqT :: SeqC a
  }

instance Functor TurtleCommand where
  fmap f (TurtleCommand a) = TurtleCommand $ fmap f a

instance Applicative TurtleCommand where
  pure a = TurtleCommand $ pure a
  liftA2 f (TurtleCommand a) (TurtleCommand b) = TurtleCommand $ liftA2 f a b

instance Monad TurtleCommand where
  (TurtleCommand a) >>= f = TurtleCommand $ a >>= \s -> seqT (f s)

instance Alternative TurtleCommand where
  empty = TurtleCommand failSequence
  (<|>) (TurtleCommand a) (TurtleCommand b) = 
    TurtleCommand $ alternateSequence a b

instance Semigroup a => Semigroup (TurtleCommand a) where
  (TurtleCommand a) <> (TurtleCommand b) = 
    TurtleCommand $ combineSequence a b
    
instance MonadPlus TurtleCommand

instance MonadFail TurtleCommand where
  fail t = TurtleCommand $ do
    addPicture $ text t
    failSequence