The Crystal Programming Language Forum

Var, let in declarations (optionally) : Would it not increase readability?

If a keyword like « var » or « let » (possibly -optionally- via a command-line switch option «- - wth-var…» ) would be used when a variable is declared (as in e.g. Rust, Go, Typescript, Nim, etc.), would it not render the code even easier to parse for humans (the compiler, of course, can do the maths and use inference to differentiate between assignment and declaration of a variable) ?

In Crystal every assignment to a local name basically is a declaration. The type is decided anew for every line.

You’d think that your proposal would guard against this case

var a = 5
a = "z"

But actually the way Crystal works, it already is like

var a = 5
var a = "z"

and let’s just drop the fake var

a = 5
p! typeof(a) # => Int32
a = "z"
p! typeof(a) # => String

The two a:s are basically two completely separate variables with two separate compile-time types. They just happen to have the same name.

1 Like

So again, why not have it as an option. I actually like the « fake » var (readability-wise).

After all, Python has fake types (aka: « hints »).

So why not ?

You can certainly just go ahead and add it to your code.

macro var(x)
  {{ x }}
end
 
var a = 5
p! typeof(a) # => Int32
var a = "z"
p! typeof(a) # => String

But I don’t imagine that generally people would appreciate using a keyword that does literally nothing

7 Likes

Brilliant !
I love it.

You know, the Python and Nim people claim that visible delimiters are useless, but in fact, they make code more readable, and they make the job of code formatters/beautifiers a lot easier (not to speak about « cut / paste » operations).

Also, if like me you use tons of different programming languages, your mind is really trained to look for beacons like curly braces, « end », etc.

I’m sure that some Crystal users write unnecessary stuff like:

X.myMethod() instead of
X. myMethod

Not out of sheer nostalgia or habit, but because it seems more expressive, less ambiguous.

But, the point is: it’s feasible (albeit quasi-blasphempous) !

Brilliant !
I love Crystal !

How is let x = 1 or var x = 1 easier to parse than x = 1? Or put another way, I think it’s subjective?

1 Like

Because your unconscious mind is scanning your code looking for clues to make sense of what you are reading.

If I can locate instantly the spot where a variable is declared, it makes the whole process of reading a long chunk of code much easier.

Another analogous example is namespaces:

If you can instantly tell that « nlp.scanner » is part of a (hypothetical) nlp class or module, it saves you the maddening feeling of « where does that come from?, where is it defined ? does it come from an import ?, etc.»

Elegance, readability, expressivity, lack of ambiguity, are not the same thing and in an ideal case ought to be balanced optimally. I don’t like Python (for instance), because IMHO it has sacrificed clarity to conciseness. That is partly why Ruby (and off course Crystal) are mode readable than Python.

The thing is that there’s no such concept as “variable declaration” in Crystal (nor in Ruby). The variable is declared the first time it’s assigned, and it’s updated the next time it’s assigned.

This is valid Crystal code:

if something
  x = 1
else
  x = 1
end

puts x

Where is x declared?

Granted! but still, using a dummy « var » hints to an « upper limit » (of sorts) of the position in the file (or in an imported file) where the (potential) initial declaration could be, which is cool.

The fact that you can make up your own « var » or your own « for … in » in Crystal (using macros) is just beautiful in my opinion.

I could see a purpose for a keyword to declare a type and pin its type, so it can’t get a different type later.
This can be achieved by setting a type restriction on the variable (var : Type), which can actually be considered a variable declaration in Crystal.
If you pack that in a macro:

macro var(x)
  {{ x.target }} : typeof({{ x.value }})
  {{ x }}
end

var a = 5
a = "z" # Error: type must be Int32, not (Int32 | String)

(Note: I’m a bit confused why the error mentions Int32 | String instead of just String.)

3 Likes

Using let required languages I can’t find any variable, declaration or not because of all the extra noise. I’m forced to rely on the editor to find declarations. Maybe let lets you transition but you’ll probably find variables faster by retraining your brain to recognize variables without extra fluff. That will probably prove useful when reading others crystal code since none of them use let.

1 Like

Please don’t.
var/let is one of the things I dislike about the languages that use it.

All it does is increase the number of words with special meaning that you have to learn. I have no idea what the difference between a “var” and a “let” is, and I am not particularly interested in having to learn it.

4 Likes

I guess the error message was generated with the type information of the variable a, not the expression "z" used in the assignment.

Maybe if the error message contains the variable name things could get more clear, like:
Error: 'a' variable type must be Int32, not (Int32 | String)

Oh, yes, I forgot to reply here. The type of the variable is set to be Int32. Then when you assign a String to it it becomes that union, and that union is incompatible with the expected type. I think improving that error message should be easy. I’ll try.

1 Like