Functional Thread 5

[)roi(]

Executive Member
Joined
Apr 15, 2005
Messages
6,282
Reaction score
405
Location
Functional Thread 5 : Predictable State Engine
In the previous threads on functional programming, we have explored a number of abstract concepts, and some of the many simplistic concepts that are employed when writing more functionally oriented code.

Here's the links to those threads:

Now for the goal for this thread:
The aim of this thread is to look more in-depth at the notion of pure functions, immutability and application state engines. This is essentially a continuation of the previous threads, except that we're going to build a simple working game engine and a very simplistic UI. The point being to not get too overwhelmed by the game logic, so that we can focus in on the state engine and its benefits of:
  • Predictable state.
  • Clearly delineation between model and view / controller.
  • Ease of reuse.
  • Ease to test.
So over the course of this thread we'll be building firstly all the models we need to support our game state, and then tying this in with a predictable state engine, and demonstrating how a reducer pattern can encapsulate actions to provide a more predictable way to affect our modelled state.

The chosen game is snake.
Whilst I mentioned in thread 4 that I had considered using the Pong game for this; I have decided rather to go with Snake primarily because its far lighter ito game logic, meaning we won't unnecessary be bogged down with too many things (including code) that doesn't relate directly with the concepts of building a Predictable State Engine with a reducer.

Ok, so now for the open challenge

Can you build a game engine for Snake, that is completely independent of its View and Controller(s); in addition the game engine, should be able to run without a UI, and all components (functions, classes, structs, enums, ...) should ideally enable 100% test coverage.

Ok, let's be a bit more specific about which Snake variation we want to build:
  • No barriers
  • No enemies or mines
  • 1 apple (the target) generated randomly at a time
  • 1 snake controlled by the keypress actions e.g. arrow keys (left, right, up, down).
  • When the snake consumes an apple it grows longer by 1 board position, and a new apple is randomly placed on the board.
  • Board size: 40 x 28 (ideally this should be configurable)
  • If snake exits the board on the right, it reenters automatically on the left, similarly top / bottom.
  • Pressing the key 'q' exits the game
  • Pressing the key 'r' resets the game
  • If a snake collides with its own body; it dies, and the game resets.
This should sound familiar to many of you, because it's the variation of the game that was popular on many non smart phones.

Feel free to code this in the language of your preference, and please take liberty with the design of your UI. The version I will finally walk though in this thread will be coded in Swift, together with a very simplistic terminal based UI using emoji characters, for example:
ezgif-2-538e0315fd.gif

Btw as for the tests: this is an optional requirement, so don't be concerned too concerned if some aspects are not easy to test. This is after all one of the goals of this thread; to demonstrate a way to build predictable application state and logic that is easy to test.

Timeline
Considering the scope of this thread and challenge. I'll only start posting actual game code after approx one and a half weeks from today.

Good luck.
 
Last edited:
There are many articles and quotes that elaborate on the major difficulties in programming, for example:
The two hardest problems in programming is:
  • Naming
  • Cache Invalidation
  • ...and off by one errors

State Management
Management of State in an application is no different; often scaling directly proportional with application complexity. Most codebases could be greatly simplified if applications didn't need make any provisions for user interaction at all, but that hasn't been the case since the batch heavy processes of early computing.

State is complicated and implementation can differ quite substantially across different applications, there are many different ways to manage state in an application. Each approach is typically intended to solve a set of domain specific problems -- implementing this to some degree of efficiency in a codebase generally involves the adoption of some level of abstraction; the fine line is trying to balance complexities, for example:
  • Implementation and maintenance complexity of the abstraction.
  • Coupled with the management of this state under effects.

Continuing on from the last thread, this thread as already stated is going to present state management using State Stores and the Reducer pattern. Here's a few links on this simple but effective solution to this problem:

For reference:
https://www.ibm.com/developerworks/library/wa-manage-state-with-redux-p1-david-geary/index.html
https://github.com/tayiorbeii/egghe.../master/03-Implementing_Store_from_Scratch.md

