For those who have been with us on the Mojo journey for a while, you’ll remember that our two function types of def and fn started quite a ways apart in functionality. The idea was that def would provide a more Python-like dynamic experience and fn would give you the behavior expected in a strongly-typed systems language. Over time, the actual functionality of these two has converged, with very little now separating them.
The proposal goes into our reasoning in depth, but we feel that having two function types with almost-identical functionality causes unnecessary confusion among Mojo developers. Therefore, we want to unify on a single function type for at least the near term. def is the more familiar naming for those coming from Python.
However, unifying on def will require changing the semantics of Mojo def to no longer be implicitly raising. This is a breaking change, but we believe that it will improve the clarity of function definitions by providing clear boundaries on where errors are and are not handled.
There will be a planned transition period while we implement this change. This will start with def being able to accept a raises clause, followed by a warning that has an easy fix-it for defs that lack explicit raises. You’ll also start to see us migrate over all of our functions in this period, which will touch a lot of code. Finally, poor fn will be deprecated and wound down. We’ll miss you.
I imagine this is going to trigger a lot of discussion, every Mojo programmer has gotten used to fn over the years. It also will impact a lot of code, but we felt it was important to get this correct before 1.0.
I’m not quite sure I like closing the door on having def be different than fn like this this early in the language’s lifetime.
What if we want to support different compilation defaults in def and fn in the future? (Think of asserts, different raising vs. aborting mechanics, error handling standards, etc.)
What happened to maybe providing an untyped script-friendly version of Mojo that defaults to Object type?
Couldn’t the future development of Classes enable another suite of behaviors that we could maybe want to be different in fn and def ?
I would like to request that this proposal only make def no longer imply raising and leave deprecating fn for a post 1.0 or even post 2.0 future. This decision seems a bit hurried to me and we can’t as a community guess what y’all are discussing behind closed doors.
I really like this decision. It will make Mojo more consistent and easier to teach and learn. Doing this pre Mojo 1.0 makes sense as a more dynamic version of Mojo is not on the roadmap until Phase 3 which will be several years from now.
The migration path from fn to def is just a simple search-and-replace, right? And then for the rare cases that actually used def to begin with will need an extra raises after, which the compiler will helpfully send you errors about, right?
Other than having a nostalgic attachment to fn, I think it’s fine to standardize on def.
I’m not quite sure I like closing the door on having def be different than fn like this this early in the language’s lifetime.
Hi Martin,
This is a common concern that several folks raised, so I’ll take a crack at it. While the “fn vs def” fork was well intentioned back when we had a bunch of behavioral differences, today it doesn’t make sense IMO.
Several thoughts:
There are real issues that we may need to resolve down the road, but a function keyword difference isn’t obviously the right way to solve it. “object” resolution issues for example can happen at global scope etc
If we need this distinction, we can always invent a new function keyword “pdef”
We may be able to solve these issues in completely different ways, or decide some of them are not important to solve.
Overall, we feel that this was “premature carving off future space” at the cost of making Mojo un-python-like in a way that is well intended, but that wasn’t carrying its weight.
It is a great move. There might be some challenges, but we are definitely on the right path. Above all, having two different function declaration keywords has never been friendly to beginners and Pythonistas (when I wrote Mojo Miji, I always struggled with which one to use).
Make def function identically to fn.
Deprecate fn.
The migration should not be too painful:
If you primarily use fn, you just need to replace fn with def in your codebase — no behavioral change.
If you primarily use def, you need to explicitly add raises to functions that actually raise, since def would no longer implicitly imply it. A compiler warning during the deprecation period could help catch these cases.
I second this. I love python but fn seems to do a better job describing what its doing e.g. defining functions.
def on the other hand stands for define which in my opinion is strange since we also “define” variables, structs, classes, etc. Why is “define” only used for defining functions?
This is fundamentally a trade-off between two values:
Semantic precision — fn is shorter and arguably “cleaner” as a keyword.
Consistency with Python — def is the keyword every Python developer already knows.
Since Mojo’s explicit goal is to integrate into the Python ecosystem, core keywords should remain consistent with Python wherever possible. Yes, def may not be the most semantically elegant choice — but it’s history, it’s muscle memory, and any Python user instantly recognizes it as “define a function.” That kind of zero-friction familiarity is worth a lot.
Moreover, fn isn’t actually more “semantically clean” in any obvious way — it’s not even the first letters of “function.” If the argument is about clarity and self-evidence, then func or function would be far more Pythonic and readable than fn. So fn doesn’t really win on its own stated terms either.