COMP1100/1130 [2020 S1]:
PROGRAMMING AS PROBLEM SOLVING Research School of Computer Science
Menu
menu
» Labs » Week 4: Cabal and CodeWorld
In this lab, we will meet the Cabal package manager, which helps us work on projects with dependencies between multiple Qles. We will then program with the codeworld-api library, which provides us with types and functions for drawing and transforming various shapes.
Table of Contents
Pre-Lab Checklist Learning Outcomes Cabal build tool CodeWorld API Extensions
Pre-lab Checklist
You should be comfortable using command line arguments in the terminal, as in the Week 1 lab.
You should be comfortable using Git Integration with VSCodium, as in the Week 2 lab.
You should be able to read type signatures of functions in Haskell.
You should be comfortable with writing function deQnitions, as in the Week 3 lab.
Learning Outcomes
Read external documentation and understand how to use functions based on the type signature and description.
Use the cabal package manager to maintain modules with internal dependencies. Use the codeworld-api library to draw shapes based on requirements.
Gain introductory understanding in using lists.
Getting Started
1. Go to the project’s dashboard under the Project tab and click on the Fork button. The Gitlab url for this lab is https://gitlab.cecs.anu.edu.au/comp1100/2020s1studentQles/lab04
2. You will be asked where to fork the repository. Click on the user or group to where you’d like to add the forked project. You should see your own name here. Once you have successfully forked a project, you will be redirected to a new webpage where you will notice your name before > project-name. This means you are now the owner of this repository. The url in your browser should re^ect the change: https://gitlab.cecs.anu.edu.au/uXXXXXXX/Lab04
3. Finally, clone your forked repository to your computer according to the instructions in the Week 2 Lab.
Cabal build tool
For this lab, we are using cabal: a build tool for Haskell that avoids the need to build all of the pieces of your project separately. The comp1100-lab04.cabal Qle lists all the requirements for the project. You do not need to understand how this Qle is written. In your own time, if you are interested feel free to explore the Qle with the documentation here.
There are several useful commands for cabal:
Cabal commands are executed in Terminal using Bash (not in ghci).
cabal v2-build
Compiles all of the necessary Haskell Qles needed for the project.
cabal v2-run
Runs the executables, compiling Qles as necessary. In this case, it will run Main.hs by
default as listed in the .cabal Qle. cabal v2-repl
REPL stands for Read-Eval-Print Loop. This will launch the GHCi interpreter and load the main program.
Modules
This project has a framework that divides the functionality into three primary Qles in the src folder: Model.hs, View.hs, and Controller.hs. This partitioning of functionality is called the Model-View-Controller (MVC) software architecture. It is a common pattern used in implementing graphical user interfaces (GUI).
The reason that the program is broken up like this is to increase readability and maintainability (so that you can Qnd and Qx problems more easily) by grouping related functions together. Moreover, there may be some functions in one module that other modules do not need. With modularisation, you can control what is exposed to other modules and what you want to use when importing.
Model
The Model deQnes the abstract problem domain, independently of the user interface. The Model.hs has been left empty in this lab since we are only using types that are already deQned in the codeworld-api.
View
The View deQnes how we render the model for presentation to the user, in graphical form. In this lab, we will be using codeworld-api functions for drawing shapes. All the functions in this lab will be implemented in this module View.hs.
Controller
The Controller implements the GUI-based mouse and keyboard input functionality of the application, allowing users to control the application.
CodeWorld API
This week we will be using the CodeWorld API (Application Programming Interface) to draw graphical pictures in a browser window. The API helps you easily draw pictures to the screen by writing Haskell code, using shapes, colours and transformations.
Let us begin by understanding the given lines of code.
Double click on the Qle Lab04/src/View.hs. You should see code as follows:
This line imports the functions deQned by the CodeWorld API:
import CodeWorld
The next few lines declare a variable with name myPicture of type Picture (“::” is
pronounced “has type”), and deQnes its value to be blank:
You can think of myPicture as a function that takes no input, and returns something of type Picture. The idea of a function taking no input might seem strange, but just as we can have functions that take one input, f(x) = 2x or functions that take two inputs, f(x, y) = x + y, we can have a function that takes no inputs, f() = 3. Note how unlike functions in previous labs, there is no variable describing the input.
In this lab, we will focus on reading the documentation and using it to write functions. So, at any time during this lab (or later), feel free to refer to the documentation if you are not sure of how a function works. We suggest you open the documentation on a separate tab in your browser for ease of use.
The Picture type and the value blank were imported from the CodeWorld API library. They are not part of the standard Haskell Prelude that deQnes the standard types such as Int, Double etc. So the import statement is necessary before you can use any of these functions.
Double click on the Qle Lab04/src/Main.hs. You should see code as follows:
This imports the functions deQned in the local View module:
import View
Before we explain the following lines
let’s have a look at what the functions in this module can achieve.
A blank screen
Make sure you are in the top-level directory. In the terminal, execute cabal v2-run:
The content of this lab will be very useful for Assignment 1 Part B, and for Assignment 2.
You will execute these cabal commands in the top-level directory of your project, the directory containing the .cabal Qle (i.e., the directory you are in when you clone/launch the VSCodium Terminal tool for your project). You can check the presence of .cabal Qle by using the bash command ls we learnt in week 1.
This lab does not use Model or Controller since all the functionalities can be captured with the View module (as we will only be drawing pictures directly to the screen, and not reading any input from the keyboard or mouse). The information in this section is intended only to help guide you through Assignment 1 Part B, which does use this architecture.
module View where import CodeWorld
myPicture :: Picture myPicture = blank
myPicture :: Picture myPicture = blank
Documentation for CodeWorld can be found at
https://hackage.haskell.org/package/codeworld-api- 0.4.0/docs/CodeWorld.html.
module Main where import CodeWorld
import View
main :: IO ()
main = drawingOf myPicture
main :: IO ()
main = drawingOf myPicture
$ cabal v2-run
Resolving dependencies…
Configuring comp1100-lab04-1.0…
Preprocessing executable ‘Main’ for comp1100-lab04-1.0.. Building executable ‘Main’ for comp1100-lab04-1.0..
[1 of 4] Compiling Controller
[2 of 4] Compiling Model
[3 of 4] Compiling View
[4 of 4] Compiling Main
Linking dist/build/Main/Main … Running Main…
Open me on http://127.0.0.1:3000/
( src/Controller.hs, dist/build/Main/Ma ( src/Model.hs, dist/build/Main/Main-tm ( src/View.hs, dist/build/Main/Main-tmp ( src/Main.hs, dist/build/Main/Main-tmp
in-t
p/Mo
/Vie
/Mai
What do you need to know about IO ()? I/O (Input/Output) allows you to write programs that have input and output while they run. The type (), also known as the unit type, contains one element only, and here means only that the function is not intended to return anything useful, but causes side-effects (in this case, drawing pictures to the screen).
The function drawingOf takes a Picture as an argument and performs an output action but does not return anything useful.
Drawing a shape
Drawing a blank does not seem very useful. Let us draw something more interesting. Before we get started, it may be useful to draw a grid.
Exercise 1 Coordinate Plane
Modify the myPicture function so it returns a coordinate plane.
You can use the CodeWorld documentation to Qnd the appropriate function to draw such
a plane. In this case, we will guide you towards the function required.
Once you have navigated to the webpage with the documentation, we can explore the Pictures section since we are looking to return an output of the type Picture. In this section, we can see that the only function that meets our requirements would be coordinatePlane (scroll down the page a little to Qnd it).
coordinatePlane :: HasCallStack => Picture
Feel free to ignore the HasCallStack term, which you will also Qnd in any function of the type Picture. For anyone interested, => is called the typeclass constraint, a concept we will explore later in this course.
Ignoring those details, we can read the type signature as follows:
coordinatePlane :: Picture
Now, we can modify the drawing by changing the function myPicture:
myPicture = coordinatePlane In order to test the function:
If you are still running the program displaying a blank screen, click on the Terminal and press Ctrl+c. This will kill the process that was running the previous I/O action. You can then reload the module using cabal v2-run.
If you had already closed the program earlier, you can follow the instructions provided earlier to draw the blank screen.
Submission required: Exercise 1 Coordinate Plane
What is a coordinate plane? This plane combines two axes:
1. The Qrst axis line is horizontal, with positive numbers to the right and negative numbers to the left. You can use this axis to describe how far left or right to draw a picture.
2. The second axis line is vertical, with positive numbers on top, and negative numbers on the bottom. You use this axis describe how far up or down to draw a picture.
You might also have heard of this grid by the name of a cartesian plane.
Exercise 2 Rectangle
Now that we have a grid, lets draw a shape. We will start with a rectangle. Draw a rectangle with a width of 2 units and height of 2 units.
You can use the appropriately named rectangle function with the type signature: rectangle :: HasCallStack => Double -> Double -> Picture
Again, ignore the HasCallStack term, so we can read the type signature as follows:
The function should have the following type signature:
myRectangle :: Picture
Accessing cabal interpreter session
While cabal v2-run conQgures, builds and runs the executable, it may be useful to make the modules available in an interpreter session by using cabal v2-repl. Again, you need to be in the top-level directory to execute the cabal command.
In this session, you will be able to try out your existing code and also, test built-in functions in the loaded modules. It may also be useful to load modules outside the project and experiment with functions together with the already available modules.
Try out the following.
Feel free to use cabal v2-repl throughout the lab. Exercise 3 Combining shapes
You can use the (&) function to combine the pictures: the coordinate plane and rectangle. which you can read as “and” or “in front of”.
Rewrite myPicture function so the output of the function looks like the following:
Hint: You can use the (&) function as follows. It can only combine two pictures at a time:
combinedPicture = picture1 & picture2
In this case, picture1 will be in front of picture2.
Test your function on your browser by following the steps in Exercise 1.
Submission required: Exercise 3 Rectangle on coordinate plane Exercise 4 Make it solid
So you’ve just drawn an outline of a rectangle. What about a solid rectangle? Or a solid circle? Refer back to the CodeWorld API documentation if you are stuck.
Submission required: Exercise 4 Solid Rectangle Transforming shapes
In CodeWorld, a transformation is a sort of function whose inputs are the original picture, along with a description of how to change the picture (colouring, moving, stretching etc.). The transformation produces a new picture that is like the original except for that change. There are a few different transformations such as colouring, translation, rotation, dilation, and scaling.
Exercise 5 Translation
The translated function can change the position of a picture within the coordinate plane. The function takes as arguments the original picture and two distances to move the picture, and returns a new picture with the same content as the original, but shifted either horizontally, or vertically, or both. Check the documentation to Qnd out the type signature of the function and learn its usage.
Submission required: Exercise 5 Translated Rectangle Exercise 6 Colouring
The coloured function modiQes the colour of a picture. The function takes as arguments the original picture and an input of type Colour. The output is a new picture with a colour depending on the input of the function. Check the documentation to Qnd out the type signature of the function and learn its usage.
Submission required: Exercise 6 Coloured Rectangle Exercise 7 Create a logo
Let’s use these tools to design a cool logo. This is deQnitely not the logo of a big tech company!
Your task is to create the same logo using the tools that we have learnt.
Call the function colouredSquares. You will need to use the colours blue, yellow, green and orange.
Submission required: Exercise 7 Logo Exercise 8 Rotation
The rotated function takes two arguments: the original picture, and an angle to rotate it. With this transformation, the result is a new picture that has been rotated but retains all other features of the original picture.
For more transformations, you can check out the scaled and dilated functions along with their usage on CodeWorld API documentation. Try to use these transformations on the drawings we have already generated.
Submission required: Exercise 8 Rotated Logo Exercise 9 Drawing Lambda
Custom shapes (lines and polygons) can be implemented using built-in functions in CodeWorld. We can use coordinates to deQne pictures composed of a sequence of line segments.
For example, try the following:
simpleLambda = coordinatePlane & polyline [(3,7), (7,1), (5, 4), (3,1)] How does polyLine work?
If we look at the type deQnition for polyLine (ignoring HasCallStack) in the codeworld documentation:
polyLine :: [Point] -> Picture
What is a Point? If we click on Point in the documentation, we discover:
type Point = (Double, Double)
We have seen tuples before in Lab03. (Check them out there if you don’t remember.)
Point is simply a pair of Doubles that represent a location in the coordinate plane. This means that the function polyLine takes a list of these Points and draws line segments in between them.
lambda = coordinatePlane & polyline [(3,6), (7,0), (5, 3), (3,0)] & polyline
Your Qnal task is to write a function solidLambda that creates a drawing as below:
Submission required: Exercise 9 Drawing Solid Lambda.
Exercise 10 Scheduling and Payment System [COMP1100 Optional, COMP1130 Compulsory]
Click on http://127.0.0.1:3000/ to open your Web browser at that link. What you see should not be too surprising, since myPicture was deQned to be blank.
Based on this, we can re-visit
So, main is the name of a function that invokes a drawingOf function. If we explore the CodeWorld documentation, we Qnd the following:
main :: IO ()
main = drawingOf myPicture
drawingOf
:: Picture –The picture to show on the screen (in this case, myPicture). -> IO ()
— Draws a Picture.
Note: You are more than welcome to explore IO in your own time, but we will not invest time on this topic during this course. The only functions that you will be expected to write in this course will always be side-effect free.
Linking dist/build/Main/Main … Running Main…
Open me on http://127.0.0.1:3000/ ^C
$ cabal v2-run
Preprocessing executable ‘Main’ for comp1100-lab04-0.1.0.. Building executable ‘Main’ for comp1100-lab04-0.1.0.. Running Main…
Open me on http://127.0.0.1:3000/
rectangle :: Double -> Double -> Picture
Write a function named myRectangle that returns a rectangle of width and height of 2 units.
$ cabal v2-repl
Preprocessing executable ‘Main’ for comp1100-lab04-0.1.0.. GHCi, version 8.6.5: http://www.haskell.org/ghc/ 😕 for help
[1 of 4] Compiling Controller [2 of 4] Compiling Model
[3 of 4] Compiling View
[4 of 4] Compiling Main
Ok, four modules loaded. *View>
( src/Controller.hs, interpreted ) ( src/Model.hs, interpreted )
( src/View.hs, interpreted )
( src/Main.hs, interpreted )
You will Qnd using cabal v2-repl very helpful for debugging code both here and in the assignment.
*View> drawingOf (thickRectangle 2 10 6) Open me on http://127.0.0.1:3000/ Program is starting…
Write a new function myRectangle’ that returns a solid rectangle with the same dimensions as myRectangle.
Modify your myRectangle’ function to move the solid rectangle 3 units to the right and 2 units down.
Write a colouredRectangle function that does not take any input but returns a blue myRectangle’.
Remember that rotated function needs the input angle in radians and will turn the picture anticlockwise.
Modify colouredSquares so the function now returns the shape but rotated 45 degrees.
In this exercise, we will be using lists, which were introduced in the lectures. We will only be using them to draw simple shapes, so you are not required to know how to write functions that manipulate lists yet – we will be exploring this in more detail next week.
Modify the deQnition of myPicture in the Qle so we can now visualize the drawing in the browser.
You are required to submit your attempts at the exercises above in order to receive full participation marks for this lab. You have until the start of your next lab to do this. You should submit by committing and pushing all your changes to your Gitlab repository.
These exercises should only be attempted after completing all the exercises above. Feel free to do this at home and discuss with your peers on Piazza, or with tutors at a drop-in consultation.
The following exercise is independent of the previous lab content for this week. The exercise is designed to reinforce content from previous week and to introduce lists brie^y before formally tackling them next week.
Suppose we are developing part of a system to manage the scheduling and payment of
m d w n
Suppose we are developing part of a system to manage the scheduling and payment of casual workers. The days of the week and times of the day they work varies, and different people have different rates of pay, presumably based on their age, experience or ability.
Create a new Haskell Qle called Scheduling.hs.
A good way to represent the days of the week is as an enumeration:
To keep things relatively simple, assume that times are represented as a Double number of hours between 0 and 24. Rates of pay are given as a Double number of dollars per hour.
A single work period will be represented as a day, a start time and a Qnish time: (Working past midnight will be represented as two separate work periods.)
Different workers are paid at different rates, and all workers receive 50% bonus pay on weekends.
The pay function should calculate the total pay for this work period, at that hourly pay rate. If the work period falls on a weekend (which you can check with your weekend function) the total pay should be 1.5 times the normal pay.
A worker’s weekly work schedule could be represented as a list of work periods, as follows:
For Occupational Health and Safety reasons, the business is required to record the number of hours each employee works during their weekly schedule.
References
[1] Cabal User Guide, https://www.haskell.org/cabal/users-guide/index.html [2] CodeWorld API library, https://hackage.haskell.org/package/codeworld-api- 0.4.0/docs/CodeWorld.html#g:2
Don’t forget to add the module declaration to the Qle: module Scheduling where.
data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving (Eq, Show)
type Time = Double type Rate = Double
type WorkPeriod = (Day, Time, Time)
DeQne a function weekend to test whether a given day is on the weekend (Sat or Sun).
weekend :: Day -> Bool weekend = undefined
DeQne a function pay that takes as arguments the hourly rate paid to a given worker, and a particular work period.
pay :: Rate -> WorkPeriod -> Double pay = undefined
type Schedule = [WorkPeriod]
Write a function grossPay which, using your pay function, calculates the total pay that a worker will receive for their weekly schedule.
grossPay :: Rate -> Schedule -> Double grossPay = undefined
Write a function totalHours which calculates the total number of hours in a schedule.
totalHours :: Schedule -> Double totalHours = undefined
Write some useful doctests for each function to check if the functions work as you designed.
Submission required for COMP1130 students: Exercise 10 Scheduling and Payment System.
Updated: 06 Jun 2020
Responsible Opcer: Director, RSCS
Page Contact:
Course Convenor
Contact ANU
Copyright
Disclaimer
Privacy
Freedom of Information
+61 2 6125 5111
The Australian National University, Canberra CRICOS Provider : 00120C ABN : 52 234 063 906