Does Language Matter?

3 minute read Published: 2020-02-09

Today I was chating with our local Mobile Community. Aleksandr send some examples of OSS projects he contributes to and I decided to look at how mobile developers write their code for mobile applications and libraries.

Prologue

Before that point our discussion was abour ServerSide Swift, languages, etc. How interesting was to find two exactly the same codesamples written in different languages.

If you want to be objective - please look at thec code_sample_1 and code_sample_2 without my comments.

Horse and water

You definetely can give a developer some new shiny languages with a familar syntax, but you can't or shouldn't expect that he\she will write in it, even if he\she will write in a files with proper extensions (.swift and .kt in our case).

Let's look at these samples and compare them one piece after another:

Iterators

Both languages has a good support of iterators with higher-level aspects on top of them:

But code samples will use iterators directly:

val iterator = this.makeIterator(text)
let iterator: CaretStringIterator = self.makeIterator(forText: text)

Those of you who didn't have enough experience with Kotlin or Swift will certanely struggle trying to understand where is Swift and where is Kotlin.

Initialization

        var affinity = 0
        var extractedValue = ""
        var modifiedString = ""
        var modifiedCaretPosition: Int = text.caretPosition

        var state: State = this.initialState

        var insertionAffectsCaret: Boolean = iterator.insertionAffectsCaret()
        var deletionAffectsCaret: Boolean = iterator.deletionAffectsCaret()
        var character: Char? = iterator.next()
        var affinity:               Int     = 0
        var extractedValue:         String  = ""
        var modifiedString:         String  = ""
        var modifiedCaretPosition:  Int     = text.string.distanceFromStartIndex(to: text.caretPosition)

        var state: State = self.initialState

        var insertionAffectsCaret: Bool       = iterator.insertionAffectsCaret()
        var deletionAffectsCaret:  Bool       = iterator.deletionAffectsCaret()
        var character:             Character? = iterator.next()

If you change formatting a little bit, there will be no difference.

Iteration

We already discussed alternatives to direct iterators usage, let's look what we get if we don't use them.

        while let char: Character = character {
            if let next: Next = state.accept(character: char) {
                state = next.state
                modifiedString += nil != next.insert ? String(next.insert!) : ""
                extractedValue += nil != next.value  ? String(next.value!)  : ""
                if next.pass {
                    insertionAffectsCaret = iterator.insertionAffectsCaret()
                    deletionAffectsCaret  = iterator.deletionAffectsCaret()
                    character   = iterator.next()
                    affinity   += 1
                } else {
                    if insertionAffectsCaret && nil != next.insert {
                        modifiedCaretPosition += 1
                    }
                    affinity -= 1
                }
            } else {
                if deletionAffectsCaret {
                    modifiedCaretPosition -= 1
                }
                insertionAffectsCaret = iterator.insertionAffectsCaret()
                deletionAffectsCaret  = iterator.deletionAffectsCaret()
                character   = iterator.next()
                affinity   -= 1
            }
        }
        while (null != character) {
            val next: Next? = state.accept(character)
            if (null != next) {
                state = next.state
                modifiedString += next.insert ?: ""
                extractedValue += next.value ?: ""
                if (next.pass) {
                    insertionAffectsCaret = iterator.insertionAffectsCaret()
                    deletionAffectsCaret = iterator.deletionAffectsCaret()
                    character = iterator.next()
                    affinity += 1
                } else {
                    if (insertionAffectsCaret && null != next.insert) {
                        modifiedCaretPosition += 1
                    }
                    affinity -= 1
                }
            } else {
                if (deletionAffectsCaret) {
                    modifiedCaretPosition -= 1
                }
                insertionAffectsCaret = iterator.insertionAffectsCaret()
                deletionAffectsCaret = iterator.deletionAffectsCaret()
                character = iterator.next()
                affinity -= 1
            }
        }

Here we can see some differences, because of Java related debts Kotlin needs to work with null. But in general, how many lines of code we have here related to business logic? I think 3:

...
... state.accept(...)
...
....insert...

No, sorry - only two lines. Other code works to make iterator happy.

More iterations

When you build another iteration when you finished a previous one, there is a Yandex developer crying (just kidding, not toxic).

        while (autocomplete && insertionAffectsCaret) {
            val next: Next = state.autocomplete() ?: break
            state = next.state
            modifiedString += next.insert ?: ""
            extractedValue += next.value ?: ""
            if (null != next.insert) {
                modifiedCaretPosition += 1
            }
        }
        while autocomplete && insertionAffectsCaret, let next: Next = state.autocomplete() {
            state = next.state
            modifiedString += nil != next.insert ? String(next.insert!) : ""
            extractedValue += nil != next.value  ? String(next.value!)  : ""
            if nil != next.insert {
                modifiedCaretPosition += 1
            }
        }

From a logical perspective we do the same things, just in another loop and with additional conditions.

Returning result

        return Result(
            CaretString(
                modifiedString,
                modifiedCaretPosition,
                text.caretGravity
            ),
            extractedValue,
            affinity,
            this.noMandatoryCharactersLeftAfterState(state)
        )
        return Result(
            formattedText: CaretString(
                string: modifiedString,
                caretPosition: modifiedString.startIndex(offsetBy: modifiedCaretPosition),
                caretGravity: text.caretGravity
            ),
            extractedValue: extractedValue,
            affinity: affinity,
            complete: self.noMandatoryCharactersLeftAfterState(state)
        )

I think we can make both samples more similar and still compilable, but the idea is simple.

Epilogue

You can use any language and still write in C.