This is the core purpose for this thread, to demonstrate by way of a simple game, how state can be predictably managed in a application using the Store and Reducer patterns. Below for comparison is an implementation of both the Reducer and Store patterns in Swift; these two simple structures is what will be used to demonstrate state management in the Snake game.

Reducer pattern
PHP:
struct Reducer<State, Action> {
  let reduce: (State, Action) -> State
}

Store pattern
PHP:
struct Store<State, Action> {
  let reducer: Reducer<State, Action>
  var subscribers: [(State) -> Void] = []
  var state: State
  
  init(reducer: Reducer<State, Action>, state: State) {
    self.reducer = reducer
    self.state = state
  }
  
  mutating func dispatch(notify: Bool = true, _ action: Action...) {
    self.state = action.reduce(self.state, self.reducer.reduce)
    if notify { updateSubscribers() }
  }
  
  mutating func subscribe(_ subscriber: escaping (State) -> Void) {
    self.subscribers.append(subscriber)
    subscriber(self.state)
  }
  
  func updateSubscribers() {
    self.subscribers.forEach { $0(self.state) }
  }
}
 
Last edited:
Simple Store / Reducer Example

Let's say we need to manage the state for a simple application with two buttons:
  • Increment
  • Reset
The increment button simply adds one to a stored value and the reset button sets it back to zero. I.e. a simple counter

Let's define a data type for this:
PHP:
struct CounterState {
  var value: Int
}

Next let's define our two actions:
PHP:
enum CounterAction {
  case increment
  case reset
}

Next let's define a default state function for our counter:
PHP:
static let defaultCounterState = CounterState(value: 0)

Next we define a reducer function that allows us to assign code to our actions, I.e changing state appropriately for each of our defined actions:
PHP:
static let counterReducer = Reducer<CounterState, CounterAction> { (state, action) in 
  var newState = state
  switch action {
    case .increment:
      newState.value += 1
    case .reset:
      newState = defaultCounterState
  }
  return newState
}

Next let's link all of these parts together in a store, a type that allows us to link together our state, our actions and the reducer that effects changes by way of our predefined actions.
PHP:
static func initializeCounter() -> Store<CounterState, CounterAction> {
  return Store(reducer: counterReducer, state: defaultCounterState)
}

Ok let's see how we use this:
PHP:
let counter = initializeCounter()

// let's define a subscriber; a code closure that will receive changes made to our store
// here's a subscriber that simply prints out the counter value to the console
counter.subscribe { state in 
  print(state.value) // prints 0 -- receive current state upon subscription
}

// now let's increment our counter
counter.dispatch(.increment) // subscriber prints 1
counter.dispatch(.increment) // subscriber prints 2 

// finally let's reset our counter
counter.dispatch(.reset) // subscriber prints 0

...and that's all there is to it.

We can now easily dispatch changes to our store using a predefined set of actions (effects), and we can add subscriber code that will be executed each time a change is made to our store.

Tying this into a UI is simple, the button actions will dispatch our actions e.g.
PHP:
// increment button method code
counter.dispatch(.increment)

// reset button method code
counter.dispatch(.reset)

...and we use subscriber closure to effect changes to our UI each time our counter state changes e.g. To update the current counter value in a Label element in our UI, or simply just to print it out to the console.
 
Last edited:
Data Types
Here we go... let's build the Snake game starting with the data types and then the game logic, and finally merging it all together in a Store, Actions and a Reducer.

First off let's define a type for the directional controls and the associated x/y vectors (+ or -):
PHP:
  enum KeyPress {
    case left
    case right
    case up
    case down
    
    var vector: Coord {
      switch self {
      case .up: return Coord(x: 0, y: -1)
      case .down: return Coord(x: 0, y: 1)
      case .left: return Coord(x: -1, y: 0)
      case .right: return Coord(x: 1, y: 0)
      }
    }
    
    func isNotInverseOf(_ direction: Direction?) -> Bool {
      return Snake.validateKeypress(self, direction)
    }
  }
Note: This is based on x/y coordinate 0, 0 located top/left of our game board. The isNotInverseOf will be explained as part of the predicate functions.

