packages feed

syntactic-0.1: Examples/MuFeldspar/Core.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}

module MuFeldspar.Core where



import Prelude hiding (max, min)
import qualified Prelude

import Data.Typeable

import Language.Syntactic.Features.Binding
import Language.Syntactic.Features.Binding.HigherOrder



-- | Convenient class alias
class    (Eq a, Show a, Typeable a) => Type a
instance (Eq a, Show a, Typeable a) => Type a

type Length = Int
type Index  = Int



--------------------------------------------------------------------------------
-- * Parallel arrays
--------------------------------------------------------------------------------

data Parallel a
  where
    Parallel :: Parallel (Length :-> (Index -> a) :-> Full [a])

instance Render Parallel
  where
    render Parallel = "parallel"

instance ToTree Parallel

instance ExprEq Parallel
  where
    Parallel `exprEq` Parallel = True

instance Eval Parallel
  where
    evaluate Parallel = consEval $ \len ixf -> Prelude.map ixf [0 .. len-1]



--------------------------------------------------------------------------------
-- * For loops
--------------------------------------------------------------------------------

data ForLoop a
  where
    ForLoop :: ForLoop (Length :-> st :-> (Index -> st -> st) :-> Full st)

instance ExprEq ForLoop
  where
    ForLoop `exprEq` ForLoop = True

instance Render ForLoop
  where
    render ForLoop = "forLoop"

instance ToTree ForLoop

instance Eval ForLoop
  where
    evaluate ForLoop = consEval $ \len init body -> foldr body init [0 .. len-1]



--------------------------------------------------------------------------------
-- * Feldspar domain
--------------------------------------------------------------------------------

type FeldDomain
    =   Literal
    :+: PrimFunc
    :+: Condition
    :+: Tuple
    :+: Select
    :+: Let
    :+: Parallel
    :+: ForLoop

type Data a = HOAST FeldDomain (Full a)

-- | Specialization of the 'Syntactic' class for the Feldspar domain
class
    ( Syntactic a (HOLambda FeldDomain :+: Variable :+: FeldDomain)
    , Type (Internal a)
    ) =>
      Syntax a

instance
    ( Syntactic a (HOLambda FeldDomain :+: Variable :+: FeldDomain)
    , Type (Internal a)
    ) =>
      Syntax a



--------------------------------------------------------------------------------
-- * Core library
--------------------------------------------------------------------------------

reifyFeld :: Syntax a =>
    a -> AST (Lambda :+: Variable :+: FeldDomain) (Full (Internal a))
reifyFeld = reify . desugar

eval :: Syntax a => a -> Internal a
eval = evalLambda . reifyFeld

-- | For types containing some kind of \"thunk\", this function can be used to
-- force computation
force :: Syntax a => a -> a
force = resugar

-- TODO Hacks to make the 'Num' instance work:
instance ExprEq (HOLambda a) where exprEq = undefined
instance Render (HOLambda a) where render = undefined
instance ExprEq Variable     where exprEq = undefined

instance (Type a, Num a) => Num (Data a)
  where
    fromInteger = lit . fromInteger
    abs         = primFunc "abs" abs
    signum      = primFunc "signum" signum
    (+)         = primFunc2 "(+)" (+)
    (-)         = primFunc2 "(-)" (-)
    (*)         = primFunc2 "(*)" (*)

parallel :: Type a => Data Length -> (Data Index -> Data a) -> Data [a]
parallel len ixf = inject Parallel :$: len :$: lambda ixf

forLoopCore :: Type st =>
     Data Length -> Data st -> (Data Index -> Data st -> Data st) -> Data st
forLoopCore len init body = inject ForLoop :$: len :$: init :$: lambdaN body

forLoop :: Syntax st => Data Length -> st -> (Data Index -> st -> st) -> st
forLoop len init body = sugar $ forLoopCore len (desugar init) body'
  where
    body' i = desugar . body i . sugar

arrLength :: Type a => Data [a] -> Data Length
arrLength = primFunc "arrLength" Prelude.length

getIx :: Type a => Data [a] -> Data Index -> Data a
getIx arr ix = primFunc2 "getIx" eval arr ix
  where
    eval as i
        | i >= len || i < 0 = error "getIx: index out of bounds"
        | otherwise         = as !! i
      where
        len = Prelude.length as

max :: (Type a, Ord a) => Data a -> Data a -> Data a
max = primFunc2 "max" Prelude.max

min :: (Type a, Ord a) => Data a -> Data a -> Data a
min = primFunc2 "min" Prelude.min