packages feed

KiCS-debugger-0.1.0: biosphere/src/Curry/Module/TransformationPartCalls.lcurry

Generating Part Call Helpers
============================

This module provides support for part calls, especially the generation of part
call helpers.
These generated functions are able to create the correct `FuncRep` term for
higher order applications. The helper to use depends on the left arity of a
part call.

Usage
-----

For a FlatCurry `(FuncPartCall n)` one can use in Haskell:

 1. missing <= arityThreshold:

    Call `<partCallsModule>.<partCallPrefix><n>` (`PartCalls.partCall<n>`).

    Shortcut for transformation (Curry) is `combDefaultPC <n> <cexprs>`.

 2. missing > arityThreshold:

    Create Yourself a helper using `mkPartCallHelper <yourModName> <n>`
    and call it in your module with `<yourModName>.<partCallPrefix><n>`.

    Shortcut for transformation (Curry) is `combPC <yourModName> <n> <cexprs>`.

> module TransformationPartCalls (
>   arityThreshold,
>   partCallsModule,
>   partCallsAs,
>   partCallsImport,
>   partCallPrefix,
>   main,
>   mkPartCallsModule,
>   mkPartCallHelper,
>   combDefaultPC,
>   combPC) 
>    where

Imports
-------

> import System

> import AbstractCurry
> import AbstractHaskell

> import FlatToAbstractCurry

> import TransformationDebugInfo
> import TransformationPrint

Interface
=========

Default Helpers
---------------

The framework is distributed with a module which contains part call helpers until the following threshold in a Haskell Module with the following name. Further part call helpers can be created by requiring modules.

> arityThreshold  = 10
> partCallPrefix  = "partCall"
> partCallsFile   = "PartCalls"
> partCallsModule = debugPackage ++ partCallsFile
> partCallsAs     = "PC"
> partCallsImport = partCallsModule ++ " as " ++ partCallsAs

Pretty prints and writes the module containing the default helpers.
Takes 1st argument as output folder for the module.

> main :: IO ()
> main = do
>   args <- getArgs
>   let filename = (head args ++ "/" ++ partCallsFile ++ ".hs")
>   putStr   "Creating default part call helpers until `"
>   putStrLn $ partCallPrefix ++ show arityThreshold ++ "` in " ++ filename ++ "."
>   prettyacy filename (mkPartCallsModule arityThreshold)

Creates the module containing a number of default helpers.

> mkPartCallsModule :: Int -> HaskellProg
> mkPartCallsModule num =
>   HaskellProg partCallsModule
>               "" -- no header

  Requires the debug monad for `FuncRep` and debug info for terms:

>               [debugMonadImport,debugInfoImport]
>               "" -- no externals
>               [] -- no types
>               [] -- no instance declarations

  Provide helpers until defined threshold:

>               (map (mkPartCallHelper partCallsAs) [1..num])
>               [] -- no operators

Part Call Helper
----------------

Creates a specific helper for given module name and arity.

> mkPartCallHelper :: String -> Int -> HFuncDecl
> mkPartCallHelper mod arity = HFunc name arity' Public constraints typE rules
>   where
>     name        = (mod,partCallPrefix ++ show arity)
>     arity'      = arity
>     constraints = debugMonadConstraint:termConstraints arity
>     typE        = mkType arity
>     rules       = CRules CFlex [rule]
>     rule        = createRule arity

Comb Shortcut
-------------

Creates the call for a specific default helper (by given missing arity) with term expression and function expression.

> combDefaultPC :: Int -> CExpr -> CExpr -> CExpr
> combDefaultPC arity termExpr funcExpr
>   | arity > arityThreshold = error "part call helper not available"
>   | otherwise              = combPC partCallsAs arity termExpr funcExpr

Creates the call for a specific helper (in given module) and arguments.

> combPC :: String -> Int -> CExpr -> CExpr -> CExpr
> combPC modName arity termExpr funcExpr =
>   comb (modName,partCallPrefix ++ show arity) [termExpr,funcExpr]

Implementation
==============

