2

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:

  • The internal state of the instance (x, y) is updated.
  • The 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:

  1. 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.

  2. 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)?

  3. 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!

6
  • There are some FP libraries for the js/ts ecosystem that attempt to model functors/monads/semigroups/etc. See fp-ts for instance. Commented Apr 14, 2021 at 1:46
  • 2
    Really, I wouldn't even have an execute function here. move returns an altered state, then the caller can call reportPosition on the new state if they want to. Commented Apr 14, 2021 at 2:10
  • 2
    And for 3, I don't know why that wouldn't be allowed for FP. Data goes in, data goes out. It isn't carrying out side effects. Commented Apr 14, 2021 at 2:12
  • I think he has some misconceptions on what "pure" means in FP. It seems based on his questions that he believes he's not allowed to call other functions, and by doing so would make it impure, which is not the case. Commented Apr 14, 2021 at 2:14
  • 1) You can return whatever you want as long as you return something and as long as you return the same output for the same input. 3) You can call a function within another one as long as it is a pure function, If the callee is impure, the caller gets impure as well. Commented Apr 14, 2021 at 8:28

1 Answer 1

1
  1. Doing two things at once doesn't necessarily make a function invalid functional programming (I think by "valid" you're referring to pure functional programming). What makes a function "pure" in functional programming is that its return value is only determined by its input values and nothing else. It also does not modify any external state or variables (referred to as "free variables" or global variables, meaning a variable that is not bound in the input parameters). What you're doing in 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)
  1. 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.

  2. 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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.