Next let's define a Coordinate type and Size type
PHP:
  struct Coord: Equatable {
    let x: Int
    let y: Int
  }

  struct Size {
    let width: Int
    let height: Int

    func randomCoord() -> Coord {
      return Coord(x: Int.random(in: 0..<width), y: Int.random(in: 0..<height))
    }
  }
Note: on the Size type we've also defined a randomCoord method, which we'll use to randomly position the Apples on the game board. Secondly we've added Equatable conformance to the Coord; because we'll need this to ecaluate if the Snake's head coordinate is the same as that of the Apple i.e. it's going to eat the Apple, and similarly if the Snake's head coordinate is the same as any of the Snake's tail coordinates i.e. it has collided with it's own tail.

Next we'll need a way to track the Status of the game:
PHP:
  enum Status {
    case running
    case menu
    case gameover
  }

And finally let's consolidate all of this into a single State type
PHP:
  struct State {
    let board: Size
    var directions: [KeyPress]
    var snake: [Coord]
    var apple: Coord
    var score: Int
    var status: Status
  }

Let's wrap up this part by creating a static variable for the initial game state
PHP:
  static let defaultState = State(
    board: Size(width: 30, height: 20),
    directions: [.right],
    snake: [Coord(x: 2, y: 9)],
    apple: Coord(x: 14, y: 9),
    score: 0,
    status: .menu
  )
Note: Our Snake always starts off on the left side of the gameboard moving to right; similarly the Apple always starts off in the centre and on the y line as the Snake i.e. we're making it easy to get the first Apple. Finally the Status is .menu meaning we start our game at the Menu.
 
Last edited:
Game Logic and State Computations
First we need a function to calculate the next coordinate for our head based on our current direction vector:
PHP:
enum Next {
  static var head: (State) -> Coord {
      return {
        guard let s = $0.snake.first, let d = $0.directions.first?.vector else { return Coord(x: 2, y: 9) }
        return Coord(x: (s.x + d.x) %% $0.board.width, y: (s.y + d.y) %% $0.board.height)
      }
    }
}
Our Snake's body coordinate array is ordered from head first to tail last, similarly the directions array is ordered according to how the keystroke were queued. We start by trying to retrieve the Snake first coordinate (the head) and the first direction's vector. The Snake coordinate array can however be empty if in the previous frame, the Snake collided with itself; in which case we simply return the default starting coordinate for the next game.
If that's all ok; we then calculate the Snake next head coordinate by adding the vector x/y values to Snake's head x/y coordinate -- but there's a catch.

Imagine what happens when our Snake keeps moving right and reaches the right side of our game board; simply adding an x vector of 1, will result in a x coordinate that it beyond the bounds of our game board. What we need to happen is that exiting the game board on the right, should result in the Snake re-entering the game board on the left; similarly top to bottom, bottom to top, and left to right. This is where this weird %% custom operator comes into play -- it's a modulo operator with a difference.

In Swift, the default modulo operator is based on a truncation, where the C function fmod is based on flooring -- neither help us with the loop around requirement (e.g. right to left); for that we need a Euclidean derivative of the modulo operator, Here's a tabular comparison of the result for trunction vs. flooring vs. euclidian:
Screen Shot 2018-08-28 at 10.11.36.png
As you can hopefully see only the Euclidean formulation is consistent; more specifically 3 % 3 (Euclidean) is equal to zero i.e we looped right to left -- but as I mentioned this is not the default behavior for Swift's modulo operator or C's fmod function. No problem let's define our own:
PHP:
/// Defines %% operator precedence and associativity
precedencegroup EuclideanModulo {
  associativity: left
  higherThan: AssignmentPrecedence
}

/// Defines %% operator and links the precedence gorup
infix operator %%: EuclideanModulo

extension Int {
  /// Euclidean Modulo method
  func euclideanModulo(_ divisor: Int) -> Int {
    let remainder = self % divisor
    return remainder >= 0 ? remainder : remainder + divisor
  }
 
  /// Euclidean Modulo Operator
  static func %%(lhs: Int, rhs: Int) -> Int {
    return lhs.euclideanModulo(rhs)
  }
}
And that hopefully now helps with the understanding of our computation in the head function using the %% operator. We basically use the Euclidean version of modulo so that we end up with a computation that delivers a result that loops; or visually will make the Snake exit on the right, and reappear on the left, etc...

