面向敏捷虚拟企业的制造agent投标优化模型
Download
Report
Transcript 面向敏捷虚拟企业的制造agent投标优化模型
Introduction to Compilation of
Functional Languages
Wanhe Zhang
Computing and Software Department
McMaster University
16th, March, 2004
Functional Programs
Based on the idea that a program is a
function with one input parameter, its input
and one result, its output.
Difference between functional and
imperative language is efficiency
consideration and readability. We can see
that from the factorial function below:
Difference between Func and
Imperative Language
Compilation of Functional
Languages
A short tour of Haskell
Compiling functional languages
Polymorphic type checking
Desugaring
Tour of Haskell
Function application syntax:
f 11 13
1)No bracket around the arguments, allows
currying to be expressed naturally.
2)Function application binds stronger than
any operator.
g n+1 is (g n) + 1 rather than g (n+1)
Tour of Haskell
Offside rule
Lists
List comprehension
Pattern matching
Polymorphic typing
Referential transparency
Higher-order functions
Lazy evaluation
Offside rule
Divide x 0 = inf
Divide x y = x/y
An equation consists of a left-hand side, followed
by the = token, followed by the right-hand side.
No explicit token to denote the end of each
equation. If treats line break as the terminator is
inconvenient.
Offside rule controls the bounding box of an
expression.
Offside rule
Everything below and to the right of the =
token is defined to be part of the expression
making up the right-hand side.
The right-hand side terminates before the
first token that is ‘offside’-to the left-of the
= position.
Lists
The polymorphic typing of Haskell, does
not allow lists to contain elements of
different.
[]
[1,2,3,4] = (1:(2:(3:(4:[]))))
[“red”, “yellow”]
[1..10]
List Comprehension
Syntax that closely matches set notation.
S = [n^2 | n <- [1..100], odd n]
List comprehension generates lists rather
than sets: ordering is important and
elements may occur multiple times in list
comprehensions.
It is convenient to use when generating new
lists from old ones.
List Comprehension
qsort []
qsort (x:xs)
= []
= qsort [y | y <- xs, y<x]
++ [x]
++ qsort [y | y <- xs, y >= x]
Pattern Matching
fac 0 = 1
fac n = n*fac (n-1)
Function equations are matched from top to
bottom; the patterns in them are matched from left
to right.
Pattern matching can be translated easily into
equivalent definitions based on if-then-else
constructs.
Fac n = if (n == 0) then 1
else n * fac (n-1)
Polymorphic Typing
An expression is said to be polymorphic if it
‘has many types’.
List [] has many types: list of characters, list
of numbers and an infinite number of others.
The main advantage of polymorphic typing
is that functions and data structures can be
reused for any desired type instance.
Type checking will discussed later.
Referential Transparency
A fixed relation between inputs and output:
f arg will produce the same output no matter what the
overall state of the computation is.
Imperative languages, assignments to global variables and
through pointers may cause two calls f arg to yield
different result.
The advantage is that it simplifies program analysis and
transformation. The bad thing is that it prevents the
programmer from writing space-efficient programs that use
in-space updates.
Add_one [] = []
add_one (x:xs) = x+1 : add_one xs
In imperative language, we can update the input list inplace.
Higher-order Functions
Higher-order function is defined as a function that takes a
function as an argument, or delivers one as a result.
Imperative languages barely support higher-order functions:
functions may perhaps be passed as parameters, cannot
create a new one.
Two way to create new functions:
1) diff f = f_
where f_ x = (f ( x + h ) – f x) / h
h = 0.0001
diff returns as its result a ‘new’ function that is composed
out of already existing functions.
2) diff f x = ( f ( x + h ) – f x) / h
where h = 0.0001
Apply an existing function to a number of arguments that
is less than the arity of the function.-------Currying
Lazy Evaluation
Lazy evaluation relaxes these constraints by
specifying that a subexpression will only be
evaluated when its value is needed for the
progress of the computation.
Compiling Functional Languages
Below is the compiler phase handles which
aspect of Haskell:
Compiling Functional Languages
The Functional Core
It must be high-level enough to serve as an
easy target for the front-end that compiles
the syntactic sugar away.
It must be low-level enough to allow
concise descriptions of optimizations, which
are often expressed as a case analysis of all
core constructs.
Functional Core of Haskell
Basic data types, including int, char, and bool;
(user-defined) structured data types;
Typed non-nesting functions;
Local bindings as part of let-expressions;
Expressions consisting of identifiers, arithmetic
operators, if-then-else compounds, and function
applications;
Higher-order functions; (cannot map onto C)
Lazy evaluation semantics. (cannot map onto C)
Polymorphic Type checking
We illustrate this by an example:
map f []
= []
map f ( x : xs ) = f x : map f xs
First equation: map :: a -> [b] -> [c]
Second equation: map :: (b -> c) -> [b] -> [c])
For the second, x is an element of the list with
type [b] and that f x is a part of map’s result list
with type [c], so the type of f is b -> c
Polymorphic Function Application
Map :: ( a -> b ) -> [a] -> [b]
length :: [c] -> Int
map length
The type checker must unify the type of
length, which is [c] -> Int, with the type of
map’s first argument, a -> b. => a = [c], b =
Int.
Map :: ( [c] -> Int ) -> [[c]] -> [Int]
map length :: [[c]] -> [Int]
Desugaring
Transform a Haskell program into its
functional-core equivalent.
We will focus on translating lists, pattern
matching, list comprehension, and nested
functions to core constructs.
The Translation of Lists
Three forms of syntactic sugar : , ..
The operator : constructs a node with three
fields: a type tag Cons, an element, and a
list.
x : xs is transformed to (Cons x xs)
[1,2] is transformed to (Cons 1(Cons 2 []))
[1..] is usually translated to calls of library
functions that express these lists in terms of :
and [].
The Translation of Pattern
Matching
The Translation of Pattern
Matching
A constant yields an equality test.
A variable imposes no constraint at all.
Constructor patterns, require additional
support to provide type information at run
time. We must be able to verify that an
argument matches the constructor specified
in pattern
Constructor Patterns
The run-time support to provide the _type_constr
function that returns the constructor tag of an
arbitrary structured type element.
Reference the fields in the constructor type.
The run-time assists us by providing the generic
_type_field n function that returns the nth field of
any structured type.
We will illustrate above by an example below:
Constructor Patterns
take 0 xs = []
take n [] = []
take n (x: xs) = x : take (n-1) xs
Optimization
The code has already been type-checked at compile
time, any second argument in a call of take is
guaranteed to be a list. So the last equation need not
verify that the argument matches the constructor
pattern, and the error guard can be omitted too.
The Translation of List
Comprehension
[expression | qualifier, ..., qualifier]
Qualifier is either a generator or a filter.
A generator is of the form var <- list
expression; it introduces a variable iteration
over a list.
A filter is a Boolean expression, which
constrains the variables generated by earlier
qualifiers.
The Translation of List
Comprehension
The transformation works by processing the
qualifiers from left to right one at a time. This
approach naturally leads to a recursive scheme as
presented below.
The Translation of List
Comprehension
Transformation rule (1) covers the base case where no
more qualifiers are present in the list comprehension.
The filter qualifier is handled in transformation rule(2),
where F stands for the filter and Q stands the remaining
sequence of qualifiers.
The generator qualifier e <- L is covered in rule (3) The
generator produces zero or more elements e drawn from a
list L. We must generate code to iterate over all elements e,
Compute the remainder Q of the list comprehension for
each value of e, and concatenate the – possible emptyresult lists into a single list.
The key idea for rule (3) is a nested function takes element
e and produces the list of values that Q can assume for e.
The function then is called over all the elements in L.
The Translation of List
Comprehension
We need calling a function for all elements in a list;
Concatenation the elements of the resulting lists into one.
Map function does not work.
map f []
= []
map f ( x : xs ) = f x : map f xs
It simply concatenates the results of function applications,
and would yield a list of lists in this case.
Modified map:
mappend :: (a -> [b]) -> [a] -> [b]
mappend f []
= []
mappend f (x:xs) = f x ++ mappend f xs
The Translation of List
Comprehension
Below we will illustrate the theory above:
Pyth n = [(a, b, c) | a <- [1 .. n],
b <- [a .. n],
c <- [b .. n],
a^2 + b^2 == c^2]
Transformation of Pyth:
Pyth n = mappend f_bc2 [1..n]
where
f_bc2 a = mappend f_2 [b .. n]
where
f_2 c = if (a^2 + b^2 == c^2)
then [(a, b, c)]
else []
The Translation of Nested
Functions
Since most target languages of functional compilers don’t
support nested routines. The functional core excludes
nested functions.
Using lexical pointers to activation records in combination
with higher-order functions and lazy evaluation causes
dynamic scope violations, since a call to a nested function
may escape its lexical scope at run time, rendering its
lexical pointer invalid.
For example, a nested function can be returned as the result
of higher-order function; lazy evaluation can delay the
execution of a call to the nested function until the caller
has returned its value, contains a reference to the
suspended call.
The Translation of Nested
Functions
Example:
Sv_mul defines the multiplicaiton of a scalar and a
vector.
Analysis of the Example
Call map to apply the nested function s_mul to
each element in the vector list.
At run time, the interpreted code for sv_mul
returns a graph holding the unevaluated expression
map s_mul vec.
If we return the routine value map s_mul vec, the
activation record of sv_mul will be removed
before the nested function s_mul is ever applied.
The Translation of Nested
Functions
The functional core supports currying( partial
parameterization).
Translating a nested routine f to a global routine is
just a matter of extending it with additional
parameters p1,p2… pa that capture the out-of-scope
pointers; each usage of the nested function f must
be replaced with a curried call: f p1…pa
The Translation of Nested
Functions
Lift the nested s_mul into a global funciton
sv_mul_dot_s_mul
Extend the function heading with an additional parameter
named scal capuring the pointer to the scal parameter of
the outer sv_mul function.
All calls of s_mul are replaced by the expression
sv_mul_dot_s_mul scal.
Conclusion
Short tour of Haskell
General concept of Compiler for Functional
programs
Type checking
Desugaring----The most important part
Questions?☺