oolong-kt / oolong Goto Github PK
View Code? Open in Web Editor NEWMVU for Kotlin Multiplatform
Home Page: http://oolong-kt.org
License: Apache License 2.0
MVU for Kotlin Multiplatform
Home Page: http://oolong-kt.org
License: Apache License 2.0
Currently the project site is generated with GitBook. Let's investigate alternatives that allow more flexibility.
Alternatives:
Please leave your suggestions below.
Describe the bug
There is a message called SetRoute
here. This is the only reference to that message, while others refer to SetScreen
To Reproduce
NA
Expected behavior
I would expect SetRoute
to be SetScreen
Screenshots
NA
Desktop (please complete the following information):
NA
Smartphone (please complete the following information):
NA
Additional context
NA
Currently, Effect
functions are suspended, however this does not prevent them from blocking when combined with batch
. The batch
function iterates over and calls each effect. Given the current implementation, each Effect
will block the iteration until completed:
typealias Effect<Msg> = suspend (dispatch: Dispatch<Msg>) -> Any?
fun <Msg> batch(effects: Iterable<Effect<Msg>>): Effect<Msg> =
{ dispatch -> for (effect in effects) effect(dispatch) }
Instead, we should define Effect
in the context of a CoroutineScope
and launch each Effect
as we iterate over them.
typealias Effect<Msg> = suspend CoroutineScope.(dispatch: Dispatch<Msg>) -> Any?
fun <Msg> batch(effects: Iterable<Effect<Msg>>): Effect<Msg> =
{ dispatch -> for (effect in effects) launch { effect(dispatch) } }
Is your feature request related to a problem? Please describe.
I want to subscribe to some events, that might be external to my system. Example:
Describe the solution you'd like
Since the present library is inspired from elm, I'd find natural to imitate elm's subscription system (in Browser.element).
That could be translated in Kotlin like this:
data class Model(val ticks: Int = 0)
sealed class Msg {
object Tick : Msg()
}
val subscribe = Subscribe<Model, Msg> { model ->
every(10.seconds).map { Msg.Tick }
}
// ...
val dispose = Oolong.runtime(
init,
update,
suscribe,
view,
render
)
Of course this is only a draft example, and any variation of the API would do.
Describe alternatives you've considered
I think I could start a coroutine from a disposable effect that will call dispatch
to fire the messages.
That would however require me to keep an instance of the dispose in my model, so that I can cancel it later.
Describe the bug
Android application fails to build when "effect" is used
To Reproduce
I've made the following repo to demonstrate the issue - Oolong Test
Additional context
I'm using oolong-jvm because this is just going to run on Android and doesn't require using Kotlin Multi-Platform. I am unsure if that is related because everything else appears to work as expected
Related links:
Please follow up with suggestions and ideas if you have them, @oolong-kt/developers.
In the case where an effect dispatches a message before the render call, it may render the state for the effect before rendering the prior state.
In this example, a delay
was added to workaround the bug:
Is there any way to unit test update
? If there's a way, is there any documentation ? Thank you
Describe the bug
Exception:
Caused by: java.lang.ClassNotFoundException: Didn't find class "oolong.Oolong$Runtime$1" on path: DexPathList...
Cause appears to be an issue somewhere in the compilation pipeline which incorrectly cases the class name. Here is the class name in the dex file:
Class #5399 -
Class descriptor : 'Loolong/Oolong$runtime$1;'
Access flags : 0x0010 (FINAL)
Superclass : 'Lkotlin/jvm/internal/Lambda;'
Interfaces -
#0 : 'Lkotlin/jvm/functions/Function0;'
Notice oolong.Oolong$Runtime$1
vs oolong/Oolong$runtime$1
; R
vs r
in runtime.
Let's make a logo/icon instead of using the green tea emoji.
Is your feature request related to a problem? Please describe.
In Kotlin, we have the chance to have a compiler that makes safe to deal with nullability. Therefore, unlike in Java where null
is very dangerous value that must be avoided at all cost, in Kotlin null
is a valid and useful value to represent the absence of something. Yet the compiler will help us to make sure we treat it safely. On other words, the null
of Kotlin is the equiavlent of Nothing
in Elm: Useful and safe to use.
Yet, many generic functions in oolong force the generic argument to be not null. Example: fun <A : Any, B : Any> map(effect: Effect<A>, f: (A) -> B): Effect<B>
This adds an unecessary constraint on something that could have been safely nullable.
It is also viral and will cause any helpers built on top to also add that generic boundary. Example:
// Doesn't compile unless adding `: Any` bound to `T` and `R`
fun <T, R> Effect<T>.map(transform: (T) -> R): Effect<R> = map(this, transform)
By the way, as a side question: why not making map
an extension function on Effect? It would be easier to discover and natural, since in Kotlin we're use to map
over collections, sequences, flows, etc.
I aggree, that it is probably very uncomon to use nullable types for model or messages. But, in a world of safe nullability, I don't see why it should completly be prevented by the framework. In elm for instance, one can freely use Maybe
for model and messages.
Describe the solution you'd like
Remove uncessary : Any
bound on generic arguments.
Describe alternatives you've considered
For a usage point of view, I can use Optional
when I want deal with nullability in model or message. But it is not very idiomatic, we have nullability directly in the Kotlin type system.
Implement a complex application to demonstrate advanced techniques.
Use this for inspiration: https://github.com/andrejewski/raj-by-example
Is your feature request related to a problem? Please describe.
I brought this up in Slack but would like to continue the conversation here to open it to other folks that would like to participate.
The Next
type is currently typealias'd to a Pair<Model, Effect<Msg>>
. The usage of Pair
comes from the desire to use the built in tuple types that the Kotlin language offers via generics and data class (Pair
and Triple
).
Unfortunately, unlike Elm's tuples which have a nice syntax for creation with parentheses (e.g. (1, 2, "a", "b")
), Kotlin relies on constructors for its data classes with no short-hand replacement. To work around that, the language offers an infix function to
to aid in Pair
's creation.
This leads to idiomatic Oolong code in the Update
/Init
functions looking somewhat like this:
val update: Update<Msg, Model> = { msg, model ->
model to none()
}
This syntax can be a little hard to decipher, especially as a novice reader of the code. It requires some knowledge of the Oolong type definitions to understand why this syntax is being used. The keyword to
reads more like a mapping than a union (to me, at least), and I think there's an opportunity here to be more explicit with types and have a more readable syntax.
Describe the solution you'd like
Give an explicit type to Next
and provide a more ergonomic infix operator for its creation to lean in to the Kotlin language features and tools.
Two steps to this solution:
Next
to data class Next<Model, Msg>(val model: Model, val effect: Effect<Msg>)
and
: infix fun <Model, Msg> Model.and(effect: Effect<Msg>) = Next(this, effect)
This changes our above example to read like this:
val update: Update<Msg, Model> = { msg, model ->
model and none()
}
Advantages:
Next
will not pollute the Pair
type in case there were extensions that were Oolong-specificNext
type will have named fields instead of first
and second
Cons:
Pair
note: I'd expect that we provide a to
infix for compatibility initially, but have that deprecated
@Deprecated(
message = "prefer `and` operator, `to` will be removed in a future update",
replaceWith = ReplaceWith("this.and(effect)")
)
infix fun <Model, Msg> Model.to(effect: Effect<Msg>) = Next(this, effect)
The problem that this doesn't solve is the syntax for the optional Effect case, I think ideally you'd be able to have a syntax that supports returning just the model or returning the model and effects, e.g.
val update: Update<Msg, Model> = { msg, model ->
model
}
Not sure what might be available for that solution (outside of a compiler plugin that allows for a more ergonomic tuple creation syntax)
Describe alternatives you've considered
with
would be an acceptable alternative to and
but it is already a reserved word in the Kotlin language*
) since that is not a standard Kotlin language feature and would likely be considered a bit obtuse in the way that to
is today with the added negative of being less discoverableAdditional context
none
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.