Def, fn and var

Some time ago it was said that def was for dynamic programming without types, and fn for static programming with types. The distinction was nice and clear. But now I see that initialization/declaration of a new variable in fn can be done without using var, and var can also be used in def.
That is a bit confusing, why is this and will it stay this way?
I work with the stable release Mojo 24.5.0

2 Likes

Such things were never what made def “dynamic”, if you want to call it that. What made it dynamic was the fact that you didn’t need to explicitly state the type of your functions arguments and return value. in fn functions you must explicitly state these things.

3 Likes

var also has some better scope constraints than declaring variables without them. When you care about controlling such details, it’s better to use it

2 Likes

I agree here. var is a good practice given it makes debugging so much easier by preventing you from multiplying strings together and other shenanigans :stuck_out_tongue_winking_eye:

I agree the rules for using var in Mojo are a bit weird right now. I expressed my dissatisfaction with the current state of var on Discord back in November. Here’s what I said:

In my opinion, the moment var became optional within fn, it lost most of its utility, because now you can’t tell whether y = x is a variable declaration or a reassignment, except by using IDE features.

I suppose a codebase can enforce that var is always used for declarations, by using a linter or a compiler flag to generate an error if you omit it. My problem with this plan is that it’s guaranteed to create a never-ending religious war between different communities using Mojo, about whether using var is “good practice” or not. This is the kind of annoying, superficial debate that will definitely still be occurring in 10 years time.

In my opinion, I think Mojo should just make a firm decision about whether variables should be declared using var, or whether we stick with Python’s syntax. In the absence of a firm and carefully justified decision, debates are going to keep occurring indefinitely, and we’re going to end up with wildly different “style guides” in different subcommunities.

With the way var currently works, IDE/tool support is required to make sure that you’re using var consistently. This doesn’t seem any better than using IDE/tool support to help you identify whether y = x in a Python program is a declaration or a reassignment. Given this, I’m wondering why we don’t just stick with Python’s syntax, and focus on having high quality support for seeing where y is declared. For example, in an IDE, you can just highlight y differently (e.g. underline it) if y = x is a declaration. The underline serves exactly the same role as var. The only difference is that rather than being part of Mojo’s grammar, it’s part of IDEs. This gives us most of the benefit of var while also remaining 100% consistent with Python, and avoiding that whole debate.

The main limitation of this approach is that it doesn’t help when browsing code on Github. It’s a shame Github has such a mediocre code viewer. Maybe some day that will change.

1 Like

Agree, but this will depend on the differences between fn and def. If the only difference will be the implicit raises + object type, then var cannot be mandatory. Using one of them for fn will just add consistency on the code we will read on the future. Disallowing implicit declarations will help to know where you are defining a variable. or remove completely var. Then if you want to distinguish between declaration and reassignment, we could use something like y := x (inferred type) or y: Int = x (explicit type) for declarations, and y = z for reassignment. For def functions, implicit declarations are fine, it’s just like python.

1 Like

Have y’all stumbled down the alias rabbit hole :smirk:

The only current difference is implicit raises + object type. It is possible that we will change that in the future though as we bring in more dynamic features. One thing we will need to rectify at some point is the default type for collection literals:

  x = [1]

It would be great for x to default to a static type like List[Int] in systems code, but it will need to resolve to a dynamic object type in python compatible scripting code. Def is one tool we have to work with that could control these behaviors - we’ll need to see if it is the right one as we build out more of the stack.

In the meantime, my hope/expectation is that the var keyword gets integrated with the (not yet started) pattern system, similar to how ref should become a pattern.