CMSI 386
Homework #5
Due: 2018-12-08

Time for Haskell and TypeScript! The TypeScript problems aren’t so bad once you get used to the basics, and you are prettty much only being asked to add types to code you already have. Haskell programming might feel a little different, so for this assignment you may work in teams of four. However, do not for a minute think it is a good idea to leave the coding to the other team members. First, that is dishonest and says that you just don’t care (you have a responsibility to learn; learning from each other is okay, but LEARN). Second, functional programming is at the core of some of the biggest money-making projects today; get used to that thinking.

Preparation

Make sure you know your way around the TypeScript and Haskell documentation. The Learn You a Haskell for Great Good! book and the TypeScript Handbook are good to skim.

DON’T PANIC

There is no category-theoretic Haskell stuff on this assignment.

Instructions

You will be adding to the private GitHub repository you started in the previous assignment. All your work should be included in a new folder called homework5 which will appear at the top level of the repo:

  .
  ├── README.md
  ├── .gitignore
  ├── homework1/
  │   └── (existing homework 1 files)
  ├── homework2
  │   └── (existing homework 2 files)
  ├── homework3
  │   └── (existing homework 3 files)
  ├── homework4
  │   └── (existing homework 4 files)
  └── homework5
      └── ts
      │   ├── .gitignore            (put *.js in here)
      │   ├── package.json
      │   ├── .eslintrc.yml
      │   ├── src/
      |   │   └── warmup.ts
      |   └── test/
      |       └── warmup-test.ts
      └── haskell
          ├── .gitignore            (put *.hi, *o, and Warmup in here)
          ├── Warmup.hs
          └── WarmupTest.hs

I encourage you to work in groups of four. Please submit only one solution set for your team. It does not matter under whose GitHub account your work is stored. What matters, if you wish to not receive a zero, is that I can clone your repo and run my tests. Since the repo is to be private, please allow me as a contributor to your repo (so I can run and comment on your work). My github name is rtoal.

Your homework submission will be the state of your repository on the branch master, at the due datetime, which is 11:59 p.m. on the due date in the America/Los Angeles time zone.

Setting up your Project

You’re already familiar with GitHub, and in Homework #1 you became familiar with the Node.js ecosystem. TypeScript lives in the same ecosystem, so you should be good setting up your TypeScript folder. Do it just like you did with Homework #1, with one addition: add the npm module @types/node (otherwise you will be sad). For your Haskell folder, just drop the source and test files in the same folder. Professional Haskell programmers do it with Cabal, HSpec, and QuickCheck, but we’re running too late in the semester to learn these technologies; focus instead on learning the language.

As in previous assignments, your job is to write a single module with several functions. The functions are described below, as is the test script.

The Problems

IMPORTANT GRADING NOTICE

For Haskell: You must write type annotations on every single function. Problem solutions that do not contain type annotations for every function will be penalized harshly.

For TypeScript: You must specify types everyone unless they are painfully obvious how they can be inferred (e.g., a local variable initialized with an expression of an obvious type.) Adding type annotations is a major part of the practice this homework affords you, and will be graded accordingly.

  1. In the file Warmup.ts reimplement the module from Homework 1 in TypeScript. You are allowed to start with my solution given on the Homework 1 answer page In fact, if you got less than a perfect score on Homework 1 then I strongly suggest that you do. 😅 (Not that my solution is great; it just works.) The reimplementation, though, is subject to the following modifications to the original JavaScript specification:
    • Use the proper, modern, module specification syntax, not the CommonJS style we used in Homework 1.
    • For the change-making problem, return a tuple, not an array.
    • For the Crockford-classless problem, don’t actually do Crockford-classless; instead, make a real class. (Study the Classes page from the TypeScript Handbook, and make the best class you can.) TypeScript has some nice features for classes; use them.

    This problem should not take too much time. With the exception of the Cylinder class, it involves only adding type annotations to existing code.

  2. In the file Warmup.hs create a Haskell module containing the following entities. Remember your type annotations! For details on how each function should work, consult the unit tests.
    1. A function that accepts a number of U.S. cents (an Int) and returns a tuple containing, respectively, the smallest number of U.S. quarters, dimes, nickels, and pennies that equal the given amount. Wrap your answer in an Either, returning an error string (if a negative amount was given) in a Left, and the result tuple (on success) in a Right.
    2. A function that accepts a string and returns a new string equivalent to the argument but with all apostrophes and double quotes removed.
    3. A function that returns the uppercased version of the fist string in a list that has a length greater than 5, wrapped in a Maybe, since there might not be any strings greater than 5 in the list.
    4. A function that produces an infinite list successive powers of a number starting at the zeroth power. Unlike Homeworks 1 and 2, you do not need callbacks or any crazy generator things; Haskell is lazy so you get this for free!
    5. A function that returns the sum of all the cubes of all the odd integers in a list. The function should accept only lists of integers (Int or Integer values).
    6. A function that swaps adjacent list elements as follows: given [a0,a1,a2,a3,a4,a5,...], return [a1,a0,a3,a2,a5,a4,...]. If there are an odd number of elements in the list, the last element stays at the end.
    7. A data type for 3-D shapes, with constructors Sphere and Box and associated functions volume and surfaceArea. Derive from Eq and Show.

