CS3217 Documents

CS3217 Documents

  • About CS3217
  • Problem Sets
  • Tutorials
  • Final Project Guidelines

›Tutorials

Tutorials

  • Tutorial 1 (week 2)
  • Tutorial 2 (week 3)
  • Tutorial 3 (week 4)
  • Tutorial 4 (week 5)
  • Tutorial 5 (week 6)

Tutorial Solutions

  • Tutorial 1 (week 2) Solutions
  • Tutorial 2 (week 3) Solutions

Tutorial 2 (week 3)

Tutorial Questions

  1. We will, together, apply Test Driven Development to create a simple Tic-tac-toe game. Meanwhile, think of what tests we should have for the game!

    1. What are test doubles?
    2. How do you use test doubles with dependency injection?
    3. Test this function, possibly refactoring it to be more testable:
      func sendEmailToUsers(message: String) {
          let userEmails = Database.queryAll(table: "users").map { $0.email }
          EmailService.sendEmail(to: userEmails, message: message)
      }
      
      (Assume that the Database.queryAll and EmailService.sendEmail methods do exist.)
    4. When is dependency injection useful? When is it not so useful?
  2. Refactor the code snippet below to adhere to the SLAP better.

    import Foundation
    
    func leftRotate(_ data: UInt32, by amount: Int) -> UInt32 {
        return data << amount | data >> (32 - amount)
    }
    
    // Taken from https://en.wikipedia.org/wiki/SHA-1#SHA-1_pseudocode
    func sha1(_ message: String) -> String {
        var data = Data(message.utf8)
    
        var h0: UInt32 = 0x67452301
        var h1: UInt32 = 0xEFCDAB89
        var h2: UInt32 = 0x98BADCFE
        var h3: UInt32 = 0x10325476
        var h4: UInt32 = 0xC3D2E1F0
        let ml = data.count
    
        data.append(0x80)
        data.append(contentsOf: Array(repeating: 0,
                                      count: (64 + (55 - ml) % 64) % 64))
        data.append(withUnsafeBytes(of: (ml * 8).bigEndian) { Data($0) })
        assert(data.count.isMultiple(of: 64))
    
        for chunkIndex in stride(from: 0, to: data.count, by: 64) {
            var words = stride(from: 0, to: 64, by: 4).map { i -> UInt32 in
                let word = data[chunkIndex + i..<chunkIndex + i + 4]
                return word.withUnsafeBytes { bytes in
                    bytes.load(as: UInt32.self).bigEndian
                }
            }
    
            for i in 16..<80 {
                words.append(leftRotate(
                    words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16],
                    by: 1
                ))
            }
    
            var a = h0
            var b = h1
            var c = h2
            var d = h3
            var e = h4
            for i in 0..<80 {
                var f: UInt32 = 0
                var k: UInt32 = 0
    
                switch i {
                case 0..<20:
                    f = (b & c) ^ ((~b) & d)
                    k = 0x5A827999
                case 20..<40:
                    f = b ^ c ^ d
                    k = 0x6ED9EBA1
                case 40..<60:
                    f = (b & c) ^ (b & d) ^ (c & d)
                    k = 0x8F1BBCDC
                default:
                    f = b ^ c ^ d
                    k = 0xCA62C1D6
                }
    
                // &+ is like +, but it does not crash when it overflows
                let temp: UInt32 = leftRotate(a, by: 5) &+ f &+ e &+ k &+ words[i]
                e = d
                d = c
                c = leftRotate(b, by: 30)
                b = a
                a = temp
            }
    
            h0 &+= a
            h1 &+= b
            h2 &+= c
            h3 &+= d
            h4 &+= e
        }
        return String(format: "%02x", h0) +
               String(format: "%02x", h1) +
               String(format: "%02x", h2) +
               String(format: "%02x", h3) +
               String(format: "%02x", h4)
    }
    
    1. What is the delegate pattern?

    2. Suppose we want to build a user interface for the Sudoku puzzle that we have built in Problem Set 0. We will have a SudokuGridView UI component (that subclasses UIView); this component represents the grid that is displayed to the user. The user can then input numbers in this grid to attempt the puzzle.

      This SudokuGridView contains no domain logic. Instead, it will communicate with a delegate class that we can specify with a delegate property in the class, i.e.

      class SudokuGridView: UIView {
          var delegate: SudokuGridViewDelegate?
      }
      

      Here, SudokuGridViewDelegate is a protocol that acts as a delegate to SudokuGridView. Suggest methods that should go in this protocol.

      You may see, e.g. UITableViewDelegate for inspiration.

  3. Watch this video on "Functional Core, Imperative Shell": https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell

    (It's only 13 minutes long! It probably can change the way you architect software!)

    Comment on the paradigm used by this video. Some guidelines:

    • What are the advantages proposed by the "functional core, imperative shell" paradigm?
    • In what situations might this paradigm not be applicable?
    • The video mentions that only the functional core needs to be tested; the imperative shell needs no tests. What do you think about this?

    You might recognise this paradigm from a few patterns, such as:

    • the IO monad
    • the actor model
    • the Flux architecture, used in Redux

    If you have time, you might also want to watch this video by the same author: https://www.destroyallsoftware.com/talks/boundaries. It's the same idea described more clearly in 33 minutes, and is also highly recommended!

← Tutorial 1 (week 2)Tutorial 3 (week 4) →
  • Tutorial Questions