Open wilbennett opened 2 years ago
Alternative that does not need any new syntax:
[<ReactComponent>]
static member Counter() =
let (count, setCount) = React.useState(0)
Html.div <| fun prop ->
prop.style <| fun style ->
style.backgroundColor color.aliceBlue
prop.children <| fun html ->
html.h1 count
html.button <| fun prop ->
prop.style <| fun style ->
style.marginLeft 5
style.marginRight 5
prop.onClick (fun _ -> setCount(count - 1))
prop.text "Decrement"
html.button <| fun prop ->
prop.style <| fun style ->
style.marginLeft 5
style.marginRight 5
prop.onClick (fun _ -> setCount(count + 1))
prop.text "Increment"
Another option, my personal favorite for UI is using CE's:
This way is also already possible and no language changes are needed.
[<ReactComponent>]
let Counter () =
let (count, setCount) = React.useState(0)
Html.div {
style
[ style.backgroundColor color.aliceBlue ]
Html.h1 { count }
Html.button {
style
[ style.marginLeft 5
style.marginRight 5 ]
onClick (fun _ -> setCount(count - 1))
text "Decrement"
}
Html.button {
style
[ style.marginLeft 5
style.marginRight 5 ]
onClick (fun _ -> setCount(count + 1))
text "Increment"
}
}
@uxsoft Your code has a brace mismatch error.
@uxsoft Your code has a brace mismatch error.
Thanks, fixed.
@uxsoft Your brace mismatch illustrates my point. Who wants to keep track of matching end tokens?
I would love to be able to do this kind of stuff with CEs but there are two problems.
@kerams care to explain the downvotes? Is it the syntax or the idea in general?
@uxsoft BTW, if you do have an example of working CEs that nest like that, please share! I've been trying to do it with no success.
@WilBennettJr What about my code?
Oh, sorry @Happypig375, that approach is nice as well. It is a little bit more verbose but I believe Fable uses that technique too - for selectively populating a record, I think. It definitely makes copy/paste easier. I'm tempted to use that instead of classes for my next DSL. I think I can live with being more verbose than I can with trying to match up end tokens. :)
Thanks for the suggestion!
BTW, if you do have an example of working CEs that nest like that, please share! I've been trying to do it with no success.
Library for Fable: https://github.com/uxsoft/fable.builders.antdesign Example: https://github.com/uxsoft/Fable.Builders.Website
Who wants to keep track of matching end tokens?
Never had an issue with that, all IDE's I used do a pretty good job at brace matching
BTW, if you do have an example of working CEs that nest like that, please share! I've been trying to do it with no success.
Library for Fable: https://github.com/uxsoft/fable.builders.antdesign Example: https://github.com/uxsoft/Fable.Builders.Website
Who wants to keep track of matching end tokens?
Never had an issue with that, all IDE's I used do a pretty good job at brace matching
BTW, why not extend the proposed syntax sugar to CEs? It smells of the C spirit from the abundance of curly braces. ;-) Example from https://github.com/uxsoft/fable.builders.antdesign :
open Fable.Builders.AntDesign
let view model dispatch =
Content {
PageHeader {
title (str "Login")
subTitle (str "Please log-in to enter.")
}
Form {
style [ MaxWidth "320px"; Margin "0 auto" ]
onFinish (fun values -> dispatch (BeginLogin(string values.["username"], string values.["password"])))
FormItem {
name "email"
key "login-email"
rules [
[ FormRule.RuleType FormRuleType.Email
FormRule.Message "This isn't a valid email" ]
[ FormRule.Required true
FormRule.Message "This field is mandatory" ] ]
Input {
prefix (basicIcon icons.MailOutlined { style [ Color "lightgray" ] })
placeholder "Email"
}
}
FormItem {
name "password"
key "login-password"
rules [
[ FormRule.Required true
FormRule.Message "This field is mandatory" ] ]
Password {
prefix (basicIcon icons.LockOutlined { style [ Color "lightgray" ] })
}
}
FormItem {
key "login-submit"
Button {
style [ Width "100%" ]
buttonType ButtonType.Primary
loading model.IsLoggingIn
htmlType ButtonHtmlType.Submit
str "Login"
}
}
FormItem {
key "login-links"
Button {
buttonType ButtonType.Link
str "Register"
}
Button {
style [ Float FloatOptions.Right ]
buttonType ButtonType.Link
str "Forgot password?"
}
}
}
}
could be rewritten like this:
open Fable.Builders.AntDesign
let view model dispatch =
Content |{
PageHeader |{
title (str "Login")
subTitle (str "Please log-in to enter.")
Form |{
style [ MaxWidth "320px"; Margin "0 auto" ]
onFinish (fun values -> dispatch (BeginLogin(string values.["username"], string values.["password"])))
FormItem |{
name "email"
key "login-email"
rules |[
|[ FormRule.RuleType FormRuleType.Email
FormRule.Message "This isn't a valid email"
|[ FormRule.Required true
FormRule.Message "This field is mandatory"
Input |{
prefix (basicIcon icons.MailOutlined { style [ Color "lightgray" ] })
placeholder "Email"
FormItem |{
name "password"
key "login-password"
rules |[
|[ FormRule.Required true
FormRule.Message "This field is mandatory"
Password |{
prefix (basicIcon icons.LockOutlined { style [ Color "lightgray" ] })
FormItem |{
key "login-submit"
Button |{
style [ Width "100%" ]
buttonType ButtonType.Primary
loading model.IsLoggingIn
htmlType ButtonHtmlType.Submit
str "Login"
FormItem |{
key "login-links"
Button |{
buttonType ButtonType.Link
str "Register"
Button |{
style [ Float FloatOptions.Right ]
buttonType ButtonType.Link
str "Forgot password?"
or even extreme:
open Fable.Builders.AntDesign
let view model dispatch =
Content |{
PageHeader |{
title (str "Login")
subTitle (str "Please log-in to enter.")
Form |{
style |[ MaxWidth "320px"; Margin "0 auto"
onFinish (fun values -> dispatch (BeginLogin(string values.["username"], string values.["password"])))
FormItem |{
name "email"
key "login-email"
rules |[
|[ FormRule.RuleType FormRuleType.Email
FormRule.Message "This isn't a valid email"
|[ FormRule.Required true
FormRule.Message "This field is mandatory"
Input |{
prefix (basicIcon icons.MailOutlined |{ style |[ Color "lightgray" )
placeholder "Email"
FormItem |{
name "password"
key "login-password"
rules |[
|[ FormRule.Required true
FormRule.Message "This field is mandatory"
Password |{
prefix (basicIcon icons.LockOutlined |{ style |[ Color "lightgray" )
FormItem |{
key "login-submit"
Button |{
style |[ Width "100%"
buttonType ButtonType.Primary
loading model.IsLoggingIn
htmlType ButtonHtmlType.Submit
str "Login"
FormItem |{
key "login-links"
Button |{
buttonType ButtonType.Link
str "Register"
Button |{
style |[ Float FloatOptions.Right
buttonType ButtonType.Link
str "Forgot password?"
-19% lines of code without sacrificing readability. I like it.
I'm not completely against the idea of something like this, but I definitely don't like the specific proposed syntax. All these open braces without corresponding close braces are going to confuse the hell out of every text editor not specifically tuned to understand them. And probably out of beginners too.
Using an opening-only collection syntax feels like a footgun to me. For example, here is a case where the compiler can detect that a line is incorrectly indented and raise an error:
List.sum [
1
List.sum [
2 // error: this value is not a function and cannot be applied
3
]
]
With an opening-only syntax, incorrect indentation would actually change the logic, not cause an error.
List.sum [[
1
List.sum [[
2
3 // incorrect indenting causes `3` to be part of the outer collection
In my opinion, the confusion of adding an opening-only collection syntax would far outweigh any potential gains. If the goal is just to remove closing braces on dedicated lines, there are styles that can do this without sacrificing the benefits of a closing brace.
Html.button [
prop.onClick (fun _ -> setCount(count + 1))
prop.text "Increment" ]
// or
Html.button
[ prop.onClick (fun _ -> setCount(count + 1))
prop.text "Increment" ]
@bisen2 @Tarmil
Thanks for your input! Sorry for the late reply.
I'm fine with whatever syntax achieves the underlying goal. Showing opening braces is just an example to get the point across. F# already supports indentation based grouping and only adds these legacy C style relics in a few places (CEs, records, etc...).
To your example specifically @bisen2, this is no different than declaring nested functions - replace "List.sum" with a "let f () = ..." and it's the same concerns as everyone already deals with today. Your example may be an error or it may not be - that particular example is valid as it is. Again, the same as if it were nested functions.
Really.... Think about it for a minute.... Imagine a world where F# had braces when defining functions and this suggestion is to use indentation instead. You are basically saying it's a bad idea, the IDEs won't understand, etc... Yet, that's what it is today and I don't think anyone has an issue with it! As for newbies, if they don't have an issue with module/function/DU indentation, why would they have a problem with this? If anything, it's more consistent. As a newbie, I had to remember: use braces when defining a record but not when defining a class or interface. Not that it's a big deal, but that does not seem "easier" to me. Even C# is consistent there (except for the new syntax sugar now).
Having closing braces on separate lines or not is not the issue. Yes, typing the code in the IDE is fine. Where you run into issues is when you want to read/understand/refactor. If you have a whole stack of nested code like this and you want to copy a portion into a whole stack of other nested code, you end up shuffling end tokens most of the time. Personally, I think it makes the code noisy and adds no real benefit - the same argument for not having braces when defining functions. It seems a bit ironic actually. This is one of the big things touted as to why F# is easier to read and has "smaller" code than C# so I don't understand why there would be opposition since it is used practically everywhere else (module, function, DU, etc...)!
Not to keep harping on the similarities here but I'll say the following. If someone can make an argument against this that doesn't also apply to not having braces for modules/functions/DUs, I think the opposition will make more sense to me. Thinking about it a bit abstractly, a module is a list of functions, a function a list of expressions (including other functions), and a DU is a list of cases. None of these require delimiting tokens. Why does record/CE even use them? Classes/interfaces don't.
I look forward to reading your responses and thanks again for your input.
BTW, if you do have an example of working CEs that nest like that, please share! I've been trying to do it with no success.
Library for Fable: https://github.com/uxsoft/fable.builders.antdesign Example: https://github.com/uxsoft/Fable.Builders.Website
Who wants to keep track of matching end tokens?
Never had an issue with that, all IDE's I used do a pretty good job at brace matching
@uxsoft apologies for the late reply.
That is awesome! I haven't had time to look at it in depth yet. I hope you don't mind if I steal the technique! I'll have to see how well it plays with intellisense and how discoverable it is for a newbie to a DSL like this. I'm optimistic.
Oh, also @bisen2:
I couldn't simply copy and paste from your last examples. I would have to remove either the leading or trailing tokens, depending on the line I copied.
To be clear, it's not like these are end of the world type concerns. Just that it interrupts your flow and seem totally unnecessary to me.
Using keywords instead of brackets:
Parameter is a list:
Html.div listof
Html.h1 "hello"
Html.h2 "World"
Parameter is a sequence:
Html.div seqof
Html.h1 "hello"
Html.h2 "World"
Parameter is an array:
Html.div arrayof
Html.h1 "hello"
Html.h2 "World"
I agree with @Tarmil, and to add to this:
I'm not completely against the idea of something like this, but I definitely don't like the specific proposed syntax. All these open braces without corresponding close braces are going to confuse the hell out of every text editor not specifically tuned to understand them. And probably out of beginners too.
Two additional things to consider:
And so an alternative to using close braces would have be eminently readable, discoverable, and understandable to be accepted in my opinion. I don't think the closest might be the plain english keywords, but that itself would imply massive changes elsewhere, supplanting symbolic syntax for type declarations and clarifying the scope of an expression.
I propose we ... (describe your suggestion here)
Add some syntax sugar to the creation of collections. This will make some DSLs cleaner and a LOT easier to refactor.
The existing way of approaching this problem in F# is ...
Here is a sample DSL snippet from Feliz:
That is only a label and two buttons! The noisiness of the brackets makes this code harder than necessary to read and refactor (this is just a simple example - add more functions and it gets really crazy!). F# already is indentation aware so I believe it can leverage that to provide a much cleaner output without having to try to match up end tokens (which is even more of a problem when pasting from somewhere else).
The idea is that we can use indentation to end the definition of collections instead of requiring closing tokens. The start tokens can be whatever makes sense - I used [[ for list in the prior example. Here are some usages with different tokens:
Pros and Cons
The advantages of making this adjustment to F# are ...
Code will be much less noisy, easier to poor man's refactor (cut & paste), and I believe more readable.
The disadvantages of making this adjustment to F# are ...
Could be confusing?
Extra information
Estimated cost (XS, S, M, L, XL, XXL):
?
Related suggestions: (put links to related suggestions here)
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply:
For Readers
If you would like to see this issue implemented, please click the :+1: emoji on this issue. These counts are used to generally order the suggestions by engagement.