Replies: 18 comments 29 replies
-
For the local definition, I'd recommend (defwidget foo []
(let [(something "default")]
(button :onclick (set something "new value")
"click me"))) in order to be more consistent with the style used by the general lisp community and by that you are using in widget params. I have yet to make my own opinion on the |
Beta Was this translation helpful? Give feedback.
-
You can copy scheme's lambda syntax for that, it looks nice and lispy |
Beta Was this translation helpful? Give feedback.
-
Down the rabbit hole for lambda-within-eww we go, as well: Here's my idea: (button :onclick {=> foo = "a"}) That's a thunk, but as mentioned we might need args. I woudn't reccomend (button :onclick {=> () foo = "a"}) additionally, this gets into what argument separators you want to use. The two I know of that are frequently used is the comma ( (my-grid :onclick {=> (x y) foo = x * y}) vs (with added white-space to look nice. (my-grid :onclick {=> (x, y) foo = x * y}) That's of course assuming that you would support multiple arguments in the first place. If that's not something you'd like, the parens might not be needed, but this is ugly: (my-scrollable-thing :onclick {amount => foo = amount}) Which takes us full circle. As for the In a defunct language of my own design I played around a little bit with using raw curly brackets ( |
Beta Was this translation helpful? Give feedback.
-
I thought of a more pipes-esque syntax and haven't been able to poke a major hole in it yet: ; any number of arguments
(.... :onclick {x, y -> x * y -> foo})
; if only a single source argument and destination, store directly
(.... :onscroll {dir -> foo})
; execute functions
(.... :ondropped {uri -> replace(uri, 'regexp?', 'text') -> foo})
; source must be an argument list of the appropriate cardinality or a
; literal: this is equivalent to {x, y -> "updated" -> foo}
(.... :onclick {"updated" -> foo})
; error cases
(.... :onclick {foo}) ; must define at least a source & destination
(.... :onclick {x, y -> foo}) ; destination argument count mismatch
(.... :onclick {x, y, z -> x * y -> foo}) ; too many arguments
; not too bad stringified
(defvar handlers "[(x, y -> x * y -> foo), ('updated' -> foo)]") I guess you could Another thing to think about: scopes and namespaces. Right now I have bar and calendar widgets, and in order to display the calendar when hovering the current time in the bar I have to define a var in the global scope, write it from the bar hover event ( |
Beta Was this translation helpful? Give feedback.
-
Not sure if you are still trying to figure this out, but I had a couple of thoughts to share. Also, all of the ideas above seem like they would work well enough, once a person has understood how to program with s-expressions none of the proposals would be hard to pick up. I am not sure there is a need to add special syntax for lambdas, they essentially already exist if one uses an expression for an event. For example, today I wrote this code
This already executes an expression body when there is a click event in the same way that a no argument lambda would execute its body. To support assignment to local or global variables all you need is to add an assignment operator.
However, the above example is only useful for places where we are attaching expressions without arguments to an event. For example, the slider widget provides a value for you to use when you change it. I have something like this in a widget
In this case the
because the My suggestion is to do one of two things:
This has the advantage of perhaps being a little more readable and could be used if you want to add a more complicated lambda structure Neither of these require much additional syntax and they seem consistent with the current syntax of This does not say anything about how you might introduce syntax for local state, but imo the use cases most people have for these widgets probably work just fine with global variables. I know as programmers we must accept them to be evil, but in this case they seem to work. I have been having a really great time using EWW and I think that it's paradigm of s-expressions for declaring widgets, CSS for styling, and calling shell scripts or programs for functionality works perfectly. It keeps it simple for you and for us. Much more accessible than using a general purpose programming language and a GUI framework to create the widgets. So THANKS for the fun tool 😄 |
Beta Was this translation helpful? Give feedback.
-
Wish I had seen this before working on #578 🤦. Oh well. On thing i'd bring up for consideration1 is the swift closure syntax { arg1, arg2 -> return in /* body */ } while that is a little heavy for what we'd need, using in as the separator would mirror the proposed (but not accepted) One thing to note, potentially against adding lambdas to simplexpr at all, is that there currently isn't a way to just run a side-effect-ful expression in simplexpr. We would need some (defwidget inc-button [?onchange]
(let count 0 in
(button
:text { count == 0 ? "None!" : "Count ${count}" }
:onclick {
; Using swift as stand in for lambda
; `:=` for assignment
; `()` for invoke
; Also note that this choice of expression chaining operator, while common, would conflict with comments.
{ in
count := count + 1;
; also also note that this would probably be a common pattern peopel would try, so it
; would be pretty much necessary to have this run _with_ the side effect of the assignment.
onchange != "" ? onchange(count) : ""
}
} Footnotes
|
Beta Was this translation helpful? Give feedback.
-
Also, I'm not sure that simplexpr has to know about the scope graph. It might make more sense, from a architecture, change propogation, and self-invalidation identification perspective to have a simplexpr evaluation just provide a #[non_exhaustive]
enum SideEffect {
WritableVarUpdate(VarRef, DynVal),
ScriptExecution(String, Vec<String>)
} yuck could then propogate those updates to the proper scope for simplexpr. With my previous example a click on (inc-button :onchange {{ new in global := new }}) would produce [WritableVarUpdate("count", 1), WritableVarUpdate("global", 1)] |
Beta Was this translation helpful? Give feedback.
-
I dislike that the syntax is so lispy. Could we get an additional syntax if it has to go the lispy way? Something a bit more like CSS rules; or if you need it java/javascript syntax secondarily (well, |
Beta Was this translation helpful? Give feedback.
-
Why everyone wants lambda like expressions? Can't we just have normal functions like widget's syntax?
Then function can be like widgets:
[EDIT] It'd be really nice to have |
Beta Was this translation helpful? Give feedback.
-
I must admit I haven't looked much at the Eww source (and the little I have was a long time ago) but I imagine that widgets are basically functions that Eww (may) reevaluate whenever internal state changes? If that's the case, introducing lambdas as first class values, and letting widgets be a special form that receives an optional varlist, could be a way to separate local state and render logic. (defwidget name []
(let ((local1 "value1")
(local2 "value2"))
(button :onclick (set local1 "new value") local1))) This wouldn't work without lifting the binding to handle it internally, and even then it would be misleading. Instead the signature of (defwidget name []
"My fancy example widget"
((local1 "value1") (local2 "value2"))
(button :onclick (lamb () (set local1 "new value")) local1)) (docstrings and implicit box/&rest argument were just things that seemed like a cute addition. not at all essential to the idea) It also kind of eliminates the whole "lambda vs regular functions" thing, as it would feel intuitive, and might open up for a bunch of other cool sutff. It might however still be useful to be able to access local variables from the outside. (defwidget toggle-group []
(box
(regular-anonymous-widget)
(tag "cb1" (my-checkbox-widget "label"))
(tag "cb2" (my-checkbox-widget "foo")))) and let But I'm probably getting ahead of myself now. |
Beta Was this translation helpful? Give feedback.
-
I think the most minimal way to internally do stuff, as opposed to through shell script, is to have a way to run eww commands internally, so instead of doing On the other hand, if there already is (defvar foo "var"), I see no problem in using something like (setvar foo "var") either locally or after a condition.
then, use something like:
or:
this makes the syntax consistent in my opinion. |
Beta Was this translation helpful? Give feedback.
-
I would like to mention haskell's syntax for a lambda. It looks like this.
Lambdas in haskell has to take one argument by design. So there is no syntax for a lambda with no args. But we could do something like this.
So in a config it would look like this.
|
Beta Was this translation helpful? Give feedback.
-
I did some shower thinking and came up with a combination of things that I read above here and in #324. Basically how about we add a new
I've typed lambda too many times, lets use Oh and here's an example: (defwidget poweroff []
(let confirm false)
(let unused "Too many brackets, lets just have each var be its own let")
(eventbox :onclick (fn
(if !confirm
(let confirm true)
else
(shell systemctl poweroff)
(for nothing in "[]" (shell echo $unused "$PATH")) ; or (shell "${unused}")
; (let cant_declare_here "because only after [] and before widgets")
)) Edits
|
Beta Was this translation helpful? Give feedback.
-
What about using the lisp QUOTE or its symbolic designation `. This would also hypothetically allow to realize the conditions (button :onclick `(update foo "new-value")
"click me") |
Beta Was this translation helpful? Give feedback.
-
Idk, I like lambda syntax :) |
Beta Was this translation helpful? Give feedback.
-
(non eww user here) couldn't you do something like
|
Beta Was this translation helpful? Give feedback.
-
I'm a new eww user (literally learned about it yesterday), and I worked with Clojure a while back, and for the callback itself you could use a syntax similar to Clojure: ; Example found in stackoverflow
(println (filter (fn [x] (zero? (mod 6 x))) (range 1 10))) ; anonymous function
(println (filter #(zero? (mod 6 %)) (range 1 10))) ; shorthand version As for the local state variables themselves, you could use something like React's (defwidget mywidget []
[ ; after the parameters would be the local variables of the widget
count set-count 0 ; The syntax would be: value, function to set the value, initial value, always in threes
; [foo set-foo] ; alternatively, we could wrap value and setter in a tuple
]
(button :onclick #(set-count (count + 1)) ; I'm not sure about the math syntax here, but you get the idea
"click me") Or, if you want something closer to how React works. I used mostly Closure's syntax (defwidget mywidget []
(let [ ; I only know the syntax for Clojure's let https://clojuredocs.org/clojure.core/let
[count set-count] (use-state 0)
[foo set-foo] (use-state)
some-other-value (get-some-external-value-somehow)
]
(button :onclick #(set-count (count + 1))
"click me")
)
) |
Beta Was this translation helpful? Give feedback.
-
I recently learned Nix and I really like some concepts it has. First of all the let block looks like this: # NIX
let
x = "x";
y = "y";
in
# expr So in yuck it could look unchanged to how I originally proposed: ; YUCK
(let
(x "x")
(y "y"))
; Or even less parens + could use `:prop`
(let
:x "x"
:y "y") Then for the functions, in nix they only take 1 argument so # NIX
# `binding: body` is the function syntax
x: y: x + y
# ~1 ~2 ~~~~3
# ~~~~~~~~~4
# 1: x arg
# 2: y arg
# 3: fn body
# 4: outter function (taking x) returns this function
# Calling syntax
let
f = x: y: x + y;
in
f 2 2
# => 4 The benefit of this way is that you can partially apply ; YUCK
(fn (x y) (+ x y)) The only usecase for single argument functions would be something like this: ; YUCK
(let
double (fn num (* 2 num)))
(double 3)
; => 6 In fact we could even make it better if functions can only take one arg: make the fn syntax just ('<binding> <body>) ;YUCK
(let
double (* 2)) ; Partially applied multiplication
; improved syntax
double ('n (* n 2))
(double 4)
; => 8 The issue is, that this is inconsistent syntax: If function application looks like |
Beta Was this translation helpful? Give feedback.
-
Hey everyone!
It's time for another one of these discussions, where I'm asking for feedback on design decisions!
In #324, we're planning to add local state -- variables that can be declared within a widget, so widgets can track their own internal state.
For this to be usable, we need some way to update these local variables from within the widgets. Specifically, from within event handlers (i.e.
:onclick
,:onhover
, etc).Currently, eww just expects a simple string, that will get executed as a shellscript. This system works, but leaves no way for eww to do anything internal -- even updating global variables has to be done by running
eww update foo=whatever
in the event handler.This cannot work in the context of local variables -- we need a more powerful syntax and structure here.
Initially, my plan was to do the following:
This simple syntax would work well in this simple case, but it has a few issues:
Initially, my approach assumed that you'd always pass these
Action
s to the eventhandler directly, like shown in the previous example.Cases in which the
Action
would need to be treated as a standard value (to be usable as arguments to custom widgets, to be able to be stored in variables themselves, contained inside lists / objects, etc) where not accounted for here.Adding these
Action
s as first-class values is weird, as suddenly we'd have lisp-y syntax in eww expressions -- that is, you'd suddenly be able to do something like this:which feels very odd, and is also just somewhat annoying to implement.
The obvious alternative would be to add these actions as some other syntax, or even functions, to the eww expression system.
That could simply look like regular assignment:
foo = "a"
. However, this feels very out of place in the declarative nature of yuck, and isn't a complete solution:To be able to actually use this construct productively, we'd need to add some sort of lambda syntax, i.e.:
At which point I hope we all agree that this looks somewhat weird.
Another issue with this is that it'd introduce side-effects into eww expressions, which eww's state architecture may re-evaluate at random points in time -- although that could be seen as a case of "well, just don't do it, lol".
I'm somewhat out of ideas as to how to structure this. I feel as though having some sort of lambda syntax and adding actions to eww expressions that way would be the "cleanest" approach in many ways, but I can't come up with any better syntax or general structure for it.
Thus, I'd love for you all to give me some of your thoughts, opinions and ideas regarding the structure and syntax of this!
Beta Was this translation helpful? Give feedback.
All reactions