For vs forEach


#1

In the classic iterative factorial function you can do:

var total = one
(two rangeTo x).forEach{i -> total *= i}

or

var total = one
for (i in two rangeTo x) { total *= i }

(Seemingly a bit weird code due to using BigInteger – Integer and Long are simply not up to the task of calculating factorials.)

Which of these Kotlin statements is deemed more idiomatic?

(Is the Kotlin equivalent of Python’s Pythonic, Kotlinic?)


#2

This was discussed here: https://github.com/yole/kotlin-style-guide/issues/33
In this case, I’d definitely go for for.

(Also, you don’t need to invoke rangeTo as a method; you should use the .. operator instead.)


#3

You are folding the collection. The example below uses integers:

val total = (2..x).fold(1) { total, value -> total * value }

#4

Any way, if I do more than one thing, I will choose for; forEach is just a method reference or side effect change


#5

I am not convinced myself about not using forEach, but if using for is the preferred style, I have no problem.

I believe I do need the rangeTo since … is not defined for BigInteger and I therefore have to define a rangeTo operator.


#6

I actually have that solution as well. This is a collection of different algorithms achieving the same goal I have the fold version, my question was the most idiomatic representation of the iterative version.


#7

Defining the rangeTo operator is what allows you to use ..


#8

Using rangeTo works, but if I use … I get a “For-loop range must have an iterator() method”. So I guess my support code is missing something.


#9

I generally prefer to use ‘for’ unless I don’t need a control variable in the body when I use forEach instead.

On your last point, ‘Pythonic’ is already an English word whereas ‘Kotlinic’ is not and so the former ‘sounds right’ to me but the latter does not.

As a portmanteau word, I think I prefer ‘Kotlinesque’ to ‘Kotlinic’.


#10

The downside of Kotlinesque would be that Pythonesque is a very negative description: Pythonic – your code is in harmony with the Python infrastructure; Pythonesque – your code has the look and feel of a series of Monty Python’s Flying Circus.


#11

I think I’m right in saying that Guido van Rossum named his language after Monty Python’s Flying Circus so Pythonistas might not necessarily object to ‘Pythonesque’ to describe the language’s idioms. In fact, according to this link the word is sometimes used in this sense.

Perhaps we don’t really need an adjective for Kotlin in any case. Running through a list of the most popular languages (Java, C, C++, C#, JavaScript etc.) I can’t think of a suitable adjective for any of them.


#12

During my Python workshops I used Pythonic as good and Pythonesque as bad to instil in people a sense of being idiomatic and in sympathy with the language and underlying runtime. Maybe I was abusing the term Pythonesque, but it worked for the audience.

The languages you mentioned are all old languages and suffer from style wars and thus doubt and uncertainty. Also a lot of poor code. Languages such as Go, Rust, Ceylon, etc. try to encourage (well Go totally enforces) a single style of code that everyone will use. This avoids style wars. Also as a side effect it makes it a lot harder to write non-idiomatic code. This I think is a good thing. Whilst I have lots of disagreements with the one-and-only Go format style, that there is only one removes a whole collection of decisions a programmer has to consider. So Go removes a whole dimension of style, and thus a whole dimension of idiomatic.

Anyway, in one sense you are totally right “idiomatic” serves for all languages. It is nice though that “idiomatic Python” can be shortened to Pythonic.


#13

Might also want to consider tail recursion as an alternative:

val BigInteger.factorial : BigInteger
    get()
    {
        tailrec fun factorial(n: BigInteger, total:BigInteger) : BigInteger
            = if(n > BigInteger.ONE)
            	factorial(n - BigInteger.ONE, total * n)
            else
                 total
        
        return factorial(this, BigInteger.ONE)
    }