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.