What’s the difference between the fn and def keywords in Mojo?

Mojo is meticulously crafted to combine the performance capabilities of systems programming with the familiarity of Python. As a testament to this objective, Mojo has become a superset of Python, ensuring that developers comfortable with Python's syntax and conventions can seamlessly transition into the Mojo ecosystem.

While Mojo directly borrows the def keyword from Python for function definitions, it also introduces its own fn keyword for function declarations, which comes with its own set of distinctive capabilities and use cases within the Mojo language. We will take a look at both these keywords one by one to understand the differences between them and when should we use them.

Note: You can learn more about Mojo in this Answer.

The def() keyword in Mojo

When we use the def keyword in Mojo, we are defining a function. The syntax for it is clear and concise, resembling its Python counterpart, making it easy for Python developers to work with. Functions declared using the def keyword can take parameters, return values, and perform a wide range of tasks, just like in Python. Here are some key characteristics of the def keyword in Mojo:

  • Dynamic and flexible: The def functions are dynamic, meaning they can be defined and redefined at runtime. This flexibility allows developers to modify and extend functions as needed, making it well-suited for dynamic programming tasks.

  • Mutable arguments: In def functions, function arguments are mutable. This means that we can modify the values of arguments within the function, just like in Python. This mutability can be advantageous in certain scenarios.

  • Scoping flexibility: Scoping in def functions are not enforced in a strict manner. This flexibility can be useful in various situations, but it’s important to be aware of scoping implications when working with def functions.

Let’s take a look at a simple example of using the def keyword in Mojo:

# Define a function using the def keyword
def main():
message = "Hello from Mojo 🔥"
print(message)

This code works exactly the same as it does in Python because the def keyword is defined to be dynamic, flexible, and generally compatible with Python. While the def keyword is well-suited for high-level programming and scripting tasks, it may not always be the ideal choice for systems programming, and that is where the fn keyword becomes important.

The fn() keyword in Mojo

When we utilize the fn keyword in Mojo, we are defining a function with a distinct and more controlled behavior compared to the def keyword. The syntax, while reminiscent of Python, introduces a level of strictness and predictability that distinguishes it from the def keyword. Functions declared using the fn keyword are crafted with the following specific characteristics:

  • Strict and structured: The fn functions are designed with a higher degree of rigidity. This means they are not as dynamically flexible as def functions, and once defined, they maintain a level of consistency in their behavior, which can be advantageous for scenarios where predictability is crucial.

  • Immutable arguments: The fn keyword enforces the immutability of argument values within the function’s body. Unlike the def keyword, where arguments are mutable, this design choice minimizes accidental mutations and encourages using non-copyable types as arguments.

  • Explicit local variable declarations: Unlike the def keywords, the fn keyword disables the implicit declaration of local variables, making it mandatory to declare all local variables explicitly. This reduces the likelihood of name typos and aligns with the scoping provided by the let and var keywords.

Note: If you want to read more on the topic of using the let and var keywords for variable assignments, please take a look at this Answer.

# Define a function using the def keyword
fn main():
message = "Hello from Mojo 🔥"
print(message)

As we can see, we get an error when we run the above code. The error message in the code arises from not explicitly declaring the message variable on line 3. To resolve this error, we can use the var or let keyword before the message variable to declare it explicitly. This correction aligns with Mojo’s requirements for fn declarations, ensuring that all variables are defined before they are utilized.

Let’s take a look at the fixed version of the above code by using the var keyword:

# Define a function using the def keyword
fn main():
var message = "Hello from Mojo 🔥"
print(message)

As we can see, when we use the var keyword in Mojo, it can trigger a warning that reminds us about the variable’s mutability. In the code, we declared the message variable as a var, which means it’s mutable, but because the code doesn’t actually change the value of the message variable after its initial assignment, we do not get any error. Try replacing the var keyword with the let keyword to remove this warning.

Function arguments and returns

An important thing to remember is that when defining a function in Mojo using the fn keyword, data types become mandatory for both function arguments (parameters) and return values. This means that we must specify the data types of the parameters the function accepts as input and the data type of the value the function returns.

Note: In this context, we specifically address the return values, and the print statements are not encompassed within this discussion.

fn add(x: Int, y: Int) -> Int:
return x + y
fn main():
let z = add(1, 2)
print (z)

The above code defines a simple add function that adds two integers and a main function that calls the add function, assigns the result to a variable z, and then prints the value of z. When executed, it will output the sum of 11 and 22, which is 33. The important thing to note is that if we remove either the return type or the argument type, the code will throw an error. Go ahead and actually try that.

Conclusion

Ultimately, both the fn and def keywords serve as valuable tools in the Mojo programmer’s toolkit, offering the flexibility to choose the most suitable option based on the specific programming context and objectives. This versatility underscores Mojo’s commitment to empowering developers with choices that cater to a wide range of application scenarios.

Free Resources

Copyright ©2024 Educative, Inc. All rights reserved