-
-
Save ileasile/1016cf09608df8f9b7f4cfe2d64625f6 to your computer and use it in GitHub Desktop.
I thought we had a convention to name such kind of functions with CAPITAL letters
My comments:
- I am not a big fan of using
animateX
. It feels unnecessary when the type is also in the argument. - Would it make more sense to use
Duration
instead ofLong
for the delay. That way we encode it more directly in the type, plus make it easier to define delays at different granularities like seconds. frameFactory
argument naming looks off. Shouldn'tid
beindex
orframeIndex
?
So something like:
fun animate(delay: Duration, iterator: Iterator<Any>)
fun animate(delay: Duration, sequence: Sequence<Any>)
fun animate(delay: Duration, iterable: Iterable<Any>)
fun animate(framesCount: Int, delay: Duration, frameFactory: (index: Int) -> Any)
// or if these are capitalized pr. Romans comment
fun ANIMATE(delay: Duration, iterator: Iterator<Any>)
fun ANIMATE(delay: Duration, sequence: Sequence<Any>)
fun ANIMATE(delay: Duration, iterable: Iterable<Any>)
fun ANIMATE(framesCount: Int, delay: Duration, frameFactory: (index: Int) -> Any)
another idea:
fun <T> animate(delayMs: Long, iterable: Iterable<T>, frameFactory: (element: T) -> Any) {
animateSequence(delayMs, sequence {
iterable.forEach { element ->
yield(frameFactory(element))
}
})
}
animate(delayMs = 50, 1..50) { it }
animate(delayMs = 50, mySnapshots) { snapshotPlot(it) }
I agree with some points from Christian:
- add functions with
Duration
to existing ones - the name for the variable
frameFactory
looks strange for me
Also, we are reserving quite common name animate
Probably put the thing you want to animate at the first place. You maybe could even provide a default duration of 1 second or something.
Makes more sense to have ANIMATE(what, how)
instead of ANIMATE(how, what)
. The lambda overload being the notable exception of course. The rest looks good to me!
One thing that comes to mind is making an extension function to animate an iterable... But a part of me doesn't like the idea of having TOP_LEVEL() extension functions, so it's probably fine as it is.
To add more convinience, I'd say we might add some defaults for this parameters framesCount: Int, delayMs: Long
Maybe we could also add a default way of animating things with the help of our libraries / integrations.
Couple of them comes to mind:
fun animate(framesCount: Int, delayMs: Long, frameFactory: Plot.(index: Int) -> {Plot})
fun animate(framesCount: Int, delayMs: Long, frameFactory: (index: Int) -> {BufferedImage})
Second version of the API, more consistent with what we have
import kotlin.time.Duration
class Frame(
val delayBefore: Duration,
val content: Any,
)
fun ANIMATE(frames: Iterator<Frame>)
fun ANIMATE(sequence: Sequence<Frame>)
fun ANIMATE(iterable: Iterable<Frame>)
fun ANIMATE(firstFrame: Frame, nextFrame: (Frame) -> Frame?)
fun ANIMATE(nextFrame: () -> Frame?)
fun ANIMATE(delay: Duration, iterator: Iterator<Any>)
fun ANIMATE(delay: Duration, sequence: Sequence<Any>)
fun ANIMATE(delay: Duration, seedValue: Any?, nextValue: (Any) -> Any?)
fun ANIMATE(delay: Duration, nextValue: () -> Any?)
fun ANIMATE(delay: Duration, iterable: Iterable<Any>)
fun ANIMATE(framesCount: Int, delay: Duration, frameByIndex: (index: Int) -> Any)
fun ANIMATE(delay: Duration, seedValue: Any?, nextValue: (Any) -> Any?)
I suggest to add a type parameter here:
fun <T : Any> ANIMATE(delay: Duration, seedValue: T?, nextValue: (T) -> T?)
This way the user won't need to write it as Long
and such.
An extension of that idea would to be to make Frame
parameterized:
class Frame<out T : Any>(
val delayBefore: Duration,
val content: T,
)
This way me may have this:
fun <T : Any> ANIMATE(firstFrame: Frame<T>, nextFrame: (Frame<T>) -> Frame<T>?)
And Frame<*>
in all other overloads. But this may be a bit over-engineered.
Thanks! Parametrized version is below. It indeed makes some lambdas easier to write
import kotlin.time.Duration
class Frame<T>(
val delayBefore: Duration,
val content: T,
)
fun <T> ANIMATE(frames: Iterator<Frame<T>>)
fun <T> ANIMATE(sequence: Sequence<Frame<T>>)
fun <T> ANIMATE(iterable: Iterable<Frame<T>>)
fun <T> ANIMATE(firstFrame: Frame<T>, nextFrame: (Frame<T>) -> Frame<T>?)
fun <T> ANIMATE(nextFrame: () -> Frame<T>?)
fun <T> ANIMATE(delay: Duration, iterator: Iterator<T>)
fun <T> ANIMATE(delay: Duration, sequence: Sequence<T>)
fun <T: Any> ANIMATE(delay: Duration, nextValue: () -> T?)
fun <T: Any> ANIMATE(delay: Duration, seedValue: T?, nextValue: (T) -> T?)
fun <T> ANIMATE(delay: Duration, iterable: Iterable<T>)
fun <T> ANIMATE(framesCount: Int, delay: Duration, frameByIndex: (index: Int) -> T)
Can one of them with functional parameters be with inline
modifier? Since they only serve delegating role, it may redice them without warmup
We don't actually need type parameters in any of the overloads except (firstFrame,nextFrame)
and (seedValue,nextValue)
. They will just cause unnecessary inference problems with unclear errors, if user types something wrong or too complex, and they provide no actual benefit. We can just use Frame<*>
, Any?
and Any
.
Also, I suggest declaring Frame as class Frame<out T>(...)
.
True, agree with the above. *
and Any?
looks right as a broadest spectre
The API I currently suggest is
Probably these should be overloads of a same function?
Probably the name itself should be different?