Library for Systematic Search for Expressions 1 Introduction 2
Total Page:16
File Type:pdf, Size:1020Kb
Library for Systematic Search for Expressions SUSUMU KATAYAMA Department of Computer Science and Systems Engineering University of Miyazaki 1-1 W. Gakuenkibanadai Miyazaki, Miyazaki 889-2155 JAPAN [email protected] http://nautilus.cs.miyazaki-u.ac.jp/˜skata Abstract: - In our previous work we showed that systematic search approach to inductive functional program- ming automation makes a remarkably efficient algorithm, but the applicability of the implemented program was limited by the very poor interpreter incorporated. In this paper, we present a library-based implementation of our systematic search algorithm which is supposed to be used with a well-known Haskell interpreter. Key-Words: - inductive program synthesis, functional programming, library design, metaprogramming, Template Haskell, artificial intelligence 1 Introduction efficient than elaborated systematic exhaustive search for type-correct programs? If so, when and how is MagicHaskeller is our inductive programming automa- it? Has someone ever compared them?” In order to tion system by (exhaustively-and-efficiently-)generate- do the right things in the right order, in this project and-filter scheme for the lazy functional language we first elaborate on optimized implementation of Haskell. Until our previous work[1][2] it has been im- systematic exhaustive search for type correct programs, plemented as a stand alone program including a cheap and then, if we become certain that we need heuristic interpreter for filtration of the generated programs. In approaches and that they really help improving the this research we implemented its API library package efficiency, we will build heuristic approaches on the which is supposed to be used with Glasgow Haskell top of our research on efficient systematic search Compiler interactive (GHCi) version 6.4 and higher. algorithms under type constraints. Using techniques The library implementation enjoys the following ef- from software science such as monad for breadth-first fects (some of which will be discussed further in Sec- search[3], generalized trie[4], monad transformer[5], tion 4): and memoization using lazy data type[2], we have already shown that systematic exhaustive search ² user-defined data types can be used; for type-correct programs is a powerful alternative ² the full language features and libraries that GHC to genetic programming. The implemented pro- provides are supported for processing generated gram and library can be downloaded and tried from expressions; http://nautilus.cs.miyazaki-u.ac.jp/~skata/ MagicHaskeller.html. ² the usability when one wants to choose from differ- ent primitive sets has improved; for example, users can predefine reusable chunks of primitives such 2.2 Template Haskell as list-related one, nat-related one, etc. and append them to obtain a suitable primitive set for the syn- Template Haskell (TH) [6] brings template metapro- thesis to be conducted. gramming to Haskell. In TH, expressions, sets of dec- ² larations, and types to be spliced have type ExpQ, it has become easier to investigate the properties of Q [Dec], and TypeQ respectively, where ExpQ = Q Exp the set of all the expressions that can be generated and TypeQ = Q Type, where monad Q is a wrapper to from the given type and the given primitive com- avoid name collision. A code block can be spliced by us- ponent set. ing $(.). An expression, a set of declarations, and a type can be quoted by using [j.j], [dj.j], and [tj.j] respectively. 2 Background For each of the primitive components we need the in- formation of both of its semantics and its syntax, i.e., the 2.1 MagicHaskeller value of the component which is used for interpretation and the name of the component which is used to print MagicHaskeller[1][2] is a project which emerged from the generated expression. The most important role of our question: “Is genetic programming really more TH in our library is to generate both of them without re- quiring users to write the same name twice, which will setPrimitives $(p [j(+) :: Num a ) a ! a ! aj]) be explained further in the definition of function p in Also, you have to specify the type unless you are us- the next section. ing a monomorphic component (just like when using the dynamic expression of Concurrent Clean), and thus you may write setPrimitives $ (p [j'A'j]), while you 3 Library specification have to write setPrimitives $(p [j[] :: [a]j]) instead of setPrimitives $(p [j[]j]) This section is devoted to the documentation of the im- plemented library. For each of the exported functions, its type and its short description are given. p :: TH.ExpQ ! TH.ExpQ type Primitive = (HValue, TH.Exp, String) 3.1 Re-exported modules p is used to convert your primitive component set This library implicitly re-exports the entities into its internal form. Usually its argument is the quasi- from module Language.Haskell.TH as TH and quote of a tuple of primitive components, but when it is module Data.Typeable from the Standard Hierarchical not, singleton list will be generated. Library of Haskell. Please refer to their documentations The internal representation of each item of the on types from them — in this documentation, types primitive component set has type Primitive = from TH are all qualified and the only type class used (HValue, TH.Exp, String), where newtype HValue = from module Typeable is Typeable.Typeable. HV (forall a.a) is a universally quantified value rep- The following types are assigned to our internal data resenting the semantics of the primitive component, representations: type Primitive representing each prim- TH.Exp represents its syntactic aspect, and String is a itive combinator and type Memo representing the mem- string representation of its type. For example, p [j((+) :: oization table. Int ! Int ! Int, 0 :: Int, 'A', [] :: [a])j] reduces to1 [(HV (unsafeCoerce#((+) :: Int ! Int ! Int)), 3.2 Setting up the synthesis TH.VarE (TH.mkName "+"), "(((->) Int) (((->) Int) Int))"), Before synthesis, you have to define at least a memo- (HV (unsafeCoerce#(0 :: Int)), ization table (or you may define one once and reuse it TH.LitE (TH.IntegerL 0), for later syntheses). Other parameters are memoization "Int"), depth and time out interval, which have default values. (HV (unsafeCoerce#'A'), You may elect either to set those values to the `global TH.LitE (TH.CharL 'A'), variables' using `set*' functions, or hand them explicitly show (typeOf 'A')), as parameters. (HV (unsafeCoerce#([ ] :: forall a 0.[a 0])), Note that the functions related to global variables are TH.ConE (TH.mkName "[]"), supposed to be used within the GHCi environment, and "([] a_0)")] are not tested on other environments. They are imple- Use of HValue and unsafeCoerce# for such purposes is mented in the same way as GHCi implements global taken from the source code of GHCi. The type consis- variables. For example, the global memoization table is tency will not be assured statically by GHC but dynam- implemented as: f g ically by our library. When the type is not given, it will -# NOINLINE refmemodeb #- be obtained via typeOf method as is seen in the 'A' case refmemodeb :: IORef Memo of the above example, although that will cause ambigu- = ( ) refmemodeb unsafePerformIO newIORef defaultMD ity error if the value is polymorphic. defaultMD = mkMemo [] ! Functions for creating memoization tables from the setPrimitives :: [Primitive] IO () primitive components You can set your primitives setPrimitives = setMemo.mkMemo ! like, e.g., mkMemo :: [Primitive] Memo ! setPrimitives setMemo :: Memo IO () $(p [j(+) :: Int ! Int ! Int, 0 :: Int, 'A', [] :: [a]j]) where the primitive set is consisted of (+) special- ized to type Int, 0 specialized to type Int, 'A' which Memoization depth has monomorphic type Char, and [] with polymor- phic type [a]. As primitive components one can in- ! clude any variables and constructors within the scope setDepth :: Int -- memoization depth unless data types holding functions such as [a ! b], IO () (Int ! Char, Bool), etc. are involved. However, be- cause currently ad hoc polymorphism is not supported 1Here we stripped some of the qualifications by module names in by this library, you may not write order to avoid clutter. setDepth can be used to set the memoization depth. Quick starters (Sub)expressions within this size are memoized, while greater expressions will be recomputed (to save the findOne :: Typeable a ) (a ! Bool) ! TH.Exp heap space). printOne :: Typeable a ) (a ! Bool) ! IO () printAny :: Typeable a ) (a ! Bool) ! IO () Functions related to time out Because the library gen- erates all the expressions including those with non- findOne pred finds an expression e that satisfies linear recursions, you should note that there exist some pred e ´ True, and returns it in TH.Exp. printOne prints expressions which take extraordinarily long time.[2] the expression found first. printAny prints all the ex- Imagine a function that takes an integer n and incre- n pressions satisfying the given predicate. ments 0 for 22 times. Another example that has smaller program size is blah in Incremental filtration blah :: Int ! Int ! Int Sometimes you may want to blah 0 i = i filter further after synthesis, because the predicate blah k i = foo (blah (k ¡ 1)) i you previously provided did not specify the function foo h 0 = 2 enough. The following functions can be used to filter foo h s = h (f h (s ¡ 1)) expressions incrementally. Giving small values in your examples is a good policy, but even then, time out is useful. For this reason, time filterFirst :: Typeable a ) out is by default taken after 1 second since each invo- (a ! Bool) ! IO [[(TH.Exp, a)]] cation of evaluation. This default behavior can be over- filterThen :: Typeable a ) ridden by the following functions. (a ! Bool) ! [[(TH.Exp, a)]] ! IO [[(TH.Exp, a)]] setTimeout :: Int ! -- time in seconds filterFirst is like printAny, but by itself it does not print IO () anything.