Skip to content
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

Allow nested triple-quote interpolation strings #14535

Closed
inouiw opened this issue Jan 3, 2023 · 7 comments
Closed

Allow nested triple-quote interpolation strings #14535

inouiw opened this issue Jan 3, 2023 · 7 comments

Comments

@inouiw
Copy link

inouiw commented Jan 3, 2023

Is your feature request related to a problem? Please describe.
The problem is described here: fable-compiler/Fable#3319
In short, a new feature in the F# Fable compiler allows using JSX syntax in triple-quote interpolation strings (see https://fable.io/blog/2022/2022-10-12-react-jsx.html), however nesting JSX is not possible.
If nesting triple-quote interpolation strings would be supported by the F# compiler then it would be possible to create complex UIs using the JSX syntax and F# for inline event handlers and for all other code. Without nested strings the new JSX feature has limited use for me.

A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
See above.

Describe the solution you'd like
All 3 examples below should compile, or at least examples 1 and 3.

Example 1

let nestedOnce = $"""
    {
        $"""
        {5}
        """
    }
    """

Example 2 (currently I have no use case for nesting twice but it would be nice for consistency if it would also work)

let nestedTwice = $"""
    {
        $"""
        {
            $"""
            {5}
            """
        }
        """
    }
    """

Example 3

open Fable.Core
[<JSX.Component>]       
let JsxInJsx () =
    JSX.jsx
        $"""
        <div>
          {
            [for x in 0..2 ->
              JSX.jsx
              // error FS3374: Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions.
                $""" 
                <div>{x}</div>
                """]
          }
        </div>
        """

A triple-quote interpolation string should be treated as any other expression within a triple-quote interpolation string and just be compiled.

Describe alternatives you've considered

Alternative 1
Use non triple-quoted strings inside a triple-quote interpolation string. --> Disadvantages: 1. It is only a solution for nesting one level because it is not allowed to nest a single-quote interpolation string within another. 2. There is unfortunately no syntax highlight by the VsCode extension mentioned in the article https://fable.io/blog/2022/2022-10-12-react-jsx.html in that case.

Alternative 2
Concat strings. This seems to work but requires to join strings to make the example work, which will make the UI code ugly. Additionally, there is only syntax highlight for one part of the string.
image

Alternative 3
Introduce a new filetype similar to .tsx as .fsjsx that allows defining JSX code next to F# code. This would be even better than the proposed change. The advantages to the proposed change are first, no need to start JSX code with JSX.jsx $""" and second, no need to escape { and } characters.
There is an in interesting argument in this link from facebook why it is not good to embed JSX in a template, the text says "Unfortunately the syntax noise is substantial when you exit in and out of embedded arbitrary ECMAScript expressions with identifiers in scope".

Additional context

If the issue is about:
String interpolation. See https://github.com/fsharp/fslang-design/blob/main/FSharp-5.0/FS-1001-StringInterpolation.md

@inouiw
Copy link
Author

inouiw commented Jan 5, 2023

I've updated the description to state that more than one level of nesting of triple-quote interpolation strings is optional. The thinking was that first I do not currently have a use case for nesting more than once and second I suppose that it is quite likely that implementing single level nesting is much simpler than unlimited nesting. I think supporting a single level nesting is simpler because it is already supported to nest single-quote interpolation strings in a triple-quote string and, my assumption is, it cannot be much different to support to nest triple-quote interpolation strings in a a triple-quote string.

@vzarytovskii
Copy link
Member

@roboz0r
Copy link

roboz0r commented Jan 7, 2023

I feel like this is encouraging complexity in an undesirable way. F# already allows arbitrary expressions in interpolated strings so rather than nesting you can keep the structure more flat with an extra function call.

open Fable.Core

let innerJsx x = JSX.jsx $"""<div>{x}</div>"""

