patternswiftMinor
Cat fight (with rockets)
Viewed 0 times
withfightrocketscat
Problem
My code is attempting to implement FP in an efficient / readable manner into some of my video games. I realize this may be a bit subjective, but I feel there is enough merit / objectivity to be of benefit to the community.
Why I'm asking for a review:
I'm not sure that my code is executing FP / procedural principles in an efficient manner, meaning if my attempts at FP / procedural are not good enough to be worth switch from mutable-state programming...
Or, if there are simpler ways of implementing what I'm trying to do functionally (I'm looking for a reasonable amount efficiency / low-verbosity), that would be cool.
Goal:
To make a single, mutable
It's done in Swift 2.2. Here is a link to an online playground for 2.2 with my program loaded. Otherwise, you can run it in any main.swift console or Xcode playground if desired:
Documentation:
```
//: Playground - noun: a place where cats can play (with rockets).
/* BATTLE CATS!! Fed up with being "cutified," one faction of furry-
friends has decided to destroy all servers and the
Internet!!
Tech-loving felines across the globe have
taken up arms to protect their fame, fortune, and
web-based addiction. Choose your side, load up, and
BATTLE WITH CATS!!!
Goal:
- 1. To make an immutable OOP-style Cat object with FP principles.
- 2. To make a single, mutable var 'cat list' that contains all Cat objects
- 3. To easily (non verbosely) have Cats battle each other, in a functional way.
Conventions:
camelCase:
- Global Mutable: gGlobalName
- External Pram: pramName
- Labels: labelName
- Named Closures: closureName()
- Enum Case: caseName
PascalCase:
- Struct/Type: ClassName
under_score:
- Locals: local_name
- Parameter: pram_name
Why I'm asking for a review:
I'm not sure that my code is executing FP / procedural principles in an efficient manner, meaning if my attempts at FP / procedural are not good enough to be worth switch from mutable-state programming...
Or, if there are simpler ways of implementing what I'm trying to do functionally (I'm looking for a reasonable amount efficiency / low-verbosity), that would be cool.
Goal:
To make a single, mutable
var 'cat list'; this list contains immutable Cat instances that can battle one another.It's done in Swift 2.2. Here is a link to an online playground for 2.2 with my program loaded. Otherwise, you can run it in any main.swift console or Xcode playground if desired:
Documentation:
```
//: Playground - noun: a place where cats can play (with rockets).
/* BATTLE CATS!! Fed up with being "cutified," one faction of furry-
friends has decided to destroy all servers and the
Internet!!
Tech-loving felines across the globe have
taken up arms to protect their fame, fortune, and
web-based addiction. Choose your side, load up, and
BATTLE WITH CATS!!!
Goal:
- 1. To make an immutable OOP-style Cat object with FP principles.
- 2. To make a single, mutable var 'cat list' that contains all Cat objects
- 3. To easily (non verbosely) have Cats battle each other, in a functional way.
Conventions:
camelCase:
- Global Mutable: gGlobalName
- External Pram: pramName
- Labels: labelName
- Named Closures: closureName()
- Enum Case: caseName
PascalCase:
- Struct/Type: ClassName
under_score:
- Locals: local_name
- Parameter: pram_name
Solution
First, let me get out of the way that I'm not a "Functional Programmer", and I probably won't ever be. I'm also not an "Object-Oriented Programmer", or any other paradigm. I tend to use whatever the language most lends itself to and what others are doing, and what fits the bill.
On that note, to the actual review! I'm only going through First Test for now.
In the beginning, there was code.
There are several potential improvements to code clarity available even before considering the approach that you take to object design.
I do not see the point of including the
Furthermore, according to this R manual page, these are the assignment operators:
This would mean that, if you were following the convention from R, you should write
On that note, to the actual review! I'm only going through First Test for now.
In the beginning, there was code.
There are several potential improvements to code clarity available even before considering the approach that you take to object design.
->>I do not see the point of including the
->> operator (even though it looks cool because I use a ligature font), as it just is =. lhs ->> rhs === lhs = rhs in your code.Furthermore, according to this R manual page, these are the assignment operators:
x x
value ->> xThis would mean that, if you were following the convention from R, you should write
mittens.fireRocket(at: nyan) ->> mittens to assign the return from Cat::fireRocket to mittens. To further that, the double angle bracket arrows search through parent scopes to find the variable, which is definitely not what is happening here. If you really want to use arrow assignment, you should probably use lhs Cat(name:)
Cat(copyCat:) -> Cat(copyOf:)
Additionally, it's generally considered bad form to have initializers have a side effect, such as printing to STDOUT.
Purity
A comment in your code suggests that methods cannot be pure because they are functions which implicitly take a parameter self. Though it is valid to think about methods as a function with a implicit self, it is more correct to think of the method actually running "in" (or "on", depending on who you ask) the containing object, and self is just a way to reference it.
In any case, methods can be pure, by any useful definition, as they don't have to modify self. An impure function is one that has a side effect by mutating state, and it doesn't matter what is available to it. In fact, in a Swift struct, a method must be pure over self if it does not have the mutating keyword.
Make status an enum
But for the point of succinctness, I won't here.
Checkpoint
At this point, test1 looks like this:
print("Test 1")
var mittens = Cat(name: "Mittens")
var nyan = Cat(name: "Nyan")
mittens = mittens.fireRocket(at: nyan)
nyan = nyan.takeDamage(from: mittens)
mittens = mittens.readyForNextBattle()
Then, there was the function.
Let's try going full Functional, shall we? Disclaimer: I do not recommend writing Swift this way in a shared code base, as Swift is usually used more like in the third section, below, but this section exists as an exercise into purely Functional Swift.
Functional code means functions which operate on data.
First, let's define our data type, Cat:
struct Cat {
let rockets, lives, hp: Int
let name, status: String
let AP = 40, DEF = 20, hpMAX = 50
init(name: String) { /* implementation */ }
init(copyOf: Cat, rockets: Int? = nil, /* etc */) { /* implementation */ }
}
Now what operations do we want to do? A rocket is fired from one Cat to another Cat. What is the result of this operation? The state of the cats afterwards.
func fireRocket(from: Cat, to: Cat) -> (Cat, Cat)
Returning a Tuple allows us to use one step to transform the data. fireRocket might be implemented like so:
func fireRocket(from attacker: Cat, to receiver: Cat) -> (Cat, Cat) {
let damageDone = attacker.AP - receiver.DEF
let attackerAfter = Cat(copyOf: attacker, rockets: attacker.rockets - 1)
let died = receiver.hp > damageDone
let hp = died ? receiver.hp - damageDone : receiver.hpMAX
let lives: Int? = died ? receiver.lives - 1 : nil
let status: String? = died ? "Dead" : nil
let receiverAfter = Cat(copyOf: receiver, hp: hp, lives: lives, status: status)
return (attackerAfter, receiverAfter)
}
(Full source of FULL FUNCTIONAL here)
Checkpoint
Here's Test 1 with this code:
print("Test 1")
var mittens = Cat(name: "Mittens")
var nyan = Cat(name: "Nyan")
(mittens, nyan) = fireRocket(from: mittens, to: nyan)
though even here we should be using immutable data for Full Functional.
In the end, there was compromise.
Truly, I believe in the best of both worlds. However, especially performance wise, I believe in the power of playing to your language's strengths.
In Swift, this means mutating structs. Mutating structs are still immutable data structures. It's just that these two calls are identical1:
struct Immutable {
let state: Int
func madeBetter() -> Immutable {
return Immutable(state: state * 2)
}
}
let imm = Immutable(state: 5).madeBetter()
/* ***** */
struct Mutable {
var state: Int
mutating func makeBetter() {
state *= 2
}
}
var mut = Mutable(state: 5)
mut.makeBetter()
The key point here is that, if you have a let x: Mutable, you cannot mutate it. mutating func` alCode Snippets
x <- value
x <<- value
value -> x
value ->> xage == nil ? (self.age = cc.age) : (self.age = age! )
name == nil ? (self.name = cc.name) : (self.name = name! )
rockets == nil ? (self.rockets = cc.rockets) : (self.rockets = rockets!)
// etcself.age = age ?? cc.age
self.name = name ?? cc.name
self.rockets = rockets ?? cc.rockets
// etc.print("Test 1")
var mittens = Cat(name: "Mittens")
var nyan = Cat(name: "Nyan")
mittens = mittens.fireRocket(at: nyan)
nyan = nyan.takeDamage(from: mittens)
mittens = mittens.readyForNextBattle()struct Cat {
let rockets, lives, hp: Int
let name, status: String
let AP = 40, DEF = 20, hpMAX = 50
init(name: String) { /* implementation */ }
init(copyOf: Cat, rockets: Int? = nil, /* etc */) { /* implementation */ }
}Context
StackExchange Code Review Q#146109, answer score: 6
Revisions (0)
No revisions yet.