450 likes | 527 Views
pHaskell focuses on developing Active Libraries, combining C++ templates and simplicity of functional languages. It generates C++ generative code with a pseudo Haskell-like syntax and introduces a new feature called "gerenatrix." Learn more about pHaskell and its translation to C++ at http://code.google.com/p/phaskell/wiki/index.
E N D
A new approach to develop Active Libraries for C++. pHaskellOverview
Introduction A library which takes an active role in generating code are called Active Library. An active library is based on Generative programming and has the ability of mutating during compilation time to offer a good performance increase result to the end users. Some examples of actives libraries are: Blitz++, POOMA, Matrix template library (MTL), and others. C++ brings us the possibility to do generative programming using template meta-programming and most of the examples shown above use Template Meta Programming techniques to reach this objective.
Introduction II In addition to enabling generic programming, the template mechanism of C++ unwittingly introduced a powerful code generation mechanism. Nested templates allow data structures to be created and manipulated at compile time by encoding them as types. This is the basis of the “expression templates technique”. Template meta-programs use the template instantiation mechanism to perform computations at compile time, and generate specialized algorithms by selectively inlining code as they are executed (for example: loop unrroling).
Introduction III Although these C++ techniques are powerful, the accidental nature of their presence has resulted in a clumsy syntax, hard to maintain and understand
pHaskell The main purpose of pHaskell is developing Active Libraries, combining the power of C++ templates and the simplicity of the functional languages pHaskell is a language which generates C++ generative code from a pseudo haskell-like syntax Also pHaskell offers a new feature called “gerenatrix” which is a function that generates code depending on the way it’s used (generative programming).
pHaskell developmentActors and skills Active Library code (activelib.phs) pHaskell Translator pHaskell Team: • C++ strong knowledge. • Haskell: Strong functional knowledge. activelib.h Active library developer skills: • C++ basics knowledge. • Haskell: Strong functional knowledge. • Library domain strong knowledge. + application.cpp End user: • C++ knowledge. C++ compiler Binary
pHaskell pHaskell vs Haskell syntax diferences The major differences between a haskell program and pHaskell pure functional code are: pHaskell uses typed lambda functions: Haskell : (x->x+1) pHaskell: (x::int->x+1) pHaskell requires “end” clause to indicate the end of a local definition section. inc n = sum (cte n) 1 where sum::int->int->int sum t r = t+r end Minor differences: Boolean constant: Haskell: True, False pHaskell: true, false Capital letters: pHaskell doesn’t require that type begins with capital letter.
pHaskell Presentation Roadmap This presentation will cover: Basis of the pHaskell translation scheme pHaskell: features and examples Generatrices: features and examples pHaskell translator utility
Translation Scheme pHaskell
The foundation of the pHaskell translation is the following equivalence: f a = exp => struct f { template<class a> struct app: exp { typedef exp value; }; }; And the evaluation: f 5 => f::app<Int<5> >::value pHaskell translation scheme Translated Expressions
pHaskell tranlation scheme As you should have noted we translate the expression in two parts: struct app: |exp|(1) { typedef |exp| value; (2) }; This is to get two strategies to evaluate an expression: Lazy: using (1) Eager: using (2)
pHaskell tranlation scheme This translation paradigm enables us to use nested templates to mimic multi-parameter functions. Also enables others functional features like currified functions, first order function values. Etc.
pHaskell tranlation scheme Multi-parameter function: f a b = exp => struct f { template<class a> struct app { template<class b> struct app: exp { typedef exp value; }; }; }; And the evaluation: f 5 6 => f::app<Int<5> >::app<Int<6> >::value Actually, this is a simplification of the a real translation, we have to include several tricks to make this ISO compliant but all these modifications are out of the scope in this presentation.
pHaskell tranlation scheme There are a lot of details on the translation paradigms, but these are outside the scope of this presentation. Many details had to be added to do the C++ generated code ISO compliant The rest of the presentation will show the pHaskell features throw examples. To know more detail about the translation from pHaskell to C++ go to http://code.google.com/p/phaskell/wiki/index In this page there are examples that show how we reached more advanced functional features like: Currified functions. Lambda expressions. Function as first order values. Pattern matching. User defined data types. Etc.
pHaskell Invoking function translated from pHaskell In pHaskell all constants and expressions are going to be translated into classes, so, if we have the pHaskell constants 4, true and [1, 2], they will be translated to: Int<4> Bool<true> metalist<Int<1>, metalist<Int<2>, metaemptylist<> > > This fact is important when we want to invoke a function translated from pHaskell
pHaskell features and examples All the following examples use a common header file: “prelude.h”. This header includes definitions of the basic operations and constructors that make the work easier for the pHaskell translator. To see the prelude.h header go to http://code.google.com/p/phaskell/wiki/prelude_h
pHaskell Invoking function translated from pHaskell All pHaskell expressions are translated to C++ classes, even the basic types (as we saw before). A result from pHaskell function could be unboxed to get the result as C++ integral type. int result = Int<4>::c_value bool result = Bool<true>::c_value Or int result = Int<4>::evaluate bool result = Bool<true>::evaluate
pHaskell Invoking function translated from pHaskell If we have a function like: sum::int->int->int sum x y = x+y It must be called in the following way: int main() { cout << sum::app(Int<2>)::app(Int<2>)::evaluate << endl; //sum 2 2 return 0; }
pHaskell Invoking function translated from pHaskell If we have a function like: length::metalist->intlength [] = 0length (x:xs) = 1 + (length xs) It must be called in the following way: int main() { //length [2] cout << length::app(metalist<Int<2> ,emptylist>)::evaluate; return 0; }
Features and examples pHaskell
pHaskell features and examples Haskell style code sum::int->int->int sum x y = x+y cte::int->int cte x = x maint::int maint = sum (cte 1) 2 To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • Pattern matching factorial::int->int factorial 0 = 1factorial x = x * factorial (x-1)maint::intmaint = factorial 5 To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples Currified functions sum::int->int->int sum x y = x+y inc::int->int inc = sum 1 maint::int maint = inc 4 Our translation paradigm enable partial evaluation of functions, also known as Currified functions To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples Functions as first order values f::int->int->int->intf x y z= x*(y+z)bindTwo::(int->int->int->int)->(int->int->int)bindTwo ff = ff 2 maint::intmaint = (bindTwo f) 1 1 We are passing the function “f” as first order value To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • Lambda Expressions and infix operands compose::(int->int)->(int->int)->(int->int)compose f g = (\x::int -> f (g x))maint::intmaint = ( (\x::int -> (x+2))'compose'(\x::int -> ((*x) 2 )) ) 5 Typed lambda expressions Infix operand To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • IF/ELSE expression factorial ::int->intfactorial x = if x>0 then x * (factorial (x-1)) else 1 maint::intmaint = factorial 5 To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • Guard defined functions factorial::int->intfactorial x | x>0 = (x * (factorial (x-1))) | otherwise = 1maint::intmaint = factorial 9 To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • List support and list pattern matching have_onetwo::metalist->boolhave_onetwo [] = false have_onetwo (1:2:xs) = true have_onetwo (x:xs) = have_onetwo xsmaint::boolmaint = have_onetwo [1, 3, 2 ,3 ,4 ,5, 1, 2] To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • Local definitions cte::int->intcte x = xinc::int->intinc n = sum (cte n) 1 where sum::int->int->int sum t r = t+r endmaint::intmaint = inc swhere s::int s=45end To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • User defined structured types/pattern matching data intTree = intNode intTree intTree | intLeaf int sumTree::intTree->intsumTree (intLeaf n) = nsumTree (intNode t tt) = (sumTree t) + (sumTree tt)maint::intmaint = sumTree (intNode (intLeaf 2) (intLeaf 5)) In this example we create a tree structure and walk this structure to sum all its leaves To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell features and examples • Lazy evaluation. We have reached a minimum level of laziness but not full lazy evaluation, We are still working on that. infiniteList::int->metalistinfiniteList n = n:infiniteList (n+1)take::int->metalist->metalisttake 0 x = []take n [] = []take n (x:xs) = x: (take (n-1) xs)sum::metalist->intsum [] = 0sum (x:xs) = x + sum xsmaint::intmaint = sum (take 5 (infiniteList 0)) Potentially infinite list We only take 5 elements of the list, so it works because of the lazy evaluation mechanism To see the whole example go to: http://code.google.com/p/phaskell/wiki/index
pHaskell data types All types in pHaskell have their C++ class translation: 5 -> Int< 5 > true -> Bool<true> data Tree = Node Tree Tree | Leaf int Node (Node (Leaf 1) (Leaf 2)) (Leaf 6) is translated as: Node< Node< Leaf<Int<1>>, Leaf<Int<2>> >, Leaf<Int<6>> >
Generatrix pHaskell Generatrix
pHaskell Generatrices Until now we were seeing a powerful translator from a haskell like functional code to C++ template meta-programming code. This is very useful to make complex computations like balancing binaries trees, searching a list of rules, transforming evaluations trees, etc. But now we want to use all this power to generate code in compilation time. Once this objective is reached, we will be able to develop Active Libraries that mutate in compilation time to bring a high performance compiled code.
pHaskell Generatrices To do this important task we have defined a new concept called “Generatrix”. A generatrix is a function that can receive classes, constants and pHaskell expressions to produce generative code taking advantage of C++ features such as Template Meta-programming, inlining. Etc. A generatrix build a new function in compilation time depending on its inputs The generatrix concept mixes together pHaskell pure functional programming style and C++ ordinary code.
pHaskell Generatrices + pHaskell common code The following example shows a complete pHaskell program split in two sections: Pure functional core: Make complex computation in compile time only. Generatrix code: Merge compile time computation with code that will be used in runtime to generate different C++ code depending on its inputs.
pHaskell Generatrices + pHaskell pure functional code {- pHaskell Section-} K:: int-> int K t | (0 <= t) && (t<=19) = 1518500249 | (20 <= t) && (t<=39) = 1859775393 | (40 <= t) && (t<=59) = 2400959708 | (60 <= t) && (t<=79) = 3395469782 ... {- generatrix section-} {uint} lror n {uint X} = (X << n::c_value) | (X >> (32 - n::c_value)) {uint} unbox t {} = t::c_value ... Pure functional pHaskell Section. Generatrix section. This snapshot was taken from a pHaskell sha1 calculator. In the following slides we will explain it in more detail. We will explain how pure functional pHaskell code and generatrix work together
pHaskell Generatrices + pHaskell pure functional code The generatrix syntax: {uint} lrorn {uint X} = (X << n::c_value) | (X >> (32 - n::c_value)) A generatrix has two kinds of parameters, runtime parameters and compile time parameters. A gereratrix will receive compile time parameters and runtime parameters to generate C++ template classes and functions Generatrix function body Generatrix runtime arguments Generatrix functional or compile time arguments Generatrix function name Generatrix return type
pHaskell Generatrices + pHaskell pure functional code {uint} lror n {uint X} = (X << n::c_value) | (X >> (32 - n::c_value)) Is translated to: struct lror { template<class n> struct generatrix { static uint code(uint X) { return (X<<n::c_value)|(X>>(32-n::c_value)); } }; }; And it is invoked from a C++ function in the following way: uint r = lror::generatrix<Int<1> >::code(4) Compile time parameter Runtime time parameter
pHaskell Generatrices + pHaskell pure functional code The main feature of the generatrix is: A generatrix can call another generatrix and use a pHaskell expression as a compilation time parameter. The following code generates code that removes all even numbers from a binary tree and fill the rest of the leaves in a vector using pre-order.
pHaskell Generatrix example {- Pure functional section -} data IntTree = IntNode IntTree IntTree | IntLeaf int | NullTree removeOdd NullTree = NullTree removeOdd (IntLeaf i) | (i%2 == 0) = IntLeaf i | otherwise = NullTree removeOdd (IntNode ta tb) = IntNode (removeOdd ta) (removeOdd tb) {- Generatrix section -} {int} getEven2 (NullTree) {intvector &evenList } = 0 {int} getEven2 (IntNode ta tb) {intvector &evenList } = {getEven2 ta}(evenList);{getEven2 tb}(evenList) {int} getEven2 (IntLeaf i) {intvector &evenList } = evenList.push_back(i::c_value) {int} getEven tree {intvector &evenList } = {getEven2 (removeOdd tree)}(evenList) The following code generates code that removes all even number from a binary tree and fill the rest of the leaves in a list using pre-order. This is the way to call a generatrix from other generatrix: { generatrix_name compile_time_parameters}(runtime_parameters)
pHaskell Generatrix example {- Pure functional section -} data IntTree = IntNode IntTree IntTree | IntLeaf int | NullTree removeOdd NullTree = NullTree removeOdd (IntLeaf i) | (i%2 == 0) = IntLeaf i | otherwise = NullTree removeOdd (IntNode ta tb) = IntNode (removeOdd ta) (removeOdd tb) {- Generatrix section -} {int} getEven2 (NullTree) {intvector &evenList } = 0 {int} getEven2 (IntNode ta tb) {intvector &evenList } = {getEven2 ta}(evenList);{getEven2 tb}(evenList) {int} getEven2 (IntLeaf i) {intvector &evenList } = evenList.push_back(i::c_value) {int} getEven tree {intvector &evenList } = {getEven2 (removeOdd tree)}(evenList) We can use pattern matching in the compile time parameters. In this case we store the leaf in a runtime parameter A getEven2 invocation Entry point, it takes the vector to fill and the tree to inspect In any generatrix invocation we can use a pHaskell function or expression as compile time parameter. In this case we use the removeOdd to remove the leaves having odd numbers
pHaskell translator utility pHaskell
pHaskell pHaskell translator utility 1) We write our active library in a .phs file with a full featured functional language (phaskell). source.phs 2) Execute phaskell.exe source.phs 3) As result we obtain two files: prelude.h: internal common definitions. source.phs.h: C++ Template code of our Active Library. phaskell.exe 4) Now, we can include source.phs.h in myprogram.cpp and use the Active Library. The active library will mutate in a compilation time depending on how the developer are using it source.phs.h + prelude.h C++ compiler myprogram.cpp binary
pHaskellFuture work and improvements • Full lazy evaluation (if it is feasible) • Type checking. • (pHaskell is designed to be a typed language but the typing rules • are not being in use yet) • Improve the parser to enable more syntactic sugar • Monads • List by comprehension • Others • Improve generatrix syntax • Add support for expression templates.