You can find more details on this mathematical choice variant of Modulo here: https://en.wikipedia.org/wiki/Modulo_operation
 
Last edited:
Predicate functions:
The three true / false questions we have to ask in the game is:
  • In the next frame, will the snake eat the Apple
  • In the next frame, will the snake collide with itself
  • Is the keypress valid based on the current direction, for example: If we are going right, we cannot go left as that would instantly result in a collision, similarly going up, should restrict going down, and visa versa.
PHP:
  static var willCaptureApple: (State) -> Bool {
    return { $0.apple == Next.head($0) }
  }
  
  static var willSelfImpact: (State) -> Bool {
    return { $0.snake.contains(Next.head($0)) }
  }
  
  static var validateKeypress: (KeyPress, Direction?) -> Bool {
    return { keypress, direction in
      guard let direction = direction else { return false }
      return keypress.vector.x + direction.vector.x != 0 || keypress.vector.y + direction.vector.y != 0
    }
  }
willCaptureApple is easy; we simply check if the next computed head coordinate is the same as the Apple.
willSelfImpact is also fairly easy; we again simply check if the next computed head coordinate is the same as a coordinate in the snake array.
validateKeypress validates if a keypress should be allowed by adding the current direction vector x/y to the keypress's vector -- an invalid keypress would result in zero, for example: going right has a x = 1, and going left has a x = -1; the sum of this would be zero and hence invalid.

Transformation functions:
Transformation functions are a few helper functions to aid in the adjusting of state when computing the next frame in the game.
PHP:
  static var grow: (State) -> [Coord] {
    return { [Next.head($0)] + $0.snake }
  }
  
  static var slither: (State) -> [Coord] {
    return { [Next.head($0)] + Array($0.snake.dropLast()) }
  }
  
  static var discardDirection: (State) -> [Direction] {
    return { Array($0.directions.dropFirst()) }
  }
  
  static var maintainDirection: (State) -> [Direction] {
    return { $0.directions }
  }
grow as the name implies grows our Snake, by adding the next computed head coordinate to the front of the Snake coordinate array. This happens when we eat an Apple.
slither is the movement of the Snake, by prepending the next head, and dropping the tail tip coordinate.
discardDirection as the names implies, drops a direction from the directions array; typically once we've processed it.
maintainDirection is a function that simply maintains the state of the directions array; this is use only to ensure that we don't empty out the directions array, because that would stop the Snake from moving -- essentially we always retain the last direction in the queue.

Next Frame Computation
The following set of functions is used to compute the next frame, and will be called indirectly by the game run loop.
PHP:
  enum Next {
    static var directions: (State) -> [Direction] {
      return { $0.directions.count > 1 ? Snake.discardDirection($0) : Snake.maintainDirection($0) }
    }
  
    static var apple: (State) -> Coord {
      return { Snake.willCaptureApple($0) ? $0.board.randomCoord() : $0.apple }
    }
    
    static var snake: (State) -> [Coord] {
      return {
        if Snake.willSelfImpact($0) { return [] }
        if Snake.willCaptureApple($0) { return Snake.grow($0) }
        return Snake.slither($0)
      }
    }
    
    static var score: (State) -> (Int) {
      return { Snake.willCaptureApple($0) ? $0.score + 1 : $0.score }
    }
  }
Next.directions simply checks if we have more than 1 direction queued, if so we discard one, and failing that we maintain the current direction.
Next.apple checks if the apple will be eaten, if so we randomly place another Apple, failing which we keep the Apple where it is.
Next.snake computes the changes to the Snake array to either grow it when an Apple is eaten, zero it when a colision occurs, or simply to slither in the direction in queue.
Next.score computes the score, adding one if the Snake easts an Apple, otherwise returning the current score.
 
Last edited:
State, Actions and Reducer
Using the State / Action / Reducer pattern -- we first need to define the different Actions (or Effects) that can be triggered in the game.
PHP:
  enum Action {
    case keypress(KeyPress)
    case nextframe
    case reset
    case menu
    case start
  }
