packages feed

parsley-core-1.7.1.1: src/ghc/Parsley/Internal/Backend/Machine/THUtils.hs

{-# LANGUAGE CPP #-}
{-|
Module      : Parsley.Internal.Backend.Machine.THUtils
Description : Functions for low-level template haskell manipulation
License     : BSD-3-Clause
Maintainer  : Jamie Willis
Stability   : experimental

This module contains some Template Haskell related functions for manipulating
template haskell as a lower, combinator-based, level.

@since 1.7.0.0
-}
module Parsley.Internal.Backend.Machine.THUtils (eta, unsafeCodeCoerce, unTypeCode) where

import GHC.Types                     (TYPE)
import Control.Arrow                 (first)
import Language.Haskell.TH.Syntax    ( Exp(AppE, LamE, VarE), Pat(VarP, BangP, SigP)
#if __GLASGOW_HASKELL__ < 900
                                     , Q, unTypeQ, unsafeTExpCoerce
#else
                                     , unTypeCode, unsafeCodeCoerce
#endif
                                     )
import Parsley.Internal.Common.Utils (Code)

{-|
Given a function (of arbitrarily many arguments, but it must at /least/ have 1), eta-reduces
it to remove redundant arguments.

@since 1.7.0.0
-}
eta :: forall r1 r2 (a :: TYPE r1) (b :: TYPE r2). Code (a -> b) -> Code (a -> b)
eta = unsafeCodeCoerce . fmap checkEtaMulti . unTypeCode
  where
    --     \       x                  ->      f       x              = f
    checkEta (VarP x)                  (AppE qf (VarE x')) | x == x' = (Nothing, qf)
    --     \       (x ::    t)        ->      f       x              = f
    checkEta (SigP (VarP x) _)         (AppE qf (VarE x')) | x == x' = (Nothing, qf)
    --     \ (!           x)          ->      f       x              = f
    checkEta (BangP (VarP x))          (AppE qf (VarE x')) | x == x' = (Nothing, qf)
    --     \ (!            x ::    t) ->      f       x              = f
    checkEta (BangP (SigP (VarP x) _)) (AppE qf (VarE x')) | x == x' = (Nothing, qf)
    --     \ x -> body                                               = \ x -> body
    checkEta qarg qbody                                              = (Just qarg, qbody)

    checkEtaMulti (LamE args body)  = uncurry LamE $
      foldr (\arg (args, body) -> first (maybe args (: args)) (checkEta arg body))
            ([], body)
            args
    checkEtaMulti qf = qf

#if __GLASGOW_HASKELL__ < 900
unsafeCodeCoerce :: Q Exp -> Code a
unsafeCodeCoerce = unsafeTExpCoerce

unTypeCode :: Code a -> Q Exp
unTypeCode = unTypeQ
#endif