Fudge

    Fudge

    3 years ago
    Text("Hello World")
             .padding()
    Seems nicer than
    Padding {
                Text("Hello World")
             }
    I have to be honest.
    miha-x64

    miha-x64

    3 years ago
    It doesn't. I don't know which widgets have
    padding()
    , why, and how should I support it in my custom widgets. External margins/paddings feel natural and simple.
    Fudge

    Fudge

    3 years ago
    All widgets can have
    padding()
    , just like you can nest any widget in
    Padding{}
    . Padding is a modifier added to a widget, in which case a widget extension method is in order and not a "helper method" that receives a widget. This has all of the added benefits of using extension methods over "helper methods" - being more discover-able and being easier to apply on already typed objects. This also has the obvious upside of reducing nesting.
    louiscad

    louiscad

    3 years ago
    @Adam Powell Would composable extensions be possible for generic things like padding? I have to admit I'm a bit afraid of readability with nesting for simple things like that, and when I saw Compose example in I/O talk, I immediately wondered why so much nesting for just a padding. I'd like to also have things like
    Text(someLongString).wrapInScrollContainer()
    . In fact, that's how I'm currently doing it with Views, using
    wrapInScrollView()
    or
    wrapInRecyclerView()
    extension functions.
    r

    romainguy

    3 years ago
    @Fudge it's also how our base view class became so big
    louiscad

    louiscad

    3 years ago
    @romainguy If Compose was based on returned values, extensions would work as they would return a padding widget wrapping its receiver, without having a 30K LOC base View class. I know it doesn't work that way in Compose, but would such a syntax be possible?
    Fudge

    Fudge

    3 years ago
    Having a big class as a result of this doesn't seem like a very big drawback. And also not a thing when you have extension methods, unless you want some kind of inheritence
    miha-x64

    miha-x64

    3 years ago
    but would such a syntax be possible?
    Annotation-based Unit-returning bullshit, with implicit receiver, of course, should be reworked.
    Fudge

    Fudge

    3 years ago
    You can always introduce more magic
    Might not be worth the effort though
    elizarov

    elizarov

    3 years ago
    Developer ergonomics do require a lot of magic. There is simply no way around it. There are tradeoffs every framework makes with this. Look at Swift UI, for example. Widgets are structs which are essentially classes (so last-century), but when you construct them they "automagically" add themselves to container even though the return value of their "constructor" is simply ignored. Yet if you apply ".padding()" they magically modify themselves, so are they also mutable? Or do they magically add themselves to parent if you don't use the result type? (Edit: the latter in fact)
    We know there are the ways to add a component to a parent's list of children: • Explict (flutter) -- inconvenient boilerplate • Implicit with a separate language (like React JSX) -- extremely intrusive, all magic • Implicit at creation time (compose) • Implicit at "result type drop" (Swift UI)
    All convenient solutions are magical. Just pick which one looks more natural to you.
    Fudge

    Fudge

    3 years ago
    I pick the SwiftUI approach, but that doesn't mean I get to use it for Android development 😦
    elizarov

    elizarov

    3 years ago
    In fact, it does not really matter. If you format them similarly they don't look different enough to matter:
    Text("...").padding()
    vs
    Padding { Text("...") }
    It is a matter of taste, style, and habit. I, for one, have been programming with Swing for too much, so the second one looks more natural for me.
    Fudge

    Fudge

    3 years ago
    That's not a fair example
    Padding is usually applied to 5-20 lines of code, in which case you might not want to put it all on one line
    r

    romainguy

    3 years ago
    @Fudge But then in SwiftUI you’ll need a way to group those 5-20 lines of widgets just to call
    .padding()
    on them
    elizarov

    elizarov

    3 years ago
    I'm worried about Swift's
    Text("...").color(...)
    example. I'll have to dig how it really works, but it looks like it have to mutate 😱 the view, which makes the whole thing not-so-very-reactive-at-all
    @Fudge If you repeat something over 5-20 lines of code that's where I'd typically suggest "extract function" refactoring to avoid this repetition. I personally start thinking about it at the second repeat.
    Fudge

    Fudge

    3 years ago
    Sorry, I don't see how that is related @elizarov (regarding the last comment)
    r

    Ruckus

    3 years ago
    I believe he mistook your "applied to 5-20 lines of code" to mean "repeated 5-20 times in code"
    elizarov

    elizarov

    3 years ago
    Maybe I did not understand what do you mean by "applied to 5-20 lines of code". I though you mean apply padding to 5-20 different components. If you mean make 5-20 different components and make padding around then it is exactly how Compose does it in the best way.
    r

    Ruckus

    3 years ago
    @elizarov I think he meant more like
    Padding { <20 lines here> }
    vs
    <20 lines here>.padding()
    elizarov

    elizarov

    3 years ago
    Got it
    r

    Ruckus

    3 years ago
    Which, admittedly, makes me lean more towards @romainguy's version, though it comes right back to your statement that it's a matter of taste/style/habit.
    r

    romainguy

    3 years ago
    We want to emphasize composition
    r

    Ruckus

    3 years ago
    Oh, well, you should have just named it that way then :trollface: Sorry, couldn't help myself
    r

    romainguy

    3 years ago
    I guess ^^
    elizarov

    elizarov

    3 years ago
    Frankly, I also like immutability that compose adds to mix (no changing color after the fact), but making immutability ergonomic is quite challenging.
    r

    romainguy

    3 years ago
    It is
    elizarov

    elizarov

    3 years ago
    To be fair here, we still have not really figured out ergonomic immutability for the Kotlin language itself.
    Fudge

    Fudge

    3 years ago
    (regarding a way to group widgets) @romainguy you mean with brackets? The usual case is that there is only one child so there's no real need for that. When you have multiple children you usually want to have a designated component for that (row, column, scrolling, etc). So you can just use
    padding
    at the end of the
    Row
    for example. It definitely may be that there is enough cases in which you have multiple children without a layout widget wrapping them, but I doubt that.
    moetouban

    moetouban

    3 years ago
    flutter way
    Padding(
      padding: EdgeInsets.all(8.0),
      child: const Card(child: Text('Hello World!')),
    )
    which uses padding as a widget.
    elizarov

    elizarov

    3 years ago
    The open design of swift language in its glory....
    Tudor Luca

    Tudor Luca

    3 years ago
    Yup 😒
    kioba

    kioba

    3 years ago
    I am not necessarily in favour of
    Text("").padding()
    but Kotlin capable of supporting both of this solution.
    Widget.padding() : Widget = Padding { this }
    the unit return types just removes this great Kotlin language feature from the developers.
    louiscad

    louiscad

    3 years ago
    Wondering if making it an extension over
    Unit
    that is annotated with
    @Composable
    could work. Maybe it's as simple as that?
    miha-x64

    miha-x64

    3 years ago
    Such a question will appear every minute if the framework ignores language rules and invents its own.
    dalexander

    dalexander

    3 years ago
    I would wonder about this from the perspective of "What do we want the syntax to look like, then what would Kotlin need to do to support our desired syntax?" It seems like improving Kotlin's DSL building capabilities wouldn't be unreasonable if there were some general extensions that could cover whatever was needed to make compose look good.
    miha-x64

    miha-x64

    3 years ago
    DSLs are workarounds for situations when you don't need to build an object graph but • wrap something imperative and mutable (Anko and Splitties) • write immediately to stream (kotlinx.html)
    dalexander

    dalexander

    3 years ago
    Your argument is that a DSL isn't appropriate for building GUIs?
    elizarov

    elizarov

    3 years ago
    DSLs are not workaround. DSLs are a very important languages feature in the modern world. Actually, Apple design document for Swift DSL has good write up showing “from first principles” why a language needs a special DSL support, because otherwise you’d have to write a lot of boilerplate. It is as good in this sense as the post that @Leland Richardson [G] wrote, showing what kind of boilerplate you’d have to write without a special
    @Compose
    compiler plugin.
    miha-x64

    miha-x64

    3 years ago
    Object graphs can be built without DSLs, in more explicit ways. By using constructors, possibly with vararg parameter.
    elizarov

    elizarov

    3 years ago
    Indeed. Both Apple’s design docs and Lelend’s post show how you can explicitly do it in “stock” language. Take a look. It is pretty self-explanatory.
    They end up proposing different kinds of compiler magic to make it bearable for end users to write, (mostly owning to different prevailing traditions and idioms in Kotlin and Swift languages, even though it looks quite similar for end users), but nonetheless some kind of language magic is required. It will not be nearly as useful without it.
    miha-x64

    miha-x64

    3 years ago
    I'm not familiar with Apple docs. But (explicit) Flutter UI-framework looks OK. (despite their poor language!)
    elizarov

    elizarov

    3 years ago
    Flutter-like approach is Ok, but it can be made better. It is known to be error-prone and verbose, with non-trivial learning and gotchas for novices. It does not mean it is bad, though. It is just how progress works. People do something, play with it, use it, find issues, identify common error patterns, iterate, improve, build better solutions. It is never ending. The very moment better solutions come out of the door the cycle repeats. It will be always better in the next generation.
    A shorter version: The fact that something is Ok should not stop you from trying to improve it.
    For example, Rx is Ok. But we are working on Kotlin Flows.
    miha-x64

    miha-x64

    3 years ago
    Compiler plug-ins are all about non-trivial gotchas! Can't agree that Rx is OK. Its quality is far lower beyond my vision of 'ok'.
    themishkun

    themishkun

    3 years ago
    Wow, 51 replies bikeshedding about if kotlin-ish dsl is better that java-style builder. And that’s all in the __kotlin__‘s slack channel
    elizarov

    elizarov

    3 years ago
    Thanks god slack has threads!
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    What about setting up a direct parent as a argument. similar to styles?
    @ComposableStyle
    fun CustomPaddingAndThings() {
    
    } 
    
    @Composable
    fun SomeComponenet(directParent = CustomPaddingAndThings()) {
    
    }
    I'm unable to format it any better for some reason
    Fudge

    Fudge

    3 years ago
    try ```
    So
    @Composable
    fun SomeComponent (directParent = CustomPaddingAndThings()) {
    
    }
    would be the same as
    So @Composable
    fun SomeComponent = CustomPaddingAndThings() {
    
    }
    ?
    If so, it doesn't sound very useful
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    no
    It was a quick draft
    Fudge

    Fudge

    3 years ago
    Please explain further then
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    I meant, creating a composable reference point that can be added
    like creating an id for a style function
    it should be more like
    @Composable
    fun SomeComponent (directParent = SomeClass::CustomPaddingAndThings) {
    
    }
    Adam Powell

    Adam Powell

    3 years ago
    To the original comparison, there are a few API differences in our approaches to components that I think justify the differences in syntax and why a trailing, builder-style
    .padding()
    is undesirable for compose. In compose, like in flutter, padding is a layout component of its own used as a wrapper, not a property of a base View class. There's nothing special about it in the compose framework, anyone could write it using public API. And perhaps more importantly, anyone else writing their own components doesn't have to think about how to correctly handle padding in their component implementation - the caller will add a
    Padding {}
    wrapper in the right place if it's desired.
    In builder-style code there's an expectation that fluent property set methods are not order dependent, but padding's place in the resulting emitted UI is very significant
    if padding is a wrapper rather than a property of every View, the semantics are quite different and worth highlighting with an API shape that emphasizes those differences
    so then what's left is the unfortunate indentation march when you use several wrappers together, which is where some language thinking might be useful 🙂
    for example, if you could do a form of partial application of several functions with trailing lambdas in a single go, things might get a little more pleasant:
    Card(...) {
      Padding(...) {
        TODO("content")
      }
    }
    vs.
    Compose(Card(...), Padding(...)) {
      TODO("content")
    }
    I'd rather not have additional parameters of composable functions written by users that get added by magic, nor do I want those composable function authors to have to think about the additional contingencies leading to some sort of standard boilerplate expected to be present in library-grade components - eliminating that kind of boilerplate is kind of the point 🙂
    so extra parameters on every component to supply these sorts of things are out
    Fudge

    Fudge

    3 years ago
    Compose(Card(...), Padding(...)) {
      TODO("content")
    }
    Is such a construct not available right now?
    Adam Powell

    Adam Powell

    3 years ago
    no, it's just something we've mused about in chat and whiteboard discussions
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    Your'e trying to direct people into writing custom components that contains things like padding right?
    Adam Powell

    Adam Powell

    3 years ago
    We're trying to direct people into writing components that accept
    @Composable() () -> Unit
    function references if they need to display arbitrary content, and in many cases the caller can add padding around their content they put there if they want it. Some designs might call for padding emitted by the container before calling the provided function, some might not.
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    That's one way to make it less verbose and "boilerplaty" but I have a feeling that it's going to be misused easily
    Adam Powell

    Adam Powell

    3 years ago
    But yes, writing a custom component for something you use more than a few times is meant to be easy here, e.g.
    @Composable fun PaddedCard(padding: ..., content: @Composable() () -> Unit) = Card {
      Padding(padding, content)
    }
    with the usual extract method refactorings making it smoother, etc.
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    Will the ide be able to show extraction suggestions when there is a code reuse?
    in the same scope
    Adam Powell

    Adam Powell

    3 years ago
    Ideally yes 🙂
    which is easy for me to say because I don't work on that part 😄
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    In our field we call it delegation
    Adam Powell

    Adam Powell

    3 years ago
    what kind of misuse are you uneasy about?
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    Things like component that should be used in multiple classes or a more complicated element that needs to be manipulated based on multiple data sources
    Adam Powell

    Adam Powell

    3 years ago
    can you give an example?
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    Not something specific but the same issues that we're caused by a misused
    <include>
    element
    Or a complicated text field with some specific design that you will see a lot of copy and paste.
    Adam Powell

    Adam Powell

    3 years ago
    using composable lambdas for content largely breaks you out of the
    <include>
    tag problems since you can use the lexical scope of the caller to perform more logic/provide parameters for deeper content
    this was by far my biggest, "aha!" moment about this whole model that occurs in pretty much all reactive systems
    we might be thinking of different problems with include tags 🙂
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    I don't know why but I have a feeling like unless you have a good architecture this entire thing is going to end up like a god activitiy
    Adam Powell

    Adam Powell

    3 years ago
    Tell me more, because I feel the opposite 🙂
    Gil Goldzweig

    Gil Goldzweig

    3 years ago
    Because the entire thing is based on model "observing" for changes it's going to become a problem when you need to combine information from multiple sources. For example database and network that now are handled inside functions and just update the view. here people will just replace the entire root because they can't access the component outside of it's scope
    As i said It's only a feeling. probably because there is not enough documentation out
    nwh

    nwh

    3 years ago
    To me, the main advantage of the composable structure is that it shows the hierarchy of the layout directly in the code. I think the main qualm with the padding example is considering "padding" to be an entire container, not just a property of another view, in which case it makes sense. @Adam Powell your example of single go passing is a great compromise IMO. It keeps the hierarchy clear while also bringing back a good option for succinctly repeating properties.