These should be fairly self explanatory, but let's review it anyway to avoid any confusion:
  • keypress is the we need to dispatch when the user the control presses keys; this maps directly to the KeyPress enum (left, right, up, down)
  • nextframe is the action dispatched to set-up the next frame for rendering.
  • reset is the action that is dispatched to reset the game to the default state.
  • menu is the action dispatched to display the game menu
  • start is the action dispatched to start a new game

On their own these Actions are meaningless, because we need a Reducer to effect computations based on the Action that was dispatched:
PHP:
extension Snake {
  static let snakeReducer = Reducer<State, Action> { state, action in
    switch action {
    case let .keypress(keypress):
      if keypress.isNotInverseOf(state.directions.current) {
        return addDirection(keypress, state)
      }
    case .nextframe:
      return nextFrame(state)
    case .menu:
      return setStatus(.menu, state)
    case .reset:
      return defaultState
    case .start:
      return setStatus(.running, state)
    }
    return state
  }
}


static func setStatus(_ status: Status, _ state: State) -> State {
  var result = state
  result.status = status
  return result
}

static func addDirection(_ direction: KeyPress, _ state: State) -> State {
  var result = state
  result.directions.append(direction)
  return result
}
The Reducer (snakeReducer) function simply takes a State and an Action, and then depending on the Action that was dispatched affects the appropriate change to the State. As you can see we have a switch which controls computation by the enum Action that was dispatched in. The setStatus function is another helper function to change the status property as per the Action dispatched; similarly the addDirection changes the directions property, append a Keypress if its valid.

Finally we need to set-up a Store containing the State and assign the Reducer
PHP:
  static func initialize() -> Store<State, Action> {
    return Store(reducer: snakeReducer, state: defaultState)
  }


Using this is quite easy
PHP:
/// Initialize game store
var snake = Snake.initialize()

/// Add game store subscription to do something when the Store is changed
/// This is where fire off changes to our UI
snake.subscribe {
  print($0)
}

/// Here we dispatch the .start action of the game, and also the .nextframe action
/// Our UI elements, keys, touches, etc... will dispatch the appropriate actions.
snake.dispatch(.start, .nextframe)
Our subscription will of course affect the necessary change to State i.e. status = .running, and it will compute the nextframe -- the snake will move 1 x coordinate to the right.
 
Last edited:
Let's now see how we tie this Snake State / Action engine with a working UI; the example I'm sharing is a simple UI for a console app that runs in the terminal. The engine is however completely unaware of the UI to which it is linked, so the UI could easily be a Cocoa MacOS application that renders the game board using SpriteKit, ...

Main -- trigger point for the console app
Here we set-up the terminal, switch terminal buffers (to save state), turn off keystroke echos, and enable non-canonical input (to stop control code echoing for arrow keys).
PHP:
import Cocoa

Ansi.Set.screenBufferAlternate().stdout()
Ansi.flush()

// Save the current TTY state
var saved_errno: Int32 = errno
let tty = Terminal.currentTTY()
var orig_termios = termios.init()

/// Restore original TTY state when we exit
func disableRawMode() {
  errno = saved_errno
  tcsetattr(tty, TCSAFLUSH, &orig_termios)
  Ansi.Set.screenBufferNormal().stdout()
  Ansi.flush()
}

/// Enable non-canonical TTY input (disable ICANON and ECHO)
func enableRawMode() {
  tcgetattr(tty, &orig_termios)
  atexit(disableRawMode)
  var raw = orig_termios
  raw.c_lflag &= ~UInt(ICANON | ECHO)
  tcsetattr(tty, TCSAFLUSH, &raw)
}
enableRawMode()

/// Initialize game store
var snake = Snake.initialize()

/// Add game store subscription for UI render
snake.subscribe {
  var view = Snake.CLI.View(state: $0)
  view.render()
}

/// Initialize Main Runloop
let app = NSApplication.shared
let applicationDelegate = SnakeDelegate.init()
app.delegate = applicationDelegate
app.activate(ignoringOtherApps: true)
app.run()
As you can see our subscription snake.subscribe is setting up our view based on the State ($0), and then calls render() to render it in the console. The Runloop part at the bottom is simply starting a application run loop to prevent our application from terminating as terminal are typically coded to do.
 
