I get the feeling that this is a horrible way to do things, and I’m sure there is a Kotlin beautiful way to do it.
When you need to iterate through something, but you need to do some setup the first time, is there a better structure that could handle it? (like a fold or a nicer use of elvis)
Example 1: a frame grabber that needs a call to “start()” This works fine, but I’d much prefer if the grabber.start() and grabber.close() (and the grabber in general) weren’t left floating, and instead were tucked into the generateSequence block.
val grabber = FFmpegFrameGrabber("input.mp4")
grabber.start()
val frames = generateSequence { grabber.grabImage() }
frames.forEach { ... }
grabber.close()
Example 2: Walk a path, but the first step needs to be from the document root instead of the current element. (not a problem in DOM because it IS an element, but is a problem with firestore). All the lateinit and index==0 feels… smelly.
private val docRef: DocumentReference = {
require(path.isNotEmpty()) { "Empty path: Can't insert documents into a firestore root."}
lateinit var tmpDocRef: DocumentReference
path.forEachIndexed { it, (collectionId, documentId)->
tmpDocRef = if(it==0) {
db.collection(collectionId).document(documentId)
} else {
tmpDocRef.collection(collectionId).document(documentId)
}
}
logger.info { "Attached to ${tmpDocRef.path}" }
tmpDocRef
}()
Note that this way you need to iterate the sequence to the end, otherwise grabber would be left open. One has to be careful not to call some early terminating operation on that sequence, such as take(n), any { ... } / all { ... } etc.
Please note the issue cited by @ilya.gorbunov, you can replace the iterator pattern with the visitor pattern, so consider to define the function:
fun FFmpegFrameRecorder.forEachFrameIndexed(...)
The example 2 looks like a fold, using initial = db.collection(collectionId).document(documentId) and operation = tmpDocRef.collection(collectionId).document(documentId)
Moreover consider to replace the { ... }() syntax with a bit more explicit run { ...}
Things like close() should be called in finally { ... } clause for extra safety. So IMO, it’s not good to put entire iteration calls inside some kind of sequence, because sequence is not guaranteed to finish.
The way I would do this is to have an extension function FFmpegFrameGrabber.forEachImage that basically encapsulates all the access logic (including the closing of it, which should be ensured with use or try finally).
For your second problem I would go to the idea of creating extension functions like first and rest that allow you to explicitly access things. Depending on the actual underlying source they can be syntactic sugar if the act of reading the first element consumes it (so only the rest is there)