Giter VIP home page Giter VIP logo

oolong's People

Contributors

dallasgutauckis avatar dependabot[bot] avatar hhariri avatar pardom avatar torresmi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

oolong's Issues

Navigation Doc - Wrong Message

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

Effect batch is not asynchronous

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) } }

Subscription support

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:

  • Get an event every X seconds
  • Subscribe to events from a websocket

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.

Effect breaks Android Build

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

  • "master" fails to build
  • "working" builds successfully because the "effect" code in Store.kt is commented out

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

Unit Test Documentation

Is there any way to unit test update ? If there's a way, is there any documentation ? Thank you

ClassNotFoundException for Runtime

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.

Logo/icon

Let's make a logo/icon instead of using the green tea emoji.

Remove ': Any' bounds in generic types

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.

Consider alternative type and syntax for Next

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:

  1. Redefine Next to data class Next<Model, Msg>(val model: Model, val effect: Effect<Msg>)
  2. Add a new infix function 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:

  • the code reads as "return the updated model and these effects", which I think is a very nice improvement
  • extensions on Next will not pollute the Pair type in case there were extensions that were Oolong-specific
  • deconstructing the Next type will have named fields instead of first and second

Cons:

  • lose access to any extensions on Pair
  • potentially breaking change to the type system

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
  • I opted against an infix operator (e.g. *) 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 discoverable

Additional context

none

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.