functional design

Upload: adam-markham

Post on 06-Apr-2018

229 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/2/2019 Functional Design

    1/16

    A Design Methodology for Functional Programs

    David Wakeling

    School of Engineering and Computer ScienceUniversity of Exeter

    Exeter, EX4 4PTUnited Kingdom

    [email protected]

    Abstract.An increasingly significant weakness of declarative program-ming is that it lacks a design methodology. In this paper, we attempt to

    provide one by showing how methodologies commonly used to developobject-oriented programs might also be used to develop functional ones.This involves mapping from a restricted subset of the diagrammatic no-tation used with these methodologies, the Unified Modeling Language(UML), to the standard lazy functional programming language, Haskell.As an example, we develop a small electronic mail system.

    1 Introduction

    In his position paper to the First Symposium on the Practical Aspects of Declar-ative Languages, Schmidt pointed out that an increasingly significant weaknessof declarative programming is that it lacks a design methodology [19]. In thispaper, we attempt to provide one by showing how methodologies, such as Catal-ysis [7], OPEN [11], and the Rational Unified Process [13], commonly used todevelop object-oriented programs might also be used to develop functional ones.This involves mapping from a restricted subset of the diagrammatic notation

    used with these methodologies, the Unified Modeling Language (UML) [2], tothe standard lazy functional programming language, Haskell [17].

    The remainder of this paper is organised as follows. Section 2 introduces arestricted subset of the UML. Section 3 shows how designs in this restrictedsubset can be mapped to incomplete Haskell programs. Section 4 presents asmall example. Section 5 mentions some related work. Section 6 considers somepossible future work. Section 7 concludes.

    2 The Unified Modeling Language

    The UML is a diagrammatic notation with nine kinds of diagram. This sectionintroduces the three that we will deal with, and then comments briefly on thesix that we will not. Those familiar with the UML should find it easy going;others may need to consult [2].

    W. Taha (Ed.): SAIG 2001, LNCS 2196, pp. 146161, 2001.c Springer-Verlag Berlin Heidelberg 2001

  • 8/2/2019 Functional Design

    2/16

    A Design Methodology for Functional Programs 147

    A

    U

    Un

    1

    .

    .

    .

    Fig. 1. The form of a use case diagram.

    2.1 Use Case Diagrams

    Figure 1 shows the form of a use case diagram. Use case diagrams capturethe functional requirements of the system being developed. The stick personrepresents an actor a user of the system in a particular role, and each ovalrepresents a use case a task to be performed with the aid of the systemthat yields an observable result of value to the actor. A use case diagram isaccompanied by a textual description of the different courses of action possiblein the use case. Both the diagram and the textual description are intended tobe simple enough that all stakeholders in the project can readily agree on whatthey mean.

    2.2 Class Diagrams

    Once the use cases have been determined, a collection of classes can be designedto provide the required functionality. A class describes objects with commonfeatures. Figure 2 shows the form of a class in a class diagram. To start with, a

    CA 1

    iO1

    O...

    A...

    j

    Fig.2. The form of a class in a class diagram.

    rectangle containing the name is all that is needed to represent a class. Later,features are added, and the rectangle is divided into three smaller ones. Thetop one contains the name, C, the middle one any attributes A1, . . . Ai, and the

  • 8/2/2019 Functional Design

    3/16

    148 D. Wakeling

    bottom one any operations O1, . . . Oj . Simplifying somewhat, the syntax of anattribute may be described by the BNF grammar

    visibility name [ multiplicity ] : type

    and that of an operation by the grammar

    visibility name ( { name : type } ) : return-type

    Access to features is controlled by prefixing them with a visibility either +or -, standing for public or private. A public feature can be accessed from anyclass, and a private feature from only its own class. An attribute may also have amultiplicity saying how many there will be. A multiplicity can be written as twonumbers separated by two dots that are the lower and upper limits of a range,with a * standing for zero or more.

    There is an association between two classes if objects of the first knowabout objects of the second. Figure 3 shows the form of an association in aclass diagram. An arrowhead on the association line specifies its navigability in

    C C1 2

    Fig.3. The form of an association in a class diagram.

    a particular direction; that is, that objects of the source class know aboutobjects of the destination class, but not the other way round. An absence ofarrowheads means navigability in both directions. Like attributes, associationscan have both a visibility and a multiplicity.

    2.3 Sequence Diagrams

    After the classes have been designed, sequence diagrams can be drawn to showhow they realise the use cases. Figure 4 shows the form of a sequence diagram.In a sequence diagram, labelled objects are placed at the top of vertical dashedlifelines. A stick person now represents an actor object an instance of theactor from the use case diagram, and a rectangle represents an ordinary object an instance of a class from the class diagram. An object may interact withanother (or itself) by sending a message. A message is represented by an arrowfrom the lifeline of the sender to that of the receiver, labelled with the nameand argument values for one of the receivers operations. Later messages appearfurther down the page, and the lifeline of an object is thickened while it is engagedin interaction. A special message, new, indicates the creation of an object withcertain attribute values. The symbol := can be used to give a name to a valuereturned in response to a message, and this name may then be used in subsequentmessages.

  • 8/2/2019 Functional Design

    4/16

    A Design Methodology for Functional Programs 149

    a : A x : O2 2

    x : O1 1

    o1 11(a ,...a1i )o (a212 ,...a2j )

    Fig.4. The form of a sequence diagram.

    2.4 Other Diagrams

    There are six other diagrams that we do not deal with. An object diagram isan instance of a class diagram that contains no more information, and a col-laboration diagram can be automatically converted to a sequence diagram thatcontains the same information. A state chart diagram describes a state machine

    and the events that cause transitions between states, of which an activity dia-gram is a special case. In our view, these state-based diagrams do not provide anatural way to describe the design of functional programs. A component diagramdescribes the relationships between program components, and a deployment di-agram describes the relationships between these components and processors ordevices. Although a component diagram could map to a script for a configura-tion management system, it is hard to see what more could be done with eitherdiagram.

    3 A Mapping to Haskell

    In this section we present a series of rules for mapping from the restricted sub-set of the UML that we have described to Haskell. Applying these rules to adesign will give an incomplete program because some operations are not fullydescribed. A programmer must then complete the program by adding code forthese operations.

    3.1 Use Case Diagrams

    In the Rational Unified Process, use case diagrams are used to generate testcases [13]. Taking this idea on board, we map use case diagrams to functionsthat can be used for testing. Currently, black box testing is performed interac-tively. Eventually, though, we plan to supplement this with white box testingperformed automatically using QuickCheck [5]. Figure 5 shows the U rule. This

  • 8/2/2019 Functional Design

    5/16

    150 D. Wakeling

    U[[ A U1, . . . U n ]] =module Main_N[[ A ]] whereimport N[[ U1 ]]

    ...import N[[ Un ]]

    main_N[[ A ]] :: IO ()main_N[[ A ]] =

    menu [ ("U1", do_N[[ U1 ]] )...

    , ("Un", do_N[[ Un ]] )]

    Fig.5. The U rule.

    rule produces the function main_N[[ A ]] that allows an actor, A, to choose whichof its associated use cases, U1, . . . U n, is done next. Here and elsewhere, the Nrule maps the name of a UML entity, which may include punctuation, spaces,and line breaks, to a Haskell name, which may not. Each use case will have its

    own module, which must be imported. The menu function for menu selection isdefined in Appendix A.Every use case has some observable result of value in the real world. Figure 6

    shows the D rule. This rule produces the function do_N[[ U ]] that does a use

    D[[ U ]] =do_N[[ U ]] :: IO ()do_N[[ U ]] =

    do { args

  • 8/2/2019 Functional Design

    6/16

    A Design Methodology for Functional Programs 151

    further interactions I1, . . . I n (in that order) then take place. The message Mcan be shown in more detail by writing it as an operation name and arguments

    above an arrow, o(a1,...ai), followed by the label of the receiver, x : C. All

    interactions are assumed to be available in the set interactions .Here, an object of the use case controller (or boundary) class N[[ U ]] iscreated with the constructor N[[ U ]]. This class has an operation run_N[[ U ]]that gets the use case U done. More will be said about the mapping for classesbelow. Again, the prompt and pick auxiliaries for collecting and selecting usecase arguments are defined in Appendix A.

    3.2 Class Diagrams

    A class maps to a module containing a datatype and some functions. Thedatatype has a single constructor with a field for each attribute and navigableassociation. There is a function on values of this datatype for each operation.Figure 7 shows the C rule. This rule produces the module N[[ C ]] for the class

    C[[ C ]] =module N[[ C ]] ( N[[ C ]](..), N[[ O1 ]], N [[ Op ]] ) where

    A[[ C ]]O[[ O1 ]] C O[[ Op ]] C O[[ Op+1 ]] C O[[ Oj ]] Cin which{O1, . . . Op} = public operations(C){Op+1, . . . Oj} = private operations(C)

    Fig.7. The C rule.

    C, using the A rule to produce the datatype, and the O rule to produce thefunctions. The visibility of operations is dealt with directly by including publicones in the export list of the module. The visibility of attributes and navigableassociations is dealt with indirectly by preventing private ones from being namedin patterns outside of the module, although our rules cannot show this.

    Figure 8 shows the A, T, M and B rules. Together, these rules produce adatatype, N[[ C ]], for a class, C. This datatype that has single constructor,N[[ C ]], with a field for each attribute and navigable association. The T ruletranslates an attribute or navigable association to a field type. It uses the M ruleto translate three of the most common multiplicities, and the B rule to translatebasic types.

    Figure 9 shows the O and V rules. Together, these rules produce the functionfor an operation, O, of class C. The type of the function is derived from that ofthe operation, with the receiver class, C, added as a first argument, and theresult type wrapped in an IO type. Adding the receiver class is common in the

  • 8/2/2019 Functional Design

    7/16

    152 D. Wakeling

    A[[ C ]] =data N[[ C ]] = N[[ C ]] T[[ A1 ]] T [[ Ai ]]in which

    {A1, . . . Ai} = attributes and navigable associations(C)

    T[[ v n m : t ]] = M[[ m : t ]]

    M[[ 1..1: t ]] = B[[ t ]]M[[ 0..1: t ]] = ( Maybe B[[ t ]] )M[[ 0..*: t ]] = [ B[[ t ]] ]

    B[[ Void ]] = ()B[[ Integer ]] = Int

    B[[ Boolean ]] = Bool...B[[ other ]] = N[[ other ]]

    Fig.8. The A, T, M and B rules.

    O[[ o(a1 : t1, . . . am : tm) : r ]] C =N[[ o ]] :: N[[ C ]] -> B[[ t1 ]] -> B[[ tm ]] -> IO B[[ r ]]N[[ o ]] this@(N[[ C ]] V[[ A1 ]] V [[ Ai ]] ) a1 . . . am = E[[ IS ]]in which{A1, . . . Ai} = attributes and navigable associations(C)

    o(a1,...am) x : C IS interactions

    V[[ v n m : t ]] = N[[ n ]]

    Fig. 9. The O and V rules.

    implementation of object-oriented programming languages. Wrapping the resulttype allows the function to have a side-effect that contributes to the observableresult of value. These rules produce the left-hand side of a function. The right-hand side is produced from the interaction diagram see below.

    For each use case, U, there must be at least a use case controller (orboundary class),N[[ U ]]. This class has no attributes or navigable associations,and a single operation, run_N[[ U ]], that gets the use case U done, and thenreturns an output string.

  • 8/2/2019 Functional Design

    8/16

    A Design Methodology for Functional Programs 153

    3.3 Interaction Diagrams

    An interaction diagram maps to the right-hand side of the function for an op-eration. Figure 10 shows the E and S rules. If an operation has an interaction

    E[[ I1, . . . I n ]] =do { S[[ I1 ]] ; S [[ In ]] ; return -?- }

    S[[ o(a1,...am) x : C IS ]] =N[[ o ]] N[[ x ]] N[[ a1 ]] N [[ am ]]

    S[[ v := o(a1,...am) x : C IS ]] =N[[ v ]]

  • 8/2/2019 Functional Design

    9/16

    154 D. Wakeling

    1. object O;2. objects directly accessible from O via its attributes;3. objects created by O as a result of receiving M;4. objects passed as arguments ofM.

    As well as describing good object-oriented design, this restriction is one way toensure that an operation maps to a Haskell function with no free variables.

    The final restriction is also on sequence diagrams, where an operation canhave a global side-effect on the state of the world, but not a local side-effecton the state of an object. Global side-effects are allowed because the functionsfor operations have IO types, but local side-effects are disallowed because thefields of a Haskell datatype may not be updated. A change in state can only bebrought about by creating a new object.

    4 An Example

    In this section we develop a small example system to give something of theflavour of our work. This system, called notmail, allows electronic mail to besent and received. Figure 11 shows the design diagram. Quite deliberately, wesay nothing more about this design. Recall, that it is supposed to be simpleenough that all stakeholders in the notmail project can readily agree on whatit means.

    Our mapping can now be applied to the design to produce an incompleteprogram. Figure 12 shows the code for the use case diagram. This testing codedoes not need to be completed by the programmer. Figure 13 shows the codefor the Send and Receive classes. The run_ operations of these two use casecontroller classes both have interaction diagrams, and the programmer needonly complete the imports (which could, in fact, have been done by a moresophisticated mapping) and the return type. Some code that they might writeto do so is shown underlined. Figure 14 shows the code for the Box and Messageclasses. None of the operations of these classes has an interaction diagram, andso the programmer must complete them on their own. Again, some code thatthey might write to do so is shown underlined. This code assumes a directoryboxes that contains a mail box file for each user. The add and remove operationsappend and delete messages from this file, where a message begins with a linethat begins with the word From:. A trick is used in code for the removeoperation: comparing the length of the mail box file with 0 ensures that theentire file is read into memory, so that the same file can then be written withoutcorruption.

    As even this small example shows, the incomplete code produced by ourmapping looks much more imperative that functional. The advantage of this isthat it is easy to relate diagrams and code, and so to complete the program. Thedisadvantage of this is that the elegance and power of functional programmingseem to go unused. However, it is worth noting that all of this elegance andpower can still be used to complete the program. In this example, unfortunately,this is not obvious because the code to complete the program simply deals with

  • 8/2/2019 Functional Design

    10/16

    A Design Methodology for Functional Programs 155

    Fig. 11. The notmail design diagram.

  • 8/2/2019 Functional Design

    11/16

    156 D. Wakeling

    module Main_User where

    import Send

    import Receive

    main_User :: IO ()

    main_User =

    menu [ ("Send", do_Send)

    , ("Receive", do_Receive)

    ]

    do_Send :: IO ()

    do_Send =

    do { args

  • 8/2/2019 Functional Design

    12/16

    A Design Methodology for Functional Programs 157

    module Send (Send(..), run_Send) where

    import Box

    import Message

    data Send = Send

    send :: Send -> String -> String -> String -> IO String

    send this@(Send) from to text =

    do { let b = Box to

    ; let m = Message from text

    ; a d d b m

    ; return "OK"

    }

    module Receive (Receive(..), run_Receive) whereimport Box

    import Message

    data Receive = Receive

    receive :: Receive -> String -> IO String

    receive this@(Receive) for =

    do { let b = Box for

    ; m

  • 8/2/2019 Functional Design

    13/16

    158 D. Wakeling

    module Box (Box(..), add, remove) where

    import List

    import Message

    data Box = Box String

    add :: Box -> Message -> IO ()

    add this@(Box owner) mesg =

    do { s IO Message

    remove this@(Box owner) =do { cs IO String

    format this@(Message from text) =

    do { return ("From: " ++ from ++ "\n" ++ text ++ "\n")

    }

    Fig. 14. The completed code for the Box and Message classes.

    that what we have described here. Typically, they support not only forwardengineering (the production of incomplete code from diagrams), but also reverseengineering (the production of diagrams from code) and round-trip engineering(the reproduction of diagrams from completed code). These tools, of course,

  • 8/2/2019 Functional Design

    14/16

    A Design Methodology for Functional Programs 159

    work with object-oriented programming languages like C++ and Java, ratherthan with functional programming languages like Haskell.

    6 Future Work

    There are a number of ways in which this work could be continued.As we have shown, even with a restricted subset of the UML it is possible to

    develop interesting programs in Haskell. Clearly, though, we need to try more de-signs that use more of the UML, and in doing so to further develop our mappingto Haskell. Our hope is that certain design patterns for functional programs willthen emerge in the same way that they have done for object-oriented ones [ 10].

    Currently, we map designs to incomplete programs by hand. Eventually, of

    course, we want a CASE tool to do this automatically. Our current plan is todevelop one from scratch, rather than to try and interface with an existing one.Clearly, this will be more work, but a Haskell aware tool will also be able toprovide much better support for forward, reverse, and round-trip engineering.

    7 Conclusions

    In this paper, we have shown how methodologies commonly used to develop

    object-oriented programs might also be used to develop functional ones. Thisinvolved mapping from a restricted subset of the diagrammatic notation usedwith these methodologies, the Unified Modeling Language (UML), to the stan-dard lazy functional programming language, Haskell. As our example electronicmail system showed, there is some awkwardness in converting from the object-oriented to the functional paradigm. However, we a prepared to accept this inexchange for ready access to the growing number of books, tools, and practition-ers that support the UML.

    Acknowledgements. An early draft of this paper was written while the au-thor was on study leave at the University of York, working in the Department ofComputer Science. The author would like to thank the Department for its hos-pitality, and Professor Colin Runciman and the Programming Languages andSystems Group for some lively discussions. The author would also like to thankthe anonymous referees for some useful comments and suggestions.

    This research was funded by the EPSRC under grant number GR/N20041Formal Description of the Software Life Cycle for Functional Programming Lan-

    guages (Feasibility Study).

    References

    1. R. S. Bird. Transformational programming and the paragraph problem. Scienceof Computer Programming, 6(2):15989, March 1986.

  • 8/2/2019 Functional Design

    15/16

    160 D. Wakeling

    2. G. Booch, J. Rumbaugh, and I. Jacobson. The Unified Modeling Language UserGuide. Addison Wesley, July 1998. ISBN 0201571684.

    3. P. Borba and S. Meira. From VDM specifications to functional prototypes. Journalof Systems and Software, 21(3):26778, June 1993.

    4. R. M. Burstall and J. Darlington. A transformation system for developing recursiveprograms. Journal of the Association of Computing Machinery, 24(1):4467, 1977.

    5. K. Claessen and J. Hughes. QuickCheck: A lightweight tool for random testing ofHaskell programs. In Proceedings of the International Conference on FunctionalProgramming, pages 268279. ACM Press, September 2000. ISBN 1581132026.

    6. A. Diller. Z: An Introduction to Formal Methods (Second Edition). John Wiley,1994. ISBN 0471939730.

    7. D. F. DSouza and A. C. Wills. Objects, Components and Frameworks with UML:The Catalysis Approach. Addison Wesley, October 1998. ISBN 0201310120.

    8. A. Evans and S. Kent. Core meta-modelling semantics of UML: The pUML ap-proach. In Proceedings of the Second International Conference on The UnifiedModeling Language, pages 140155. Springer Verlag, October 1999. LNCS 1723.

    9. M. Fowler and K. Scott. UML Distilled (Second Edition). Addison Wesley, October1999. ISBN 020165783X.

    10. E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns. AddisonWesley, December 1994. ISBN 0201633612.

    11. B. Henderson-Sellers and B. Unhelkar. OPEN Modeling with UML. Addison Wes-ley, June 2000. ISBN 0201675129.

    12. M. Johnson and P. Sanders. From Z specifications to functional implementations.

    In J. E. Nicholls, editor,Proceedings of the 4th Annual Z User Meeting

    , pages86112. Springer Verlag, 1990.13. P. Krutchen. The Rational Unified Process: An Introduction (Second Edition).

    Addison Wesley, April 2000. ISBN 201707101.14. Karl J. Lieberherr and Ian Holland. Assuring good style for object-oriented pro-

    grams. IEEE Software, pages 3848, September 1989.15. Object Management Group. OMG Unified Modeling Language Specification (Ver-

    sion 1.3), October 2000. See http://www.omg.org.16. G. ONeill. VDM - automatic translation of VDM specifications into Standard ML

    programs (short note). Computer Journal, 35(6):623624, 1992.

    17. S. Peyton Jones and J. Hughes. The Haskell 98 Language Report, February 1999.18. Rational. Rational Rose, April 2001. See http://www.rational.com .19. D. A. Schmidt. A return to elegance: The reapplication of declarative notation

    to software design. In Proceedings of the Conference on the Practical Applicationof Declarative Languages, pages 360364. Springer Verlag, January 1999. LNCS1551, ISBN 3540655271.

    20. L. B. Sherrell and D. L. Carver. FunZ: An intermediate specification language.Computer Journal, 38(3):193206, 1995.

    21. M. Shields and S. L .Peyton Jones. Object-oriented sytle overloading for Haskell,July 2001. Submitted to the Workshop on Multi-Language Infrastructure and

    Interoperability.22. D. A. Turner. Functional programs as executable specifications. In C. A. R. Hoare

    and J. Shepherdson, editors, Mathematical Logic and Programming Languages,pages 2954. Prentice-Hall, 1985. ISBN 0135614651.

  • 8/2/2019 Functional Design

    16/16

    A Design Methodology for Functional Programs 161

    A Auxiliary Function Definitions

    This appendix defines some auxiliary functions used in the main text.

    menu :: [ (String, IO ()) ] -> IO () menu options =

    do { mapM option (zip [1..] ss)

    ; n IO ()

    option (n,s) =

    putStrLn (show n ++ ") " ++ s)

    prompt :: [ String ] -> IO [ (String, String) ]

    prompt =

    mapM ask

    ask :: String -> IO (String, String)

    ask question =

    do { putStr question

    ; putStr "? "

    ; answer [ (String, String) ] -> String

    pick x ((n,v):nvs)

    | x == n = v

    | otherwise = pick x nvs