n

    napperley

    3 years ago
    Sounds like Compose is strongly moving in the Functional direction (aka Functional DSLs). Would there be any plans in the compose language proposal on how state is managed? Do think that the compose language proposal should include the ability to initialise the context in a code block without having to pass through function parameters, so a user can use a composable API like this eg:
    // ...
    StoryWidget {
        story = StoryData
        Clickable {
            onClick = onClick
            Padding {
                amount = 8.dp
                Column {
                    Title { txtContent = story.title }
                    Image { src = story.image }
                    Text { txtContent = story.content }
                }
            }
        }
    }
    Chuck Jazdzewski [G]

    Chuck Jazdzewski [G]

    3 years ago
    We considered this syntax and there are issues with it. First it introduces into the scope symbols which are ambiguous which you see in your example
    onClick = onClick
    It might be clear you that the
    onClick
    you are setting is the
    Clickable::onClick
    but does right-hand
    onClick
    refer to the readable value of the
    Clickable::onClick
    or some outer scope
    onClick
    . Does it require
    onClick@MyWidget
    to disambiguate? Also, we want to be able to skip the call to
    Clickable
    if all the values passed to it are the same. If you can mix in statements it becomes unclear what can be skipped and what cannot be. The function call syntax already resolves these issues by having a parameters.
    n

    napperley

    3 years ago
    Biggest issue with the function call syntax is that it cannot be made free form. For instance one cannot define a variable for use in simple calculations, and function calls can't be made which limits functional composition potential.
    In the example above there is a typo. The
    onClick = onClick
    should be
    onClick = onClick@MyWidget
    which isn't ambiguous.
    Chuck Jazdzewski [G]

    Chuck Jazdzewski [G]

    3 years ago
    Even if you can disambiguate the expression, the fact you need to remember to do it is a problem as unedited version is legal and wrong.
    Can you give an example "free form" use that the current syntax is preventing?
    n

    napperley

    3 years ago
    Function call syntax is highly inflexible and makes the APIs too rigid. Always desirable to provide APIs that can provide some flexibility.
    Chuck Jazdzewski [G]

    Chuck Jazdzewski [G]

    3 years ago
    Can you give me an example?
    The example above is can be transformed into
    StoryWidget(story = story) {
      Clickable(onClick = onClick) {
        Padding(amount = 8.dp) {
          Column {
            Title(text = story.title)
            Image(src = story.image)
            Text(text = story.content)
          }
        }
      }
    }
    One large problem with the proposed syntax is that is becomes unclear what to do with:
    Padding {
      if (indented) amount = 8.dp
    }
    What is unclear is what happens when
    indented
    turns from
    true
    to
    false
    ? It is unclear what to do with this as it needs to "unset" the value
    amount
    . The current syntax never gets into this situation.
    n

    napperley

    3 years ago
    Here is the free form example:
    // ...
    StoryWidget {
        val newAuthor = "$firstName $lastName"
        validateAuthor(newAuthor)
    
        author = newAuthor
        story = StoryData
        onClick = onClick@MyWidget
        Padding {
            amount = 8.dp
            Column {
                Title { txtContent = story.title }
                Image { src = story.image }
                Text { txtContent = story.content }
            }
        }
    }
    Chuck Jazdzewski [G]

    Chuck Jazdzewski [G]

    3 years ago
    First, validation of author should be done in the model not here as composition is the wrong place for validation. That aside, it largely a matter of style as the
    val newAuthor
    can be lifted one level and you get:
    val newAuthor = "$firstName $lastName"
    StoryWidget(author = newAuthor, story = story) {
       ...
    }
    Or simply,
    StoryWidget(author = "$firstName $lastName", ...) {
       ...
    }
    gildor

    gildor

    3 years ago
    Isn't this synax requires receiver (aka own class for each component and instance of this class for scope) for each widget type and this receiver should be mutable?
    b

    bdawg.io

    3 years ago
    I find the function call syntax way more idiomatic. Is a specific nested component just a function? Is it an object constructor? The beauty of the Kotlin function/constructor syntax is why should we care which one it is if that's the interface of the component regardless? Existing XML layouts are declarative and the most awkward part of them IMO are the object references, which you would need to have if you want compose to provide builder patterns
    n

    napperley

    3 years ago
    A constructor doesn't use curly braces (not normally and if so it wouldn't be Kotlinic - making the constructor behave like a function even though it isn't). Don't agree that the function call syntax is Kotlinic (idiomatic Kotlin) since Kotlin is a hybrid programming language (can easily mix both OOP and Functional concepts seamlessly), and not a pure Functional language, which is to be taken into account with the Compose library design. Compose is using both Functional and OOP concepts even though it is mainly heading down the Functional path.
    c

    cedric

    3 years ago
    At the risk of being pedantic, the fact that
    Text()
    and others return
    Unit
    is not exactly functional.
    I don't really care personally, the rationale for this given earlier by (Leland or Adam, forgot) is convincing enough.
    s

    streetsofboston

    3 years ago
    @cedric Writing to a screen, rendering, is a side-effect and it could return Unit much like a bunch of printlns. The way I see it is that the Compose annotation could be seen as something like the functional type
    IO<Unit>
    of the function it annotates.
    I wonder what @raulraja would have to say about this being functional or not 😃
    c

    cedric

    3 years ago
    Pretty sure he'd say it's barely composable, let alone functional 😃
    themishkun

    themishkun

    3 years ago
    The syntax you proposed mixes things up. Add a receiver up and you'll find yourself in the awkward position where you can't distinguish between tree modifications and logic. Also how would you actually decompose your "freeform" into separate functions?
    And also, regarding variables and statements - you always can use
    run
    block when passing parameter, or make all of the calculations beforehand
    raulraja

    raulraja

    3 years ago
    Compose is not functional, as @cedric mentioned it returns Unit. The only useful thing you can do with a Unit return is perform effects. Also personally I dislike builders that use mutability like this. The same can be achieved with immutable data classes and a few extra comas. Also I agree that for this to be interesting it needs to bake in state management otherwise is nothing but another view building DSL with less power than most other UI frameworks like React etc.
    themishkun

    themishkun

    3 years ago
    @raulraja It actually performs state management behind the scenes.
    @Compose
    annotation adds implicit argument that handles all of the memoization and computations
    raulraja

    raulraja

    3 years ago
    @themishkun thanks!, any useful links or docs where I can read about it?
    All I found about it does not mention state or how this is done
    gildor

    gildor

    3 years ago
    If you check this channel and threads you can get many parts about implementation details, Compose team promise to publish some blogposts about it
    themishkun

    themishkun

    3 years ago
    @raulraja it is a pre-alpha release, the docs are pretty laconic about implementation, but you can check https://github.com/aosp-mirror/platform_frameworks_support/blob/androidx-master-dev/compose/runtime/src/main/java/androidx/compose/Composer.kt for the most interesting parts)
    raulraja

    raulraja

    3 years ago
    thanks!
    c

    cedric

    3 years ago
    What I find interesting is that the Compose code that you see on the screen is only the very, very tiny tip of a giant iceberg, since so much is happening behind the scenes. Trying to assess whether that code is functional (which I don’t find a very useful exercise to be honest, so this is just for the sake of argument) is actually not that trivial.
    n

    napperley

    3 years ago
    Indeed, the Compose library is doing too much magical stuff.
    Fudge

    Fudge

    3 years ago
    The
    @Composable
    is just a few syntactic sugar improvements
    n

    napperley

    3 years ago
    Annotations are still magical even though some good explanations have been provided in the article on how Compose works under the hood, which clears up some issues. It is important to realise that annotations are not like programming language keywords. One of the Compose team members is planning to put forth a Compose proposal to the Kotlin team, which if accepted would significantly reduce the in the Compose library by minimising the need to use annotations, and removes the need to use compiler plugins.
    b

    bdawg.io

    3 years ago
    It's because it becomes part of the compiler itself haha
    gildor

    gildor

    3 years ago
    I'm against including such domain specific syntax to the language, because you need not only modifier, but also runtime library for this, at least basic one, and this goes completely against current ideology of compose as unbundled library. One more problem is that @Compose is not the only annotation I don't see significant difference between @Compose and compose modifier, it a bit better synthactically and originally Kotlin had modifier-like syntax for annotations, but it was dropped because cause significant problems with tooling I'm also not sure that I would like to allow compiler plugins introduce new keywords, especially, if in case if compose, it very easy to solve using annotations and possible to do using compiler plugin, not everything should be a part of the language, I think every attempt modify language, just because of some minor style changes, just because somehow annotations are and a new language modifier is not are not just counterproductive as for composer and for Kotlin but imo just harmful, because do not allow to focus on library development, instead just cause more bikeshedding
    themishkun

    themishkun

    3 years ago
    @napperley how removing of the
    @
    suppose to reduce "magic"?
    b

    bdawg.io

    3 years ago
    We already have annotations. And if compiler plugins make them a much better experience to use than kapt, then all the better without a need for a new source code paradigm
    gildor

    gildor

    3 years ago
    How annotation vs modifier is related to kapt?
    b

    bdawg.io

    3 years ago
    I'm talking about not needing modifiers because we already have annotations. It's more of compiler plugins vs kapt
    gildor

    gildor

    3 years ago
    but nobody talks about kapt
    and it’s impossible to modify code with kapt/apt, only generate new
    this is the main point of compiler plugins
    b

    bdawg.io

    3 years ago
    Exactly what I'm saying haha
    compiler plugins make them a much better experience to use than kapt....without a need for a new source code paradigm
    gildor

    gildor

    3 years ago
    But I’m completely agree, that why introduce modifiers if annotation is just fine