PA 0 — Due: Fri Sep 29 11pm CT
The following is adapted from a post originally written by Brian Hempel.
In this warmup assignment, you will set up your development environment for this course, and then practice making a submission to your Github Classroom repository. Whereas subsequent weekly programming assignments will be worth 10 points each, PA 0 is worth 1 point. Subsequent PAs will be released (at least) one week before they are due; this setup assignment is due sooner.
For this course, you will want four things on your computer:
A terminal for compiling and running code.
A good text editor for writing code.
The latest stable version of Haskell (currently 9.2.8 or higher, or 9.4.7 or higher, or 9.6.2 or higher).
Git for tracking changes in your code and submitting assignments.
Download Git, includes a terminal for Windows (“Git Bash”)
Tell us which GitHub account you will use in this course (see pre-course survey on Ed)
Compiling and running Haskell programs generally happens on the command line, so you’ll need to have a terminal application.
All macOS machines come with an app called “Terminal” installed. A popular alternative is iTerm.
On Windows, if you don’t have a terminal you’ll get one for free when you install git below (it will be called “Git Bash”).
You will want to develop some comfort on the command line. Here are the bare essentials:
See where you are (“p
rint w
orking
d
irectory”):
$ pwd
(The $
above is just an indication that you’re on a
command line. All you have to type is pwd
.)
List what’s in this folder:
$ ls
List “a
ll” files (files that begin with .
are usually hidden), with “l
ong” information:
$ ls -al
Learn about any command by looking at its man
page
(manual page):
$ man ls
(Hit q
to quit.)
Enter a folder (“c
hange d
irectory”):
$ cd some_folder
Go up one folder:
$ cd ..
The “root” folder is /
, and your home directory is
~
.
Make a folder:
$ mkdir new_directory
Move (or rename!) a file or folder:
$ mv old_name new_name
Copy:
$ cp old_file new_file
Copy a folder (“r
ecursive” copy):
$ cp -r old_folder new_folder
Log on to a remote machine:
$ ssh username@computername.cs.uchicago.edu
Each computer’s name is written on the side of the machine. Or, there are a few
machines for shared use. You can leave SSH with ctrl-d
on Mac and Linux and ctrl-z
then <Enter>
for Windows.
Copy a file to another machine:
$ scp some_file username@computername.cs.uchicago.edu:~/some/path/on/other/machine
If you use a terminal text editor like emacs
or
vim
, you can use ssh
to do your homework
remotely (although the University may soon require a VPN for using SSH
off-campus).
If some app is running in a terminal and you want it to stop, hit
ctrl-c
. If some app in the terminal is asking you for input
and you don’t want to give it any, hit ctrl-d
(end of
input). ctrl-d
is also a way to end your terminal
session.
Microsoft Word ain’t gonna cut it. You want a text editor that makes your code colorful and automatically indents every new line you type.
Visual Studio Code is a
popular GUI text editor these days. The lab machines have Sublime Text installed. For VS
Code: install the Haskell
extension and you will be
rewarded with many nice editor features.
On the command line, the popular options are vim and emacs. Both these editors are pre-installed on the lab computers. Using a command-line editor takes some practice, but an experienced user can learn to rapidly perform complicated edits. Another advantage is that these editors can be used on remote machines over SSH. The main disadvantage is you can’t use your mouse.
Also, if you haven’t already maximized your key-repeat rate yet in your keyboard settings, you’ll thank yourself later for doing it now. You’ll quickly get used to it and it will save you days worth of time.
You can install Haskell onto your own machine using these instructions. Choose the defaults whenever GHCup asks in purple text “Do you want to…?”. We will probably not use Haskell Tool Stack, so you may skip installing “stack” if prompted.
Mac users may need to first install the XCode command line tools. There should be a dialog that pops up asking you to do it. But if not, you can run:
$ xcode-select --install
To see if you have Haskell installed correctly, open up a Terminal and run:
$ ghci --version
The Glorious Glasgow Haskell Compilation System, version 9.2.5
If your version of GHCi is not of the latest stable versions mentioned above, then you may have to troubleshoot. For Mac users, run the command
echo $PATH
and check that ~/.ghcup/bin
and
~/.cabal/bin
appear. If not, you might have to add these to
your path. If you have the file ~/.profile
or
~/.bash_profile
, you can run the following:
$ echo 'PATH=~/.ghcup/bin:~/.cabal/bin:$PATH' >> ~/.bash_profile
(If you are using macOS Catalina or later, it is likely that your
default shell is Z Shell (Zsh), not Bash. You should be able to tell by
checking if the top of the Terminal app window says zsh
. If
this is the case, replace ~/.bash_profile
with
~/.zshrc
above.)
Restart the terminal and try again. Then test GHCi with a couple arithmetic commands:
$ ghci
GHCi, version 9.2.5: https://www.haskell.org/ghc/ :? for help
ghci> 2 + 2
4
ghci> 8 ^ 10
1073741824
Usually on a terminal you stop a program by typing
ctrl-c
, but in GHCi ctrl-c
will only stop a
long-running line of code. Instead, you leave GHCi by typing
ctrl-d
, which means “End of Input”. You can also leave GHCi
by typing :q
and hitting return.
(For non-Mac users (especially Windows users) you may have to consult Stack Overflow and help each other for troubleshooting. None of the instructors/TAs are particularly familiar with Windows, but we can try to help the best we can!)
Git (pronounced like “get”) is a version control system. Git lets you take snapshots of an entire folder and stores the snapshots as “commits”. With git you can:
To use technical terms, Git is a distributed version control system. Because of its speed and flexibility, it has become the leading tool for managing source code. For example, GitHub is the world’s largest storehouse of open source projects. However, GitHub did not make Git—that honor goes to Linus Torvalds, who you may recognize as the creator of Linux.
If you’re looking for a version control system to learn in 2022, Git is the one. You won’t be using many of its features (for example, you won’t be collaborating with each other), but you will use Git to submit code and that will give you a taste of what you can do.
You can download git here. Mac OS X computers may already have git installed.
To check if you have git installed, run this command on the command line:
$ which git
You should see a path, like:
/usr/local/bin/git
If you do, you’re good to set up git.
Check your Git config and see if you have a name and email set.
$ git config --list
If you don’t have a name and email set, choose the name and email that will appear on all your commits. You do not have to use your UChicago email.
$ git config --global user.name "Name"
$ git config --global user.email "email@example.com"
Enable color to be happier:
$ git config --global color.ui auto
We will use GitHub Classroom to create git repositories for you, one per assignment—this will make it slightly easier for us to distribute starter code and feedback on your submissions.
To start, check Ed for the post containing the GitHub Classroom
invitation link for PA 0. When you follow that link and accept the
invitation—you will need to sign in to GitHub with the
USERNAME
that you registered in our pre-course survey—a PA
0 repository will be created for you. (Each programming assignment will
start this way: check Ed, accept the GitHub Classroom invitation.)
On the command line, navigate to your folder for this class. Now, clone your PA 0 repository from the submission system onto your computer:
$ git clone https://github.com/UChicago-PL/cs223-fa23-pa-0-hello-world-USERNAME.git
$ cd cs223-fa23-pa-0-hello-world-USERNAME
Note: Some Mac users have reported trouble cloning into an iCloud directory. Clone outside of iCloud.
There is also a web interface to your repository. Log in to https://github.com/UChicago-PL/ to ensure you can see your repository. After pushing changes, it’s often best to check on the web to make sure you pushed everything—because what you see on the web is what we see when we grade.
If you encounter this error…
Cloning into 'cs223-TERM-pa-0-hello-world-USERNAME'...
Username for 'https://github.com': USERNAME
Password for 'https://USERNAME@github.com': XXX
remote: Support for password authentication was removed on August 13, 2021.
remote: Please see ... for information on currently recommended modes of authentication.
fatal: Authentication failed for ...
… follow that link for information about how to set up an SSH key or personal access token. (While “classic” PATs are not the most recommended option, they are next simplest compared to password authentication.)
Let’s make sure Haskell is working and practice Git’s workflow.
There should be a Hello.hs
file in your PA 0 repository,
which looks something like:
data Level
= Undergraduate
| Masters
| PhD
| Other
deriving Show
name :: String
= "Your name here"
name
level :: Level
= Undergraduate
level
major :: String
= "Your major or program here"
major
why :: String
= "A sentence about what inspired you to..."
why
distance :: Int -> Int -> Int
= rate * time distance rate time
Put in your own answers for name
, level
,
major
, and why
. Make sure to save the file
Hello.hs
.
On the command line, start GHCi by typing:
$ ghci
You should see ghci>
. (If you see
Prelude>
instead, you are probably using an older
version of Haskell.) To load the file, use :l
(the letter
l).
ghci> :l Hello
You should see:
ghci> :l Hello
[1 of 1] Compiling Main ( Hello.hs, interpreted )
Ok, 1 module loaded.
ghci>
You can now use the items you defined.
ghci> name
"Chuck Norris"
ghci> level
Other
ghci> distance 15 20
300
There’s a still another modification we’ll make below, but it’s a good idea to submit your changes as you work.
Exit GHCi by hitting ctrl-d
. Then run:
$ git status
You should see something like:
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: Hello.hs
Untracked files:
(use "git add <file>..." to include in what will be committed)
.Hello.hs.swp
no changes added to commit (use "git add" and/or "git commit -a")
Git is telling you that it’s not watching the
Hello.hs.swp
file (a temp file generated by vim). Any
changes to this file will be ignored. And it’s telling you that it is
tracking the file Hello.hs
but that your changes to the
file have not yet been tracked.
Tell Git to track your changes with git add
.
$ git add Hello.hs
Now if you run git status
you should see something
like:
On branch main
Your branch is up to date with 'origin/main'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: Hello.hs
Untracked files:
(use "git add <file>..." to include in what will be committed)
.Hello.hs.swp
We haven’t actually created a snapshot (a commit). We’re just telling Git what files we want to include in the commit. If we had more files, we could also add them to create a single big snapshot with multiple files.
Let’s just make a commit with this file.
$ git commit -m "Hello.hs with some personal info"
The string after -m
is your message for the
commit. It’s sort of a name for the snapshot. Use it to describe what
you changed since the last commit.
You can see a log of commits with git log
:
$ git log
commit 30a672d7a3b3f615080a7fd79eccaa831f5285dc
Author: Brian Hempel <brianhempel@uchicago.edu>
Date: Mon Oct 31 22:46:35 2016 -0500
Hello.hs with some personal info
The commit is still only on your own computer. To push this change to
the remote repository (the mit.cs.uchicago.edu
server
that’s acting as our submission system), just type:
$ git push
(For your very first push, you may have to type
git push -u origin main
.)
You should see a message about pushing to main.
Okay, now it’s time to make a bigger change. Open
Hello.hs
and paste this code at the bottom, updating the
existing definition of main
, if any:
main :: IO ()
= do
main putStrLn "Hello, world!"
putStrLn ""
putStrLn ("My name is " ++ name ++ ".")
putStrLn ("I am a " ++ show level ++ " student.")
putStrLn ("I'm studying " ++ major ++ ".")
putStrLn ("I'm in this class because " ++ why)
putStrLn ""
putStrLn $
"If you travel 15mph for 30 hours you will go " ++
show (distance 15 30) ++ " miles."
Reload the file with :r
(the letter r), and try it
out:
$ ghci
ghci> :r
ghci> main
You should see output.
Now exit GHCi, and try compiling (rather than interpreting) the file with:
$ ghc Hello.hs
GHC should produce an executable in the same folder called
Hello
.
Run it:
$ ./Hello
You should see output. You’ve just compiled your first working Haskell executable!
Now, let’s commit this change.
Use git diff
to see any changes that we haven’t told Git
we want to commit. (Note, this shows changes in tracked files
only; Hello.hs
is tracked because we already committed
it.)
$ git diff
...
main :: IO ()
main = do
putStrLn "Hello, world!"+ putStrLn ""
+ putStrLn ("My name is " ++ name ++ ".")
+ putStrLn ("I am a " ++ show level ++ " student.")
+ putStrLn ("I'm going into " ++ major ++ ".")
+ putStrLn ("I'm in this class because " ++ why)
+ putStrLn ""
+ putStrLn $
+ "If you travel 15mph for 30 hours you will go " ++
+ show (distance 15 30) ++ " miles."
Or, you can see a summary with git status
:
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: Hello.hs
Untracked files:
(use "git add <file>..." to include in what will be committed)
.Hello.hs.swo
Hello
Hello.hi
Hello.o
no changes added to commit (use "git add" and/or "git commit -a")
The Hello
executable and a few intermediate files
created by Haskell are untracked. Let’s let them be and not commit
them.
But we do want to commit our change to Hello.hs
. Tell
Git to include our change in our next commit:
$ git add Hello.hs
Now, actually make the commit:
$ git commit -m "Print out my info"
If you run git log
, you will now see two commits:
$ git log
commit 32133475e7e83c3ba4017787f4c0d56aab35f058
Author: Brian Hempel <brianhempel@uchicago.edu>
Date: Mon Oct 31 23:01:02 2016 -0500
Print out my info
commit 30a672d7a3b3f615080a7fd79eccaa831f5285dc
Author: Brian Hempel <brianhempel@uchicago.edu>
Date: Mon Oct 31 22:46:35 2016 -0500
Hello.hs with some personal info
The “Print out my info” commit is still only on your computer.
$ git push
There you go! If your Hello.hs
file looks right on GitHub, you’ve successfully
pushed. Push early and push often.
Each week, when you think your submission is final, you should verify that your changes were pushed:
git status
to make sure you don’t have any local
changes.main
branch and that
your files look right.If everything looks good, your submission is done. An automated script will grab your repository at the deadline.
There’s one more thing you should know. Your instructor or the graders may distribute grades by making changes to your remote repository. When there are more commits on the remote repository than on your local repository, Git will not let you push.
You have to first run:
$ git pull
This will pull any commits from the remote repository and automatically merge them with your local changes.
A vim session may open with a commit message saying “Merge … into
main”. Save the message and exit vim by hitting :x
(colon
x).
Afterwards you can run:
$ git push
In addition to new files for grade reports, feedback may also be provided as GitHub comments on Pull Requests, as opposed to files in the repository. Stay tuned for more details.
In the exercise above, we ran ghc
directly to compile
our code into an executable. This works great for small and simple
projects with minimal dependencies. But soon, we will start using a
system called Cabal to
build our code. During the course, we will encounter and use only a few
basic commands. For now, let’s just make sure Cabal is up and
running.
Go somewhere, make a new directory, and then run
cabal init
:
$ cd SOMEWHERE
$ mkdir my-first-haskell-project
$ cabal init --non-interactive
...
Generating CHANGELOG.md...
Generating app/Main.hs...
Generating my-first-haskell-project.cabal...
...
Take a look at the files and folders created, notably:
my-first-haskell-project/
my-first-haskell-project.cabal
app/
Main.hs
You can build…
$ cabal build
… and run:
$ cabal run
Up to date
Hello, Haskell!
Start poking around the .cabal
file to see what it looks
like. To learn a little more about what goes into this file, create a
new directory and run cabal init --interactive
instead.
For the git add
and git commit
part of the
workflow, a GUI tool may help (I, Brian, use the Mac-only GitX). A GUI helps visualize
changes much better. There are equivalent tools for other operating
systems. Git comes with gitk
, which you might try.
There are man pages documenting the different git commands, e.g.:
$ man git-add
$ man git-diff
$ man git-commit
$ man git-pull
...
Or:
$ git help add
$ git help diff
...
These docs describe an interactive mode for git add
that
you might find useful:
$ git add -i
*** Commands ***
1: status 2: update 3: revert 4: add untracked
5: patch 6: diff 7: quit 8: help
What now>
Of course, you can also search online for tutorials, for example Learn Git Branching and Try Git. (Though we don’t recommend using branches during this course—you don’t want to accidentally forget to push main and fail your assignment!)
The above commands are all you should need for this class, but in the future you may want to learn more about what you can do with Git.
Git gives you complete flexibility. You can grab old versions of files. You can undo entire commits. You can work on different branches to separate different features while you are working on them. You can reorder commits. You can go back in time and change history. (However, a rule of thumb is that you should not rewrite history after pushing.)
For what it’s worth, these are the commands I (Brian) use in my programming workflow. More advanced users may find these interesting. For this course, multiple branches or rewriting history is not recommended.
git add --patch # Actually via GitX; but `git add -p` is close
git rm # Un-stage a change so it's not committed
git commit # Again, via GitX
git commit --amend # Change last commit (GitX again)
git commit -a -m "wip" # WIP commits better than stashing. Never stash
git reset HEAD^ # Un-commit a WIP commit (but keep changes)
git checkout file_name # Discard changes to file
git checkout -b branch_name # New branch
git branch -d branch_name # Delete a branch
git pull --rebase # Pull, then re-apply local commits
git rebase branch_name # Replay commits on top of branch
git cherry-pick # Apply a single commit from a different branch
git reflog # Recover deleted branches, mistaken hard resets
git rebase --interactive HEAD^^^^^^ # Clean up history before pushing!