Advantages of using functional programming in Photomath’s step-by-step solutions

Jurica Miletić
Photomath Engineering
7 min readOct 31, 2022
Photo by olia danilevich on Pexels

Have you ever wondered what are the actually good use cases for functional programming? If you’ve ever attended any functional programming language lecture (i.e. Haskell), you probably wondered “Cool, I’ve learned it, what now??”. In this article, I am going to offer you a glimpse into Photomath’s step-by-step solution software, where we applied the functional programming paradigm, writing software in an in-house domain-specific programming language (DSL).

Step-by-step solution offered by Photomath gives students a proper understanding of mathematical problems and its purpose is to teach people procedures for solving tasks. As early as elementary school, we are conditioned into thinking of mathematical problems as menial tasks that need to be solved right there and forgotten later. That is caused by a lack of comprehension of mathematics and we in Photomath try to give people a better understanding of mathematics and offer many ways to approach problems that are written in mathematical notation, as is the purpose of learning math. Our recent success of 300M downloads got us thinking, maybe we’re not so wrong after all :)

Step-by-step solution for the quadratic equation problem
Substeps procedure of the first step from the example from the above

The technical side involves decomposing the procedure of the solution in a number of steps, and each step can be further on decomposed into substeps (for more detailed explanation, if it’s needed). Each step has both a brief explanation of what transformation of the task is made and also an in depth one. Observing the example from the first picture, we can see that the solution procedure of task “Find x in quadratic equation x² + 2x + 1 = 0” can be decomposed into 3 steps, where each of the steps has its own substeps. Since, let’s say, the first step might not offer enough explanation what kind of transformation is applied there, on the second picture we have substeps (detailed steps) of the first step which offer just that. In Photomath, we have a team of software engineers, called “core” team, that develop the solver software which takes in mathematical tasks as input and outputs the right solution procedure for it with all the right steps and substeps. I, myself, am part of that team, and I take great pride in ensuring the quality and accuracy of our solver software which is used through the Photomath app by tens of millions of active users.

From all of the above we can conclude that a step consists of an expression, transformed expression and texts describing the expression, in depth and brief one. Now you have a feeling of how the structure of the step-by-step solution looks like. So why did we decide to use exclusively functional programming and not say Python? I will give you an insight on why we chose a functional programming paradigm for coding the step-by-step solution.

FUNCTIONAL PROGRAMMING

Well, we can see from the structure of step-by-step solution, that the approach to building the solution would be to create a list of consecutive steps, and we would create a new step by taking the output of the last step as input for the new step and append the transformed input and explanation texts. Let’s see how would that look like when we break down the procedure from the first picture:

  1. “x² + 2x + 1” -> “(x + 1)² = 0” (“Factor the expression”)
  2. “(x + 1)² = 0” -> “x + 1 = 0” (“Set the base equal to 0”)
  3. “x + 1 = 0” -> “x = -1” (“Move the constant to the right”)

We can see we have 3 steps here, where the output of the last step represents the solution of the initial quadratic equation.

There are several useful FP concepts I’ll cover that come in handy. The first concept is called a pure function. A pure function is one whose results are dependent only upon the input parameters, and whose operation initiates no side effect, that is, makes no external impact besides the return value. On the contrary, the object-oriented approach gives the method function the ability to change the state of the object from inside the function, meaning it allows external impact for functions, and it can be dependent on the state of the object, not only on its parameters. Here, we can see that the function of creating and appending new steps is dependent only on the former steps as its input, doesn’t change any of the former steps, adds a new step to them and returns the resulting list of steps, so pure functions are the way to go.

Another useful concept from functional programming is immutability. Immutability disallows functions to change input arguments in order to avoid side effects that are not meant to happen. It follows the idea that the return value should reflect the work done by the function. Here, it makes more sense for us to create a list of new steps from former steps, than to modify them, because if for some reason the function fails it won’t have any side effects on the rest of the procedure.

Given that we explained the advantages of functional programming, let’s take a look at Python, which enables functional programming, but is not a pure functional language. The advantages of using that language is extensive library and very good support since it is widely used for various purposes. Let’s say we manage to find a library that would cover easier representations of mathematical expression and its operations. What’s the problem with Python? Well, it’s dynamically-typed language, which means that verification of type safety occurs at the runtime. It slows the execution of the software, and speed of the execution is of the utmost importance to us, because the user experience of Photomath app can go south really fast if users need to wait a lot for the response once they enter a task in the app. Users of mobile phones today are used to instantaneous response whenever they use any app and we ensure that the Photomath app meets their demands. So, although it is easier to write code in Python because you don’t have to assert a type to your variable, verification of those types prolongs runtime and we would like to shorten the runtime as much as we can to provide a more reliable product. Although Python supports multiple programming paradigms, having a more restricted paradigm (as functional one is) becomes beneficial for our use case.

So we decided to create our own functional programming language, namely our own domain-specific language (DSL), called Photomath solver language or PMS language in short. That language would tackle the issue of creating mathematical solutions. One additional worry with creating our own DSL was the way of implementing and executing such code. We decided to load the model, which is the entire code written in PMS language, in prewritten C++ code.

Loading of the model as such took a lot of time, and since we loaded our model on the mobile phones back in the day, it raised questions on how it would affect user experience if it took too long. Luckily, with great optimizations in C++ code, we managed to minimize the effects of model loading and in return we managed to implement code a lot faster in the PMS language than we could in any other programming language.

At least, those were the starting days. Nowadays, we load our model on the cloud so we perform faster in that aspect as well. Let’s get back to our solution. Since our solution is written as a series of steps, how did we resolve creation and addition of a step to our solution?

Core team

RULE

A way we decided to represent creation of a new step in the procedure is by the concept we call “rule”. Rule consists of three structures called: template, transformation and texts (brief one and in depth one). It’s all written in one line, and what the rule does is that it matches the result of the last step in the current procedure with the template and creates a step by combining template, transformation and texts.

It’s pretty intuitive and it enables us to write a procedure of some problem in a number of consecutive lines. A neat feature that we also enabled is the infix notation of expressions which enables us to write code in truly mathematical notation.

This seems very straightforward, but over the years PMS language became more complex and refined, as did the solver software itself, so hopefully in the future we will make another article that will cover that topic in full.

PMS code for the step-by-step solution of the problem stated in the first picture.

If all of this made you interested in the usage of functional programming in Photomath, feel free to contact me by mail jurica.miletic@photomath.com.

Like what you’ve read? Learn more about #LifeAtPhotomath and check out our job postings: https://careers.photomath.com/

--

--