The Decorator Pattern in Haskell

Total Page:16

File Type:pdf, Size:1020Kb

The Decorator Pattern in Haskell Submission deadline: Monday, 12 May 2014, anywhere on Earth Functional Pearl: The Decorator Pattern in Haskell Nathan Collins Tim Sheard Portland State University [email protected] [email protected] Abstract Suppose we implement exponentiation in Haskell, using divide- The Python programming language makes it easy to implement and-conquer: decorators: generic function transformations that extend existing pow b p = functions with orthogonal features, such as logging, memoization, if p <= 1 and synchronization. Decorators are modular and reusable: the user then b * p does not have to look inside the definition of a function to decorate else pow b (p `div` 2) * pow b (p - (p `div` 2)) it, and the same decorator can be applied to many functions. In this paper we develop Python-style decorators in Haskell generally, And equivalently, in Python: and give examples of logging and memoization which illustrate the def pow(b, p): simplicity and power of our approach. if p <= 1: Standard decorator implementations in Python depend essen- return b * p tially on Python’s built-in support for arity-generic programming else: and imperative rebinding of top-level names. Such rebinding is not return pow(b, p//2) * pow(b, p - (p//2)) possible in Haskell, and Haskell has no built-in support for arity- generic programming. We emulate imperative rebinding using mu- Now, suppose we want to observe our function in order to debug tual recursion, and open recursion plus fixed points, and reduce the it. One way to do this would be to print out call-trace information arity-generic programming to arity-generic currying and uncurry- as the function runs. This could be accomplished by interleaving ing. In developing the examples we meet and solve interesting aux- print statements with our code (using Debug.Trace in Haskell): iliary problems, including arity-generic function composition and ugly, but it works. first-class implication between Haskell constraints. In Python, we can instead do something modular and reusable: we can write a generic call-tracing decorator: LEVEL = 0 1. Decorators by Example in Python and Haskell def trace(f): We begin by presenting Python and Haskell decorators by example, def traced(*args): while glossing over a lot of details which will be provided in later global LEVEL sections. This section serves both to motivate the problem and give prefix = "| " * LEVEL the intuition for our solution. The code described in this paper, print prefix + ("%s%s" % (f.__name__ , args)) and more elaborate examples not described here, are available on LEVEL += 1 GitHub [3]. r = f(*args) Our example decorators are call-tracing and memoization, and LEVEL -= 1 our example function to decorate is natural exponentiation bp. We print prefix + ("%s" % r) choose this example function because 1) it admits an obvious recur- return r sive implementation which makes redundant recursive calls, and 2) return traced it’s not a unary function.1 The recursion makes call-tracing inter- For those not familiar with Python, decorators in Python are esting and the redundant recursion makes memoization applicable. explained in more detail in Appendix A. Their utility depends We care about higher arity because we want our decorators to be heavily on being arity-generic2 and being able to trap recursive calls arity generic. to the function being traced3. After adding the line 1 For unary functions an obvious example is Fibonacci, which we consider pow = trace(pow) later in Section 2.1. to the source program, we run pow(2, 6) and see pow(2, 6) | pow(2, 3) | | pow(2, 1) 2 In Python, *args as a formal parameter, in def traced(*args), de- clares a variadic function, like defun traced (&rest args) in LISP; *args as an actual parameter, in f(*args), applies a function to a se- quence of arguments, like (apply f args) in LISP; % as a binary operator is format-string substitution. 3 In Python, function names are just lexically scoped mutable variables, so [Copyright notice will appear here once ’preprint’ option is removed.] trapping is simply a matter of redefinition. short description of paper 1 2014/8/27 | | 2 functions that instead take a single tuple as argument. In Haskell | | pow(2, 2) then, a good analogy is arity-generic currying and uncurrying:4 | | | pow(2, 1) curry f x1 ... xn = f (x1 , ... , xn) | | | 2 uncurryM f (x1 , ... , xn) = f x1 ... xn | | | pow(2, 1) | | | 2 Here curry f in Haskell corresponds to def f(*args): ... | | 4 in Python, and uncurryM f args in Haskell corresponds to | 8 f(*args) in Python. We’ll explain how to statically type and | pow(2, 3) implement these functions later, but for now we just need to un- | | pow(2, 1) derstand them operationally at an intuitive level. | | 2 With curry and uncurryM in hand we can write a well-typed5 | | pow(2, 2) call-tracing decorator in Haskell quite similar to the Python deco- | | | pow(2, 1) rator we saw earlier: | | | 2 | | | pow(2, 1) trace levelRef name f = curry traced where | | | 2 traced args = do | | 4 level <- readIORef levelRef | 8 let prefix = concat . replicate level $ "| " 64 putStrLn $ prefix ++ name ++ show args modifyIORef levelRef (+1) Noting the repeated sub computations, we see that memoization r <- uncurryM f args would be an improvement. So, we write a generic memoization modifyIORef levelRef (subtract 1) decorator: putStrLn $ prefix ++ show r def memoize(f): return r cache = dict() Similary, we can write a well-typed memoization decorator: def memoized(*args): if args not in cache: memoize cacheRef f = curry memoized where cache[args] = f(*args) memoized args = do return cache[args] cache <- readIORef cacheRef memoized.__name__ = f.__name__ case Map.lookup args cache of return memoized Just r -> return r Nothing -> do and replace the line r <- uncurryM f args pow = trace(pow) modifyIORef cacheRef (Map.insert args r) return r with These decorators are both monadic; we discuss non-monadic dec- pow = trace(memoize(pow)) orators in Section 3. To apply these decorators to we make two changes: 1) Running pow(2, 6), we see pow we rewrite pow as a monadic function, because the decorators are pow(2, 6) monadic; 2) we rewrite pow as an open-recursive function, so that | pow(2, 3) we can trap recursive calls. In general, (1) is obviously unnecessary | | pow(2, 1) if the function we want to decorate is already monadic, and for pure | | 2 functions we can actually use unsafePerformIO6 to avoid making | | pow(2, 2) the function monadic, as we explain in Section 3.2. For (2), we can | | | pow(2, 1) alternatively use mutual recursion, as we discuss in Section 2.1. | | | 2 Before decoration, a monadic version of pow using (unneces- | | | pow(2, 1) sary) open recursion is | | | 2 | | 4 openPowM pow b p = do | 8 if p <= 1 | pow(2, 3) then pure $ b * p | 8 else (*) <$> pow b (p `div` 2) <*> 64 pow b (p - (p `div` 2)) powM = fix openPowM Arity-generic decorators are easy to write in Python, and are reusable. In Haskell, things do not appear to be so simple. But, We can now decorate powM with both memoization and tracing with it turns out that, in Haskell it’s also easy to write arity-generic just a few lines: decorators! Indeed, that’s what this paper is about. powM :: Int -> Int -> IO Int An arity-generic decorator needs to solve two problems: inter- cept recursive calls and handle functions of any arity uniformly. In 4 The “M” in “uncurryM” stands for “monadic”. Python, arity genericity is easy to implement via the built-in *args 5 In fact, once curry and uncurryM are defined, GHC 7.6.3 can infer feature, and a function name is simply a statically scoped mutable the types of these decorators. In practice, the type annotations make good variable, so a simple assignment can be used to intercept recursive documentation, but we aren’t ready to explain the types yet, so we postpone calls. In Haskell these problems need to be solved in another way. them until Section 2.3.2. Let’s start with arity-genericity. What Python’s *args feature 6 Yes, unsafePerformIO is easily abused, but we think Debug.Trace is does is allow us to treat functions of any arity uniformly as unary good precedent here, at least in the call-tracing use case. short description of paper 2 2014/8/27 powM b p = do if n <= 1 levelRef <- newIORef 0 then n cacheRef <- newIORef Map.empty else fib (n-1) + fib (n-2) fix (trace levelRef "powM" . memoize cacheRef . openPowM) b p Writing recursive functions in this mutually recursive style may seem pointless, but it makes them amenable to decoration at a Running powM 2 6 we see7 neglibile cost – less than one line. It is also very easy to re-factor a function not written in this style using regexp-search and replace powM(2,(6,())) in your editor. Once done, this split into two mutually recursive | powM(2,(3,())) functions does not need to be undone if one decides that decoration | | powM(2,(1,())) is no longer necessary. For example, after using trace to debug | | 2 a function, we might want to disable tracing by removing the | | powM(2,(2,())) decorator. | | | powM(2,(1,())) Alternatively, we can use open recursion and fixed points. Given | | | 2 the open-recursive openFib defined by | | | powM(2,(1,())) | | | 2 openFib :: (Int -> Int) -> (Int -> Int) | | 4 openFib fib n = | 8 if n <= 1 | powM(2,(3,())) then n | 8 else fib (n-1) + fib (n-2) 64 we can rewrite fib as a fixed point of openFib: Of course, it may not yet be obvious how to implement curry and uncurryM.
Recommended publications
  • Unfold/Fold Transformations and Loop Optimization of Logic Programs
    Unfold/Fold Transformations and Loop Optimization of Logic Programs Saumya K. Debray Department of Computer Science The University of Arizona Tucson, AZ 85721 Abstract: Programs typically spend much of their execution time in loops. This makes the generation of ef®cient code for loops essential for good performance. Loop optimization of logic programming languages is complicated by the fact that such languages lack the iterative constructs of traditional languages, and instead use recursion to express loops. In this paper, we examine the application of unfold/fold transformations to three kinds of loop optimization for logic programming languages: recur- sion removal, loop fusion and code motion out of loops. We describe simple unfold/fold transformation sequences for these optimizations that can be automated relatively easily. In the process, we show that the properties of uni®cation and logical variables can sometimes be used to generalize, from traditional languages, the conditions under which these optimizations may be carried out. Our experience suggests that such source-level transformations may be used as an effective tool for the optimization of logic pro- grams. 1. Introduction The focus of this paper is on the static optimization of logic programs. Speci®cally, we investigate loop optimization of logic programs. Since programs typically spend most of their time in loops, the generation of ef®cient code for loops is essential for good performance. In the context of logic programming languages, the situation is complicated by the fact that iterative constructs, such as for or while, are unavailable. Loops are usually expressed using recursive procedures, and loop optimizations have be considered within the general framework of inter- procedural optimization.
    [Show full text]
  • Design Patterns Promote Reuse
    Design Patterns Promote Reuse “A pattern describes a problem that occurs often, along with a tried solution to the problem” - Christopher Alexander, 1977 • Christopher Alexander’s 253 (civil) architectural patterns range from the creation of cities (2. distribution of towns) to particular building problems (232. roof cap) • A pattern language is an organized way of tackling an architectural problem using patterns Kinds of Patterns in Software • Architectural (“macroscale”) patterns • Model-view-controller • Pipe & Filter (e.g. compiler, Unix pipeline) • Event-based (e.g. interactive game) • Layering (e.g. SaaS technology stack) • Computation patterns • Fast Fourier transform • Structured & unstructured grids • Dense linear algebra • Sparse linear algebra • GoF (Gang of Four) Patterns: structural, creational, behavior The Gang of Four (GoF) • 23 structural design patterns • description of communicating objects & classes • captures common (and successful) solution to a category of related problem instances • can be customized to solve a specific (new) problem in that category • Pattern ≠ • individual classes or libraries (list, hash, ...) • full design—more like a blueprint for a design The GoF Pattern Zoo 1. Factory 13. Observer 14. Mediator 2. Abstract factory 15. Chain of responsibility 3. Builder Creation 16. Command 4. Prototype 17. Interpreter 18. Iterator 5. Singleton/Null obj 19. Memento (memoization) 6. Adapter Behavioral 20. State 21. Strategy 7. Composite 22. Template 8. Proxy 23. Visitor Structural 9. Bridge 10. Flyweight 11.
    [Show full text]
  • A Right-To-Left Type System for Value Recursion
    1 A right-to-left type system for value recursion 61 2 62 3 63 4 Alban Reynaud Gabriel Scherer Jeremy Yallop 64 5 ENS Lyon, France INRIA, France University of Cambridge, UK 65 6 66 1 Introduction Seeking to address these problems, we designed and imple- 7 67 mented a new check for recursive definition safety based ona 8 In OCaml recursive functions are defined using the let rec opera- 68 novel static analysis, formulated as a simple type system (which we 9 tor, as in the following definition of factorial: 69 have proved sound with respect to an existing operational seman- 10 let rec fac x = if x = 0 then 1 70 tics [Nordlander et al. 2008]), and implemented as part of OCaml’s 11 else x * (fac (x - 1)) 71 type-checking phase. Our check was merged into the OCaml distri- 12 Beside functions, let rec can define recursive values, such as 72 bution in August 2018. 13 an infinite list ones where every element is 1: 73 Moving the check from the middle end to the type checker re- 14 74 let rec ones = 1 :: ones stores the desirable property that compilation of well-typed programs 15 75 Note that this “infinite” list is actually cyclic, and consists of asingle does not go wrong. This property is convenient for tools that reuse 16 76 cons-cell referencing itself. OCaml’s type-checker without performing compilation, such as 17 77 However, not all recursive definitions can be computed. The MetaOCaml [Kiselyov 2014] (which type-checks quoted code) and 18 78 following definition is justly rejected by the compiler: Merlin [Bour et al.
    [Show full text]
  • A Call-By-Need Lambda Calculus
    A CallByNeed Lamb da Calculus Zena M Ariola Matthias Fel leisen Computer Information Science Department Department of Computer Science University of Oregon Rice University Eugene Oregon Houston Texas John Maraist and Martin Odersky Philip Wad ler Institut fur Programmstrukturen Department of Computing Science Universitat Karlsruhe UniversityofGlasgow Karlsruhe Germany Glasgow Scotland Abstract arguments value when it is rst evaluated More pre cisely lazy languages only reduce an argument if the The mismatchbetween the op erational semantics of the value of the corresp onding formal parameter is needed lamb da calculus and the actual b ehavior of implemen for the evaluation of the pro cedure b o dy Moreover af tations is a ma jor obstacle for compiler writers They ter reducing the argument the evaluator will remember cannot explain the b ehavior of their evaluator in terms the resulting value for future references to that formal of source level syntax and they cannot easily com parameter This technique of evaluating pro cedure pa pare distinct implementations of dierent lazy strate rameters is called cal lbyneed or lazy evaluation gies In this pap er we derive an equational characteri A simple observation justies callbyneed the re zation of callbyneed and prove it correct with resp ect sult of reducing an expression if any is indistinguish to the original lamb da calculus The theory is a strictly able from the expression itself in all p ossible contexts smaller theory than the lamb da calculus Immediate Some implementations attempt
    [Show full text]
  • Dependency Injection in Unity3d
    Dependency Injection in Unity3D Niko Parviainen Bachelor’s thesis March 2017 Technology, communication and transport Degree Programme in Software Engineering Description Author(s) Type of publication Date Parviainen, Niko Bachelor’s thesis March 2017 Language of publication: English Number of pages Permission for web publi- 57 cation: x Title of publication Dependency Injection in Unity3D Degree programme Degree Programme in Software Engineering Supervisor(s) Rantala, Ari Hämäläinen, Raija Assigned by Psyon Games Oy Abstract The objective was to find out how software design patterns and principles are applied to game development to achieve modular design. The tasks of the research were to identify the dependency management problem of a modular design, find out what the solutions offered by Unity3D are, find out what the dependency injection pattern is and how it is used in Unity3D environment. Dependency management in Unity3D and the dependency injection pattern were studied. Problems created by Unity3D’s solutions were introduced with examples. Dependency in- jection pattern was introduced with examples and demonstrated by implementing an ex- ample game using one of the available third-party frameworks. The aim of the example game was to clarify if the use of dependency injection brings modularity in Unity3D envi- ronment and what the cost of using it is. The principles of SOLID were introduced with generic examples and used to assist depend- ency injection to further increase the modularity by bringing the focus on class design. Dependency injection with the help of SOLID principles increased the modularity by loosely coupling classes even though slightly increasing the overall complexity of the architecture.
    [Show full text]
  • Facet: a Pattern for Dynamic Interfaces
    Facet: A pattern for dynamic interfaces Author: Eric Crahen SUNY at Buffalo CSE Department Amherst, NY 14260 201 Bell Hall 716-645-3180 <[email protected]> Context: Wherever it is desirable to create a context sensitive interface in order to modify or control the apparent behavior if an object. Problem: How can I modify the behavior of an existing object so that different behaviors are shown under different circumstances; and yet still maintain a clean separation between the policy (when each behavior is available) and implementation of each behavior allowing them to be developed independently of one another? Forces: Interfaces provide an essential level of abstraction to object oriented programming. Generally, objects are able define aspects of their function very well using interfaces. At times, this may not be enough. When dealing with two or more classes whose responsibilities are distinctly separate, but whose behavior is closely related, classes can begin to resist modularization. For example, adding security to an application means inserting code that performs security checks into numerous locations in existing classes. Clearly, these responsibilities are distinct; the original classes being responsible for the tasks they were designed to perform, and the security classes being responsible for providing access control. However, making those original classes secure means intermingling code that deals with security. When the classes dealing with security are changed many classes are going to be impacted. One method of adding new behavior to an existing class might be to simply create a subclass and embed that new behavior. In the security example, this would mean creating a subclass for each class that needs to be secure and adding calls to the security code.
    [Show full text]
  • NU-Prolog Reference Manual
    NU-Prolog Reference Manual Version 1.5.24 edited by James A. Thom & Justin Zobel Technical Report 86/10 Machine Intelligence Project Department of Computer Science University of Melbourne (Revised November 1990) Copyright 1986, 1987, 1988, Machine Intelligence Project, The University of Melbourne. All rights reserved. The Technical Report edition of this publication is given away free subject to the condition that it shall not, by the way of trade or otherwise, be lent, resold, hired out, or otherwise circulated, without the prior consent of the Machine Intelligence Project at the University of Melbourne, in any form or cover other than that in which it is published and without a similar condition including this condition being imposed on the subsequent user. The Machine Intelligence Project makes no representations or warranties with respect to the contents of this document and specifically disclaims any implied warranties of merchantability or fitness for any particular purpose. Furthermore, the Machine Intelligence Project reserves the right to revise this document and to make changes from time to time in its content without being obligated to notify any person or body of such revisions or changes. The usual codicil that no part of this publication may be reproduced, stored in a retrieval system, used for wrapping fish, or transmitted, in any form or by any means, electronic, mechanical, photocopying, paper dart, recording, or otherwise, without someone's express permission, is not imposed. Enquiries relating to the acquisition of NU-Prolog should be made to NU-Prolog Distribution Manager Machine Intelligence Project Department of Computer Science The University of Melbourne Parkville, Victoria 3052 Australia Telephone: (03) 344 5229.
    [Show full text]
  • Dependency Injection with Unity
    D EPEN DEPENDENCY INJECTION WITH UNITY Over the years software systems have evolutionarily become more and more patterns & practices D ENCY complex. One of the techniques for dealing with this inherent complexity Proven practices for predictable results of software systems is dependency injection – a design pattern that I allows the removal of hard-coded dependencies and makes it possible to Save time and reduce risk on your NJECT assemble a service by changing dependencies easily, whether at run-time software development projects by or compile-time. It promotes code reuse and loosely-coupled design which incorporating patterns & practices, I leads to more easily maintainable and flexible code. Microsoft’s applied engineering ON guidance that includes both production The guide you are holding in your hands is a primer on using dependency quality source code and documentation. W I injection with Unity – a lightweight extensible dependency injection TH DEPENDENCY INJECTION container built by the Microsoft patterns & practices team. It covers The guidance is designed to help U software development teams: various styles of dependency injection and also additional capabilities N I of Unity container, such as object lifetime management, interception, Make critical design and technology TY and registration by convention. It also discusses the advanced topics of selection decisions by highlighting WITH UNITY enhancing Unity with your custom extensions. the appropriate solution architectures, technologies, and Microsoft products The guide contains plenty of trade-off discussions and tips and tricks for for common scenarios managing your application cross-cutting concerns and making the most out of both dependency injection and Unity. These are accompanied by a Understand the most important Dominic Betts real world example that will help you master the techniques.
    [Show full text]
  • Variable-Arity Generic Interfaces
    Variable-Arity Generic Interfaces T. Stephen Strickland, Richard Cobbe, and Matthias Felleisen College of Computer and Information Science Northeastern University Boston, MA 02115 [email protected] Abstract. Many programming languages provide variable-arity func- tions. Such functions consume a fixed number of required arguments plus an unspecified number of \rest arguments." The C++ standardiza- tion committee has recently lifted this flexibility from terms to types with the adoption of a proposal for variable-arity templates. In this paper we propose an extension of Java with variable-arity interfaces. We present some programming examples that can benefit from variable-arity generic interfaces in Java; a type-safe model of such a language; and a reduction to the core language. 1 Introduction In April of 2007 the C++ standardization committee adopted Gregor and J¨arvi's proposal [1] for variable-length type arguments in class templates, which pre- sented the idea and its implementation but did not include a formal model or soundness proof. As a result, the relevant constructs will appear in the upcom- ing C++09 draft. Demand for this feature is not limited to C++, however. David Hall submitted a request for variable-arity type parameters for classes to Sun in 2005 [2]. For an illustration of the idea, consider the remarks on first-class functions in Scala [3] from the language's homepage. There it says that \every function is a value. Scala provides a lightweight syntax for defining anonymous functions, it supports higher-order functions, it allows functions to be nested, and supports currying." To achieve this integration of objects and closures, Scala's standard library pre-defines ten interfaces (traits) for function types, which we show here in Java-like syntax: interface Function0<Result> { Result apply(); } interface Function1<Arg1,Result> { Result apply(Arg1 a1); } interface Function2<Arg1,Arg2,Result> { Result apply(Arg1 a1, Arg2 a2); } ..
    [Show full text]
  • Design Patterns in Ocaml
    Design Patterns in OCaml Antonio Vicente [email protected] Earl Wagner [email protected] Abstract The GOF Design Patterns book is an important piece of any professional programmer's library. These patterns are generally considered to be an indication of good design and development practices. By giving an implementation of these patterns in OCaml we expected to better understand the importance of OCaml's advanced language features and provide other developers with an implementation of these familiar concepts in order to reduce the effort required to learn this language. As in the case of Smalltalk and Scheme+GLOS, OCaml's higher order features allows for simple elegant implementation of some of the patterns while others were much harder due to the OCaml's restrictive type system. 1 Contents 1 Background and Motivation 3 2 Results and Evaluation 3 3 Lessons Learned and Conclusions 4 4 Creational Patterns 5 4.1 Abstract Factory . 5 4.2 Builder . 6 4.3 Factory Method . 6 4.4 Prototype . 7 4.5 Singleton . 8 5 Structural Patterns 8 5.1 Adapter . 8 5.2 Bridge . 8 5.3 Composite . 8 5.4 Decorator . 9 5.5 Facade . 10 5.6 Flyweight . 10 5.7 Proxy . 10 6 Behavior Patterns 11 6.1 Chain of Responsibility . 11 6.2 Command . 12 6.3 Interpreter . 13 6.4 Iterator . 13 6.5 Mediator . 13 6.6 Memento . 13 6.7 Observer . 13 6.8 State . 14 6.9 Strategy . 15 6.10 Template Method . 15 6.11 Visitor . 15 7 References 18 2 1 Background and Motivation Throughout this course we have seen many examples of methodologies and tools that can be used to reduce the burden of working in a software project.
    [Show full text]
  • Small Hitting-Sets for Tiny Arithmetic Circuits Or: How to Turn Bad Designs Into Good
    Electronic Colloquium on Computational Complexity, Report No. 35 (2017) Small hitting-sets for tiny arithmetic circuits or: How to turn bad designs into good Manindra Agrawal ∗ Michael Forbes† Sumanta Ghosh ‡ Nitin Saxena § Abstract Research in the last decade has shown that to prove lower bounds or to derandomize polynomial identity testing (PIT) for general arithmetic circuits it suffices to solve these questions for restricted circuits. In this work, we study the smallest possibly restricted class of circuits, in particular depth-4 circuits, which would yield such results for general circuits (that is, the complexity class VP). We show that if we can design poly(s)-time hitting-sets for Σ ∧a ΣΠO(log s) circuits of size s, where a = ω(1) is arbitrarily small and the number of variables, or arity n, is O(log s), then we can derandomize blackbox PIT for general circuits in quasipolynomial time. Further, this establishes that either E6⊆#P/poly or that VP6=VNP. We call the former model tiny diagonal depth-4. Note that these are merely polynomials with arity O(log s) and degree ω(log s). In fact, we show that one only needs a poly(s)-time hitting-set against individual-degree a′ = ω(1) polynomials that are computable by a size-s arity-(log s) ΣΠΣ circuit (note: Π fanin may be s). Alternatively, we claim that, to understand VP one only needs to find hitting-sets, for depth-3, that have a small parameterized complexity. Another tiny family of interest is when we restrict the arity n = ω(1) to be arbitrarily small.
    [Show full text]
  • HEDGEHOG: Automatic Verification of Design Patterns in Java
    HEDGEHOG: Automatic Verification of Design Patterns in Java Alex Blewitt I V N E R U S E I T H Y T O H F G E R D I N B U Doctor of Philosophy School of Informatics University of Edinburgh 2006 Abstract Design patterns are widely used by designers and developers for building complex systems in object-oriented programming languages such as Java. However, systems evolve over time, increasing the chance that the pattern in its original form will be broken. To verify that a design pattern has not been broken involves specifying the original intent of the design pattern. Whilst informal descriptions of patterns exist, no formal specifications are available due to differences in implementations between programming languages. This thesis shows that many patterns (implemented in Java) can be verified automatically. Patterns are defined in terms of variants, mini-patterns, and artefacts in a pattern description language called SPINE. These specifications are then processed by HEDGEHOG, an automated proof tool that attempts to prove that Java source code meets these specifications. iii Acknowledgements I am indebted to Alan Bundy who has given me the freedom to work on this thesis whilst at the same time guiding me towards the final production and presentation of these results. I not would have been able to achieve this without Alan’s support through a sometimes difficult, but always busy part of my life. This project, and especially the production of this thesis, would not have been possible without the care and attention that Alan provided. Ian Stark has provided invaluable feedback on all aspects of this thesis, from the low-level technical intricacies of Java’s design patterns through to the high-level structure of the thesis as a whole.
    [Show full text]