Actually, if you really want to have re-evaluting (sub)expressions, then Python does give you an option: comprehensions.
Actually, if you really want to have re-evaluting (sub)expressions, then Python does give you an option: comprehensions.
List.init 4 (fun _ -> [])
[for _ in 1..4 -> []]
Or any sane language that uses immutable values by default like Elixir, Erlang, Clojure, Racket, Scheme, OCaml, etc.> Actually, if you really want to have re-evaluting (sub)expressions, then Python does give you an option: comprehensions.
That's the complaint, that [[]]*4 is not shorthand for list comprehensions and that it's something entirely different. This is common in Python where two things you'd expect to be the same are indeed different in subtle ways.
List comprehensions in Python are closer to sane, but they still have unexpected behavior because of the way scoping works in Python.
There are lots of reasons you would want items in a list repeated rather than having, essentially, the result of separate executions of the same constructor -- [] is just shorthand for list() -- so its good that [x()]4 is not shorthand for [x() for _ in range(4)]. Especially since that would mean it would have to be special-case sytax for list construction, and not a list operator that you could use on existing lists. The mildly annyoing edge case you need to learn about isn’t worth wiping out the whole broadly-useful language feature, or creating a great special-case inconsistency which would be more cognitive load to deal with, to eliminate.
[list() for _ in range(4)]I'm not. The fun _ -> [] is needed because the argument is the element's index. That's just what List.init happens to expect, a function that takes an index and initializes the element, but the index is not needed here.
Ah I see, you’re saying that the construction of a list in F# mandates a lambda, thereby reducing one possible point of confusion. I could agree with that. But then it comes at a cost of comfort: [0]*4 has less boilerplate than the F# version. So it comes down to trade-offs again.
Well, of course. It's a shorthand for duplicating the list elements four times. In this case, the list contains a single reference to an empty list, do the result contains four references to an empty list.
It would be bad for this case, though. As confusing as the particular case of:
[[]] * 4
might be to people who haven't learned how it works, list multiplication doesn't just apply to single element lists, and is useful and usefully distinct from what people are suggesting the behavior should be for the operation based on this one case.Besides, that still doesn't help with the case of
x = function_that_returns_a_list()
x = x * 4
where there is no literal list expression.