Academia.eduAcademia.edu

Modern Declarative Programming: A Swift Conclusion

Declarative programming can be described as a paradigm, a style of building the structure and elements of computer programs, that expresses the logic of a computation without describing its control flow. Many languages applying this style attempt to minimize or eliminate side effects by describing what the program should accomplish in terms of the problem domain, rather than describing how to go about accomplishing it as a sequence of the programming language primitives.

Modern Declarative Programming - A Swift Conclusion Declarative programming can be described as a paradigm, a style of building the structure and elements of computer programs, that expresses the logic of a computation without describing its control flow. Many languages applying this style attempt to minimize or eliminate side effects by describing what the program should accomplish in terms of the problem domain, rather than describing how to go about accomplishing it as a sequence of the programming language primitives. Several styles of programming, or forms of programming, exist that represent a declarative solution. Constraint programming: Relations between variables are stated in the form of constraints, specifying the properties of a solution to be found. Domain Specific Languages: This is a computer language specialized to a particular application domain. This can take the form of either internal or external DSL's and therefor implemented with a wide variety of computing languages. Functional programming: This attempts to minimize of eliminate side effects. Hybrid languages: These specify dependencies in a declarative fashion, but include imperative lists of actions to take as well. Logic programming: The specifics of how queries are answered is up to the implementation and its theorem prover. It typically takes the form of some sort of unification. NOTE: Counter to declarative programming is imperative programming, where algorithms are implemented in terms of explicit steps. This is important to note as any declarative solution must avoid this implementation in their code for it to be truly declarative. Introduction to Swift Swift is a new programming language for IOS and OS X introduced at Apples WWDC 2014 in June.. It was designed/created by Chris Lattner and has already been dubbed with many comparisons. Such as 'Objective-C without C' and 'Rusty Objective-C#++', it most certainly adopts the readability of Objective-C's named parameters and the power of Objective-C's dynamic object model. Iy also provides seamless access to existing Cocoa frameworks and mix-and-match interoperability with Objective-C code. It is a Strongly typed language that adopts safe programming patterns and adds modern features to make programming easier, more flexible, and more fun. It is a industrial-quality systems programming language that is as expressive and enjoyable as a scripting language. Some important features that allow for the ability to write in declarative programming style are the following: Type inference & explicitly mutable/immutable variables Generics Operator Overloading Classes/Reference Types (objects) & Value Types (structs, enums) Tuples Type extension Optional Types Pattern Matching Declaring the problem Looking at the different approaches to declarative programming, and the different uses it has, I decided to invest the time in looking into Domain Specific Languages (DSL's) and whether or not it was possible to write a DSL in swift using a declarative syntax. The project of demonstrating this particular example in no way represents a generalised result across the other mediums of declarative programming. A DSL is a computer programming language of limited expressiveness focused on a particular domain. In particular a DSL focuses 'limited expressiveness', where a general purpose programming language provides lots of capabilities: supporting varied data, control, and abstraction structures. All of this is useful but makes it harder to learn and use. A DSL supports a bare minimum of features needed to support its domain. In other words, you can use DSL's to help build a system, or to give your system the definition of what you want the results to be, but not how you wish to gain these results. Reading through Martin Fowlers 'Domain Specific Languages' he gave a clear distinguishment of the difference between internal and external DSL's, and from this point onwards and reference to DSL's (unless specified) will be to internal DSL's. Specifically a internal DSL is a particular way of using a general purpose language. A script in an internal DSL is valid code in its general purpose language, but only uses a subset of the languages features in a particular style to handle one small aspect of the overall system. The result should have the feel of a custom language, rather than its host language. It is with this concept in mind that we seek to write code in Swift to discover the limits of how we can create an internal DSL, or in other words, does swift allow for declarative programming, or enough declarative programming to gain a result similar to examples from other languages such as Ruby. The task I have created is to generate a solution to a problem. The problem domain is a company desiring to move to a automated time sheet system, allowing staff to sign in and out easily, and to lessen the workload for payroll. This system would need allowances for managers to review and adjust the entries when necessary. However the commissioners of this problem are unsure how they would like to implement the automated timesheet system, so ideally they want something to be created that shows what the system will do, rather than how it will do it. This is a similar problem that Martin Flowler deals with, and the solution is present in the same manner as his in showing the process in making code, and syntax, as declarative as possible, and looking more like a custom syntax then the actual language we are programming with. Below is a state diagram representing the problem, and showing how this problem can be solved using a state machine in the first place! The below example is written with Java: Event signOut = new Event("SGNOUT"); Event signIn = new Event("SGNIN"); Event approve = new Event("APPRV"); Command Command Command Command flag = new Command("FLAG"); record = new Command("RCRD"); adjustTimeStamp = new Command("ADJTS"); finalize = new Command("FNLZ"); State signedIn = new State("Signed In"); State signedOut = new State("Signed Out"); State pendingApproval = new State("Pending Approval"); StateMachine machine = new StateMachine(signedOut); signedOut.addTransition(signIn, signedIn); signedOut.addAction(record); signedOut.addAction(adjustTimeStamp); signedIn.addTransition(signOut, pendingApproval); signedIn.addAction(record); signedIn.addAction(flag); signedIn.addAction(adjustTimeStamp); pendingApproval.addTransition(approve, signedOut); pendingApproval.addAction(finalize); This does not really represent a Declarative State Machine DSL, in fact it is hard to tell it is a configuration code for a state machine. The below code is written in JRuby, this is a far more declarative solution to the previous Java but is still considered an internal DSL. event :signOut, "SGNOUT" event :signIn, "SGNIN" event :approve, "APPRV" command command command command :flag, "FLAG" :record, "RCRD" :adjustTimeStamp, "ADJTS" :finalize, "FNLZ" state :signedIn do actions :record, :adjustTimeStamp, :flag transitions :signOut -> :pendingApproval end state :signedOut do actions :record, :adjustTimeStamp transitions :signIn -> :signedIn end state :pendingApproval do actions :finalize transitions :approve -> :signedOut end So we have something here we like the looks of. It is simple, readable, and is of limited expressiveness. This Looks far more like a DSL describing the configuration of a state machine now. A Swift Solution So we have seen a solution using Java, and even JRuby within Java. These solutions increasingly proving to be declarative solutions to our problem with creating an automated Timesheet system. The next step is to see if we can replicate these solutions using Swift and its syntax. Then if we can prove this we will want to progress further and see to what limit we can write a DSL in a declarative way, that is, show what we want the solution to be, rather than how we get the solution. There are a few ways to approach this, the easiest is with simple object method calls, which will turn out something like the original java code above. This is what I began with to create a base from which I could begin to remove as many side effects and limit its expressiveness to the point of either a similar solution that JRuby can achieve, or perhaps a great declaractive answer. let let let let arrive, depart, approve: Event record, finalise, flag, adjustTimeStamp: TimeSheetActions signedIn, signedOut, pendingApproval: State stateMachine: StateMachine init() { arrive = Event() ; depart = Event() approve = Event() record = TimeSheetActions() ; finalize = TimeSheetActions() flag = TimeSheetActions() ; adjustTimeStamp = TimeSheetActions() = State(actions: [record, flag, adjustTimeStamp]) signedIn signedOut = State(actions: [record, adjustTimeStamp]) pendingApproval = State(actions: [finalize]) let signOn = Transition(event: arrive, state: signedIn) let signOff = Transition(event: depart, state: pendingApproval) let approval = Transition(event: approve, state: signedOut) signedIn.addTransition(signOff) signedOut.addTransition(signOn) pendingApproval.addTransition(approval) } stateMachine = StateMachine(states: [signedIn, signedOut, pendingApproval]) stateMachine.currentState = signedOut This looks a little better than the java code, but it still isn't enough, it still does not look like a state machine. Each of the objects need to be declared and instantiated, which is not a very declarative at all. In this solution each of the states are instantiated with a set of actions that it can perform. Then the transitions are declared and instantiated using the events and states previously instantiated. Then these transitions are added to the states, with the states finally being added into the state machine, while also setting the initial state for it to start in. To try and create a more declarative solution we must try to remove the object declarations. I decided to pursue the use of enumeration's, and see how easily Swift allows you to implement them, and how powerful they are within this language. One of the reasons for choosing this method is that you can very easily set predefined values and constants using them. With Swift what this allows is to pass in types of enums given a set of value Types (i.e.. String, Int, enum) and distinguish them from any other set of values. In the instance below you can see the enum 'Config' and how it has set up the particular value types for each case. enum Config { case Transition(Config, Config, Config) case State(String, Config) case Event(String) case Actions([String]) } let stateMachine: StateMachine init() { stateMachine = StateMachine(StateMachineConfiguration: [ Config.Transition(Config.State("signedIn", Config.State("pendingApproval", Config.Event("signOut")), Config.Transition(Config.State("signedOut", Config.State("signedIn", Config.Event("signIn")), Config.Transition(Config.State("pendingApproval", Config.State("signedOut", Config.Event("approve")) ]) } Config.Actions(["record", "flag", "adjustTimeStamp"])), Config.Actions(["finalise"])), Config.Actions(["record", "finalise", "adjustTimeStamp"])) Config.Actions([])), Config.Actions([])), Config.Actions([])), This use of enums have allowed us to create what looks like a custom syntax for configuration code. As you can see it is very readable, and looks much more like a DSL. However this is only the front end of the DSL, this is the language we have created to allow for very easy instantiation of a StateMachine. Below is some of the code that reads in the information passed into the StateMachine, and uses the enum to instantiate and create the actual objects from simple user input data. init(StateMachineConfiguration: [Config]) { for transition in StateMachineConfiguration { switch transition{ case .Transition(let startState, let endState, let event): var startName, endName, eventName: String switch startState{ case .State(let stateName, let Actions): self.addState(startState) startName = stateName } switch endState{ case .State(let stateName, let Actions): self.addState(endState) endName = stateName } switch event { case .Event(let eventID): var newEvent = true eventName = eventID for e in events { if eventID == e.IDcode { newEvent = false} } if newEvent { events.append(Event(IDcode: eventID)) } } } } } self.getState(startName)!.setTransition(self.getEvent(eventName)!, state: self.getState(endName)!) In the above code you can see how the use of the enums has allowed us to separate easily the information provided in the initial configuration and then instantiate the objects. This particular block of code also checks to see if any repeat data has been put in so it does not create multiples of the objects. This method of using the enums has allowed us to write a custom syntax for our DSL, and a 'fake' parsing of the data to generate the code. For both users and programmers it is easy to read the syntax, and write in it, with very little side effects present. So this solution feels a little bit better then our original swift attempt. However it still does not read very well. Using the naming schemes and parameter syntax that swift enforces we can cleverly set out or program to maximize its usefulness. let let let let stateMachine: StateMachine signedIn = State() signedOut = State() pendingApproval = State() init() { } signedIn.onEvent("depart", transitionTo: pendingApproval, withActions: "record", "flag", "adjustTimeStamp" ) signedOut.onEvent("arrive", transitionTo: signedIn, withActions: "record", "adjustTimeStamp" ) pendingApproval.onEvent("approve", transitionTo: signedOut, withActions: "finalize" ) stateMachine = StateMachine(States: signedIn, signedOut, pendingApproval) In comparison to the JRuby code further above, we can see that the syntax we have created is quite different, though still very readable. In fact by using the enforced syntax of swift we can create something quite declarative, this was also accomplished without the use of anything fancy like enums or structs. The only disadvantage to this particular solution is that some variables had to be declared prior to initialization. Conclusion I have shown various solutions for the problem domain of an automated timesheet system using a Domain Specific Language, and how Swift has the capabilities to write a relatively declarative solution to this problem. In particular I presented a comparison point with Java and JRuby, and believe that without writing an actual parser I have demonstrated that Swift is able to be utilized as a declarative language while writing a DSL. This however is only one example of writing declarative code and should in no way mean that Swift can remain a declarative language writing in other declarative styles, as mentioned at the beginning of this article. I am interested to see how far swift can go in writing with other declarative mediums to tackle various problems, as this is only one problem that has been discussed here. In general I would describe swift as a very modern and versatile language that has a lot of potential. References Martin Fowler, Rebecca Parsons. 2011. Domain Specific Languages. Apple's Introduction to the Swift Programming Language Wikipedia's description of Declarative programming (it's mostly correct) Swift Tutorial's