[<JSX.Component>]       
let JsxInJsx () =
    JSX.jsx $"""
        <div> { [ for x in 0..2 -> innerJsx x ] } </div>
        """

@inouiw
Copy link
Author

inouiw commented Jan 7, 2023

@roboz0r for me complexity increases if I need to mentally map between how the code is written and what it does. When I write JSX, I think how it will be rendered. When child components are defined inline, I do not need any mental mapping because they will be rendered where I defined the code. However if the child component is defined above, I will need to find where it is defined and imagine how it is rendered at another place.

The blog post that writes about the JSX syntax has an example that actually uses nesting. It only works because the outer markup is not defined using JSX but using the F# syntax. So if you would like to replace the F# syntax with JSX it would not compile. Here the code from the blog post.

Code from: https://fable.io/blog/2022/2022-10-12-react-jsx.html

[<ReactComponent>]
let QrCode() =
    let value, setValue = Hooks.useState("https://fable.io/") |> asTuple
    React.fragment [
        Html.input [
            prop.className "input mb-5"
            prop.type' "text"
            prop.autoFocus true
            prop.value (value)
            prop.onTextChange setValue
        ]
        Html.div [
            toReact <| JSX.jsx $"""
            import {{ SlQrCode }} from "@shoelace-style/shoelace/dist/react"
            <SlQrCode value={value} radius="0.5"></SlQrCode>
            """
        ]
    ]

When I look at other JSX code that uses TypeScript, I often see JSX created within JSX. For example the following code from https://github.com/adrianhajdin/project_chat_application/blob/master/client/src/components/TextContainer/TextContainer.js

const TextContainer = ({ users }) => (
  <div className="textContainer">
    <div>
      <h1>Realtime Chat Application <span role="img" aria-label="emoji">💬</span></h1>
      <h2>Created with React, Express, Node and Socket.IO <span role="img" aria-label="emoji">❤️</span></h2>
      <h2>Try it out right now! <span role="img" aria-label="emoji">⬅️</span></h2>
    </div>
    {
      users
        ? (
          <div>
            <h1>People currently chatting:</h1>
            <div className="activeContainer">
              <h2>
                {users.map(({name}) => (
                  <div key={name} className="activeItem">
                    {name}
                    <img alt="Online Icon" src={onlineIcon}/>
                  </div>
                ))}
              </h2>
            </div>
          </div>
        )
        : null
    }
  </div>
);

@inouiw
Copy link
Author

inouiw commented Jan 13, 2023

I would like more people to be interested in this issue. Maybe some commenters from the pull request that introduced string interpolation #8907 are interested? @dbrattli, @Swoorup, @0x53A, @realvictorprm, @MikaelUmaN

@inouiw
Copy link
Author

inouiw commented Jan 31, 2023

Update:
Now the issue is about one month old. For me the proposed solution or especially the mentioned alternative 3 would very much improve the web development experience with F#. It would be closer to what is now possible with JSX or TSX.

My need for this feature was because I wanted to use my favourite programming language F# to write a web application that uses the javascript material-ui library. With the new fable feature to embed JSX in interpolated strings that is now possible. However for me it is not close enough to writing JSX or TSX in a separate file. That is why I created this issue.

Seeing not much discussion here made me look for alternatives that allow me to use the material-ui library with a better language than TypeScript. Now I found that Kotlin has a DSL to create markup and that there is a kotlin-wrapper for the MUI library.

Concluding, I think F# is a much better language than Kotlin and F# would be much better for web development than any other language, however, in web development, there is a constant need for change and currently (without having tried it yet) there seems to be more support for web development with JetBrains Kotlin.

@0101
Copy link
Contributor

0101 commented Mar 27, 2023

Please create a language suggestion for this.

@0101 0101 closed this as not planned Won't fix, can't repro, duplicate, stale Mar 27, 2023
@github-project-automation github-project-automation bot moved this from Not Planned to Done in F# Compiler and Tooling Mar 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

4 participants