-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Make frame render functions return the frame. #474
Proposal: Make frame render functions return the frame. #474
Conversation
743cb75
to
915aea0
Compare
We can't do these changes, because it means any code today that does |
I see. The problem would be if you create a frame in one cell, then update/render it in another. Without a I tend to co-locate my frames with the code necessary to render into them in the same cell, which (without a fluent API) necessarily means the frame can't be the implicit output. (I have to assign it, render with But yeah, dividing the frame creation and rendering between cells could lead to double-embedding issues, makes sense.
Would it make sense to have them return a more intention-revealing |
I'm not convinced that this DX is a bad thing: if you are creating a frame in one cell and using it in another, you are probably experimenting with frames (and might want to see the intermediary output). As opposed to building a re-renderable UI in a single cell (and necessarily co-locating listeners after frame initialization, where you need a handle to it). A simple |
Side note: if we do decide on this, releasing as a potentially breaking change, we can probably drop the |
Hey @christhekeele :)
To render a group of outputs, a preferred way would be using grid, so that it's a single render.
We changed Also, there is a bit of a difference in that, when you call Either way, I don't think it's worth the breaking change at this point. |
On a sidenote, instead of frame = frame() |> Kino.render()
...
Kino.nothing() I would consider this more idiomatic: frame = frame()
...
frame It's less code and arguably makes it easier to change if you want to render a composition of elements, such as But that's definitely subjective. I can see people preferring the first snippet, if they are less familiar with the notion of "cell outputs effectively being |
Hey @jonatanklosko! Thanks for your explanations, they are instructive.
In my case, I've been finding myself adding more outputs conditionally, so they don't reliably fit into a specific size grid, and frame chainability with
Some of this is definitely me learning new idioms for UI, evolving from simple inputs to packing forms and frames. In my case, what I'm experimenting with is keeping cohesive bits of related complex UI concerns grouped in single code cells, with my final output being |
PR Summary
There is one discussion point in my mind yet unresolved, which is
I maintain that return However, that is separate from what this PR was exploring—so I will close, and allow someone to take the point above into an issue/discussion if there is interest beyond my own. Thanks for the discussion everyone! |
I think size shouldn't be a problem, by default it's a single column, so my point is that this: Kino.Frame.append(frame, Kino.Markdown.new("foo"))
Kino.Frame.append(frame, Kino.Markdown.new("bar"))
Kino.Frame.append(frame, Kino.Markdown.new("baz")) looks the same as this: Kino.Frame.render(
frame,
Kino.Layout.grid([
Kino.Markdown.new("foo"),
Kino.Markdown.new("bar"),
Kino.Markdown.new("baz")
])
) You can build the list conditionally, but if you prefer append, I think that's fine.
It's not leaking implementation, we return |
This accomplishes three things in the name of pipe-ability:
Kino.Frame.render
consistent withKino.render
This also lets you do things like this:
Kino.Frame.append(frame, term)
more chainableFor example:
Kino.Frame.clear(frame)
chainableFor example:
This implementation wraps the
GenServer.call
s withwith
, which technically may return something other than:ok
, but effectively makes the same assumptions that the previous typespec did that it will always be:ok
. We could just match on:ok
(like inclear
'scast
) or even ignore the result of the call, but this is the most backwards-compatible with any existing code today that tries to handle non-:ok
GenServer.call
results (I would be suprised if any such code was out there, though).