Last edited:
Next let's have a look a the controller
Which in the main trigger code is the initialised as linked in as the SnakeDelegate.
PHP:
import Cocoa

extension Snake {
  enum Controller {
    static func dispatchEvents(_ store: inout Store<State, Action>, _ event: NSEvent ) {
      switch event.keyCode {
      case 123: // left arrow
        store.dispatch(.keypress(.left))
      case 124: // right arrow
        store.dispatch(.keypress(.right))
      case 125: // down arrow
        store.dispatch(.keypress(.down))
      case 126: // up arrow
        store.dispatch(.keypress(.up))
      case 53: // esc
        store.dispatch(.menu)
      case 15: // r = reset 
        store.dispatch(.reset)
      case 1: // s = start
        store.dispatch(.start)
      case 12: // q = quit
        exit(EXIT_SUCCESS)
      default:
        _ = () // noop
      }
    }
  }
}

class SnakeDelegate: NSResponder, NSApplicationDelegate {
  func applicationDidFinishLaunching(_ notification: Notification) {
    // Add a game loop timer to the current application run loop
    let timer = Timer(fire: Date(), interval: 0.1, repeats: true) { _ in snake.dispatch(.nextframe) }
    RunLoop.current.add(timer, forMode: RunLoop.Mode.default)
    // Add a keydown event listener
    NSEvent.addGlobalMonitorForEvents(
      matching: NSEvent.EventTypeMask.keyDown,
      handler: { event in Snake.Controller.dispatchEvents(&snake, event) }
    )
  }
}
Our SnakeDelegate implements both NSResponder and NSApplicationDelegate i.e. so we can set it up as a delegate class for NSApplication's runloop; then we create a repeating timer that dispatches the .nextframe action every 100ms; we could do this more frequently but the game would be fare to quick to play. 100ms essentially means we refresh compute the nextframe 10 times per second, or 10 frames / second. The overhead of our State / Action is very minor, and will more than adequately accommodate more than ten of thousands of State frame updates per second i.e. it's not going to be a typical bottleneck.

In a space invader variant; this rudimentary timer nextframe dispatch approach would not be ideal, and it would be preferred unhinge the timer e.g. run is at 0.001ms i.e. 1000 frames per second, and then to rather control how often certain sprite groupings receive frame updates (based on targeted frames / second). Which practically would mean the space invaders could move slower than the ship, similarly the projectiles.

Screen Shot 2018-07-31 at 14.18.37.png


Note: that we also have a function called dispatchEvents that is called by the event monitor on keydown, and the switches over the keys pressed and dispatches the appropriate Actions, for example: .keypress(.left), .start, .reset, ...
 
Last edited:
Ok next let's see the code for the View
PHP:
import Foundation

extension Snake {
  enum CLI {
    static let (filler, snake, apple) = ("〰️", ":)", ":love:")
    struct View {
      var board: [[String]]
      let state: State
      
      init(state: State, filler: String = "〰️") {
        self.state = state
        self.board = [[String]](repeating: [String](repeating: filler, count: state.board.width), count: state.board.height)
      }
      
      mutating func render() {
        switch state.status {
        case .menu:
          title(text: "SNAKE", y: 0, xOffset: 2)
          title(text: "'s' to start", y: 3, xOffset: 2)
        case .gameover:
          title(text: "GAME OVER", y: -1, xOffset: 2)
        case .running:
          board[state.apple.y][state.apple.x] = apple
          state.snake.forEach { board[$0.y][$0.x] = snake }
        }
        let output = board.map { $0.joined(separator: "") }.joined(separator: "\n")
        print("\u{1b}cSCORE: \(state.score)\t`q` -- quit, `esc` -- menu\n\(output)")
      }
      
      mutating func title(text: String, y: Int, xOffset: Int = 1) {
        let title = text.compactMap { $0 }
        let y = state.board.height / 2 + y
        var x = state.board.width / 2 - (title.count - 1)
        for letter in title {
          board[y][x] = String(letter) + " "
          x += xOffset
        }
      }
    }
  }
}