> termConstraints :: Int -> [TypeClass]
> termConstraints arity = map termConstraint [0..arity]
>   where
>     termConstraint = (constraint staticInfoClass) . convertTypeVariable

> createRule :: Int -> CRule
> createRule arity = noGuardRule [patTerm,patFunc] (mkBody arity) [localDecl]
>   where

  Match left side the given term (with name info and terms of already
  applied arguments) and the wrapped function:
  `partCallArity (Term name info args) f = ...`

>     patTerm   = CPComb staticInfoCons [patName,patInfo,patArgs]
>     patName   = CPVar (0,"name")
>     patInfo   = CPVar (1,"info")
>     patArgs   = CPVar (2,"args")
>     patFunc   = CPVar (3,"f")

  Declare a local function `term` to create a `Term` with name, static info and
  already applied terms of the handled function for given additional argument
  terms:
  `term = Term name info . (args++)`

>     localDecl = CLocalFunc termFunc
>     termFunc  = CFunc ("","term") 0 Public untyped
>                       (rules [constantRule termExpr])
>     termExpr  = point $$ (CSymbol staticInfoCons $$ CVar (0,"name")
>                                                  $$ CVar (1,"info"))
>                       $$ (presym "++" $$ CVar (2,"args"))

> mkBody :: Int -> CExpr
> mkBody arity = mkLambdas arity 1

Handles current argument `var` in a part call with given left arity.
Creates a function representation containing generated terms of the already applied arguments.
`FuncRep (term [t1 ... tVar-1]) (\...)`

> mkLambdas :: Int -> Int -> CExpr
> mkLambdas arity var

  Last argument, create func rep that does the call:
  `\xArity -> f x1 ... xArity`

>   | arity == var = funcRep $ lambda $ mkCall arity

  Further arguments left, open a `do` to create term of the current argument
  and reate func rep with recursive lambda which handles the other arguments:
  `\xVar -> do let tVar = genTerm xVar ...`

>   | otherwise    = funcRep $ lambda $ mkDo arity var

  Function representation containing terms of all applied arguments:

>   where funcRep func = CSymbol funcRepCons $$ allTerms $$ func
>         allTerms = CSymbol ("","term") $$ newTerms
>         newTerms = list (map termVar [1..(var-1)])
>         termVar  = \v -> CVar (arity+v,'t':show v)

  Lambda matching left-side the current argument:

>         lambda   = CLambda [px var]

Constructs the `do` expression which scopes the term for the current var for all sub-lambdas in a `let` and returns the recursive func rep with a part call hook.

> mkDo :: Int -> Int -> CExpr
> mkDo arity var = CDoExpr [stLet,stRet]
>   where
>     stLet    = CSLet [CLocalPat termVar termExpr []]
>     termVar  = CPVar (arity+var,'t':show var)
>     termExpr = (genTermCallVar var)
>     stRet    = hook $ CSExpr $ presym "return" $$ mkLambdas arity (var+1)
>     hook     = id -- TODO: partCall

Constructs the final call to `f` with all applied arguments and wraps it with a func call hook.
`f x1 ... xArity`

> mkCall :: Int -> CExpr
> mkCall arity = hook $ CSymbol ("","f") $$$ map xx [1..arity]
>   where hook = id -- TODO: funcCall

Type signature
--------------

Following code is taken from B.I.O. to create signature for part call helper. (mapped from FlatCurry to AbstractCurry, `TypeExpr` ~> `CTypeExpr` etc.).

> infixr 7 ~>

> (~~>), (~>) :: CTypeExpr -> CTypeExpr -> CTypeExpr
> x ~~> y = CTCons funcRepType [CTVar (-1,debugTVarName),x,y]
> (~>)    = CFuncType

> mkType :: Int -> CTypeExpr
> mkType n = termType ~> 
>            foldr (\ v -> (tx v ~>)) resultType [0..(n-1)] ~>
>            foldr (\ v t -> tx v ~~> t) (tx $ n) [0..(n-1)]
>   where termType   = (CTCons staticInfoCons [])
>         resultType = CTCons debugTVar [tx n]