The following unit tests files are provided for you, free of charge, because you are special.

TypeScript unit test file coming soon
WarmupTest.hs
import Data.List
import Warmup

is_approx :: Double -> Double -> Bool
x `is_approx` y = abs (x - y) < 0.0000001

fixture :: [(String, Bool)]
fixture =
    [ ( "change works for 0", change 0 == Right (0, 0, 0, 0) )
    , ( "change works for 219", change 219 == Right (8, 1, 1, 4) )
    , ( "change works for 97", change 97 == Right (3, 2, 0, 2) )
    , ( "change works for 1000000000", change 1000000000 == Right (40000000, 0, 0, 0) )
    , ( "change detects negatives", change (-50) == Left "amount cannot be negative" )
    , ( "stripQuotes works for empty", stripQuotes "" ==  "" )
    , ( "stripQuotes works for no quotes", stripQuotes "dog" == "dog" )
    , ( "stripQuotes removes stuff", stripQuotes "''\"\"a'''" == "a" )
    , ( "stripQuotes does not remove backslashes", stripQuotes "a\\b" == "a\\b" )
    , ( "firstUppercasedOverLengthFive returns nothing on empty"
      , firstUppercasedOverLengthFive [] == Nothing
      )
    , ( "firstUppercasedOverLengthFive returns nothing if none"
      , firstUppercasedOverLengthFive ["a", "bcdef"] == Nothing
      )
    , ( "firstUppercasedOverLengthFive works"
      , firstUppercasedOverLengthFive ["a", "abcdef", "g"] == Just "ABCDEF"
      )
    , ( "powers of 2", take 10 (powers 2) == [1,2,4,8,16,32,64,128,256,512])
    , ( "powers of 3", take 5 (powers 3) == [1,3,9,27,81])
    , ( "powers of 1000000", (powers 1000000 !! 9) == 10^54)
    , ( "powers of -1", take 10 (powers (-1)) == [1,-1,1,-1,1,-1,1,-1,1,-1])
    , ( "powers of 0.5", take 5 (powers (0.5)) == [1,0.5,0.25,0.125,0.0625])
    , ( "socoo works for empty", sumOfCubesOfOdds [] == 0)
    , ( "socoo works for single even", sumOfCubesOfOdds [8] == 0)
    , ( "socoo works for single odd", sumOfCubesOfOdds [3] == 27)
    , ( "socoo works for a mixed list", sumOfCubesOfOdds [-3, 2, -8, 5, -1] == 97)
    , ( "adjacent swapping for empty", swapAdjacents ([]::[Int]) == ([]::[Int]))
    , ( "adjacent swapping for single element", swapAdjacents [8] == [8])
    , ( "adjacent swapping for two elements", swapAdjacents [False, True] == [True, False])
    , ( "adjacent swapping for six elements", swapAdjacents [1..6] == [2,1,4,3,6,5])
    , ( "adjacent swapping for seven elements", swapAdjacents [1..7] == [2,1,4,3,6,5,7])
    , ( "volume of sphere radius 1", volume (Sphere 1) `is_approx` (4 * pi / 3))
    , ( "volume of sphere radius 2", volume (Sphere 2) `is_approx` (32 * pi / 3))
    , ( "surfaceArea of sphere radius 1", surfaceArea (Sphere 1) `is_approx` (4 * pi))
    , ( "surfaceArea of sphere radius 2", surfaceArea (Sphere 2) `is_approx` (16 * pi))
    , ( "box volume", volume (Box 1.5 3 20) == 90)
    , ( "box surfaceArea", surfaceArea (Box 1.5 3 20) == 189)
    , ( "spheres are equalable", (Sphere 6.28) == (Sphere 6.28))
    , ( "boxes are equalable", (Box 2 10 3) == (Box 2 10 3))
    , ( "spheres can be shown", (show $ Sphere 3) == "Sphere 3.0")
    , ( "boxes can be shown", (show $ Box 3 1 2) == "Box 3.0 1.0 2.0")
    ]

main =
    let results = map test fixture in do
        putStrLn $ unlines $ map fst results
        putStrLn $ show (sum $ map snd results) ++ " failure(s)"
        where test (message, condition) =
                ( message ++ ": " ++ (if condition then "SUCCESS" else "FAIL")
                , if condition then 0 else 1
                )