Ps. I substituted smiley faces for the snake and apple unicode emojis, because MyBB doesn't appear to support this.

...and that's pretty much all there is to this. Both the controller and the View are completely separated from the game State engine, and its similarly oblivious about the fact that its tied into a console app UI.
 
Last edited:
I'll post a link to the full Xcode project for this; so you can play around with it -- however considering this by in large divorced from Cocoa MacOS API, this should also work on Ubuntu without any modification. I haven't tested it though, so let me know if you have issues getting it work.

Here's the gist link for the xcode code project for the Snake game: FPSnake.zip (45KB)

As always let me know if you need help translating this to another language.

In closing;
I have tried my best to avoid too much FP code wrangling in the internal makeup of the State / Action / Reducer engine.

For more complex scenarios we would typically build multiple stores, multiple reducers and tie these all together with Optics (Lenses and Prisms) -- but that's a intermediate / advanced topic for another occasion..
 
Last edited:
Going through it, it's quite simple, actually doing it yourself is difficult. :D

Been having time constraints for sitting at a PC and doing leisure work, but am going to try and make this in a swift variant over the next couple of weeks.

Thanks for the thread
 
Going through it, it's quite simple, actually doing it yourself is difficult. :D

Been having time constraints for sitting at a PC and doing leisure work, but am going to try and make this in a swift variant over the next couple of weeks.

Thanks for the thread

This, but php and not swift
 
Going through it, it's quite simple, actually doing it yourself is difficult. :D

Been having time constraints for sitting at a PC and doing leisure work, but am going to try and make this in a swift variant over the next couple of weeks.

Thanks for the thread
Always a pleasure.
This, but php and not swift
Glad you liked the thread. Interested to see what you can cook up from it.
PM if you need any help.
 
As for the next thread.
It'll probably pick up from this discussion on constructor oriented Dependency Injection versus alternative approaches including a solution for testing (e.g. Mocks)

I'll try to lead the topic with a simple challenge around the problem that DI aims to address, in particular, that:
  • Application / Classes are independent from the creation of their dependencies.
  • Configuration of the dependencies is separated.
  • Ability to support different configurations
  • Ability to test inherently impure and difficult to test code.

As for the functional approach to DI:
We should keep in mind that we are solving a problem, and that problem is always the particular programming task at hand.

The primary programming challenge is never focused on "how to best implement dependency injection".
 
[)roi(];21920975 said:
Always a pleasure.
Just re-read this. Meant to write Scala. :D
Will try python one in the next 3/4 weeks (after exams).
[)roi(];21920985 said:
As for the next thread.
It'll probably pick up from this discussion on constructor oriented Dependency Injection versus alternative approaches including a solution for testing (e.g. Mocks)

I'll try to lead the topic with a simple challenge around the problem that DI aims to address, in particular, that:
  • Application / Classes are independent from the creation of their dependencies.
  • Configuration of the dependencies is separated.
  • Ability to support different configurations
  • Ability to test inherently impure and difficult to test code.

As for the functional approach to DI:

I'd be quite interested in this one, was reading this a week ago: http://code.activestate.com/recipes/413268/ in regards to my current assignment.
 
[)roi(];21920985 said:
As for the next thread.
It'll probably pick up from this discussion on constructor oriented Dependency Injection versus alternative approaches including a solution for testing (e.g. Mocks)

I'll try to lead the topic with a simple challenge around the problem that DI aims to address, in particular, that:
  • Application / Classes are independent from the creation of their dependencies.
  • Configuration of the dependencies is separated.
  • Ability to support different configurations
  • Ability to test inherently impure and difficult to test code.

As for the functional approach to DI:
As for the thread on DI; I am currently leaning towards providing some of my DI code examples in C#; considering that originating discussion was in regard to a C# code example provided by [MENTION=375958]Spacerat[/MENTION], ... and for comparison I'll also provide code examples in Swift.

I'll open up this thread (incl. challenge) on the weekend.
 
Top
Sign up to the MyBroadband newsletter
X