Reputation: 23
Suppose I have a class in JS with Typescript like this:
type Command = 'F' | 'B' // Forwards, Backwards
class Car {
private x: number
private y: number
constructor(x: number, y: number) {
this.x = x
this.y = y
}
private reportPosition() {
return `(${this.x}, ${this.y})`
}
private move(command: Command) {
if (command === 'F') this.y++
if (command === 'B') this.y--
}
execute(command: Command) {
this.move(command)
return this.reportPosition()
}
}
When I create a Car instance and execute the execute
method, two things happen:
execute
function returns a string.Now I want to write the same thing in a more FP way, making the functions pure, but I stumble at the execute
function.
My approach is this:
type Command = 'F' | 'B'
type Car = {
x: number
y: number
}
function createCar(x: number, y: number): Car {
return { x, y }
}
function reportPosition(car: Car) {
return `$({car.x}, ${car.y})`
}
function move(car: Car, command: Command) {
if (command === 'F') return createCar(car.x + 1, car.y)
if (command === 'B') return createCar(car.x - 1, car.y)
return car
}
function execute(car: Car, command: Command) {
const newCar = move(car, command)
const msg = reportPosition(newCar)
return [newCar, msg]
}
My questions are the following:
Since execute
does two things at once, I feel I am forced to return two values from it in the function. But this feels wrong.
Is this "valid" functional programming? Or would I never create such a function in the FP world and just call each of the functions inside (move
, reportPosition
) separately.
What if the move
function also had to return the information on whether the car has crashed after its move? Would I also have to return two values from that modified function: the new car instance and a boolean (indicating a crash)?
Also, I used the createCar
function within the move
function, which is technically not allowed for pure functions, correct?
What would be the best way to fix that? Pass the createCar
function as an argument to move
?
Thanks!
Upvotes: 2
Views: 210
Reputation: 692
execute
can be expressed in a functional programming language trivially, for example Haskell:execute :: Car -> Command -> (Car, String)
execute car cmd = let newCar = move car cmd in (newCar, reportPosition newCar)
If move
had to report additional data, you could include it in the return type and it would remain pure. However, assuming "if the car crashed" is an error state, then typically this would be modeled by returning a sum type (Maybe
or Either
in Haskell). Take Maybe
for example: data Maybe a = Just a | Nothing
, if the car crashed you could return Nothing
and if it didn't then return Just position
, then anything using the move
function can verify that it didn't return Nothing
.
Why would you not be allowed to call createCar
inside move
? Neither move
nor createCar
are modifying any external state/variables, both are only using the inputs provided in their returns.
Re-iterating my comment in the main post, a lot of these things from Haskell that I mentioned above (e.g. Maybe
) are available in libraries for JavaScript/TypeScript. For TypeScript in particular, there's https://github.com/gcanti/fp-ts. It can be a little bit confusing sometimes, as usually there are many names that refer to the same concept. For instance, some libraries refer to Maybe
as Option
.
Upvotes: 1