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

Figure out what string interpolation syntax should be #11

Open
toroidal-code opened this issue Mar 6, 2014 · 38 comments
Open

Figure out what string interpolation syntax should be #11

toroidal-code opened this issue Mar 6, 2014 · 38 comments
Labels

Comments

@toroidal-code
Copy link
Member

We really need this
I don't want c/c++

"thing: " + var + " is still here"
@zellio
Copy link

zellio commented Mar 6, 2014

str = "#{Variable} not variable"
my $str = "This is $interpolated";
str = "Python's is dumb %(name)s." % variable

@zellio
Copy link

zellio commented Mar 6, 2014

More importantly, there should be literal string and interpolated string. Either we need to use an inline glif or a format function. It's really up to which way we fall in that case.

@weswigham
Copy link
Contributor

"Using a widely adopted {{thing}}" `context` {thing: "standard"}

Mustache/Handlebars-style interpolation as a language feature.
It goes so well with any system with objects or structures.
It would be so nice. Especially if, like, it was a method on the String object...

"This literal can be {{made}}".context {made: "interpolated."}

@weswigham
Copy link
Contributor

One moment, let me summon my knowledge thus far... implementation of mustache w/o iterating over array/list structures...

String -> HashTable<String, T> -> String
fn context(str, tbl)
    tbl.each -> |k, v|
        str = context_internal(k, str, v)
    str

String -> String -> HashMap<String, T> -> String 
fn context_internal(prefix, str, table)
    table.each -> |k, v|
        str = context_internal(prefix+"."+k, str, v)
    str

String -> String -> String -> String
fn context_internal(prefix, str, rep)
    str.gsub("{{"+prefix+}}", rep)

And let me tell you about how much I love language features implemented in the language itself.

@toroidal-code
Copy link
Member Author

@zellio's right. We're going to need some sort of distinction between interpolated strings and string literals. At least in the first round of the compiler, we (probably) won't be able to handle unicode in interpolated strings, since the compiler will have to do string manipulation on them, and OCaml has pretty bad unicode support.

I really like the ability to just format strings without any sort of format function, because it's pretty, but that will require we have something special like $"some string {{var}} " to denote interpolated strings. This will also require that we have a special AST pass that traverses the tree and grabs the variable's references when compiling.

On the other hand, a format function could be part of the String class, and reduce this to a simple function call such as "some string {{var1}} {{var2}}".fmt(var1, var2) that occurs at run-time.

Opinions?

@nickserv
Copy link

nickserv commented Mar 6, 2014

We could do what Ruby does, and have single- and double-quoted strings, where only double quoted strings support interpolation and character escapes.

@weswigham
Copy link
Contributor

I like my option (with a function on String). Explicit interpolation all the way~

@toroidal-code
Copy link
Member Author

@nicolasmccurdy right now 'c' and "c" are considered of type Char and String respectively. It's the only really way to differentiate for the type system

@zellio
Copy link

zellio commented Mar 6, 2014

I like the ruby style of explicit interpolation via the quoting character. (Which, we could implement by calling an internal function of the String object). It should search it's current scope for variables though. I strongly dislike

"string {{var}}" format var

@zellio
Copy link

zellio commented Mar 6, 2014

We can do characters as #\C

@toroidal-code
Copy link
Member Author

@zellio Lol, no Lisp chars plz

@toroidal-code
Copy link
Member Author

If we have macros (as I'm really really hoping we can do) the $"some string {{var}} " could become a macro-transformation on the AST. I'd prefer to have this at either a higher (function) or lower (compiler) level though.

@weswigham
Copy link
Contributor

@zellio I don't like doing automatic binding to scope variables, as it makes it so I can't pass around partially formatted strings, since the formatting is coupled to the scope the format literal was created in.

@toroidal-code If you want to support unicode at some point in the future, you can save work now by nixxing the concept of a 'Char' entirely and acknowledging it as a single length string. A 'Character' is vague and character-set dependent. Character set information can be included with a String - a string is a heavyweight thing. you use chars when you want something lightweight, but once you start including charset meta-information it becomes just as heavy.

@toroidal-code
Copy link
Member Author

@weswigham Brick's Chars can handle unicode just fine, it's OCaml's char type that's ascii-only. I'm going to reference Rust on this one: http://static.rust-lang.org/doc/0.9/std/char/index.html

@weswigham
Copy link
Contributor

Meh, refer to following. Old information is old.

@weswigham
Copy link
Contributor

Distinguishing between a Char and a single character String means I need two overloads of every function that handles variable length string input on my external APIs, one to handle Char and one for the more general String case.

I also hate having to distinguish between '' and "" literals inside my editor - If I typed 'something' I wanted a string, regardless of putting it in single quotes. The compiler even knows its invalid as a char. It's one of my gripes with C.

@weswigham
Copy link
Contributor

Most of my reasoning is because it's more useful to thing of a Char as a specific case of String rather than things of a String as composed of Char.

@zellio
Copy link

zellio commented Mar 6, 2014

I'm kinda with @weswigham on this whole Char type thing. What is the point?

@weswigham - doesn't mean you can't have them look to scope when it's not provided in the format args

@weswigham
Copy link
Contributor

@zeillio That just seems like an absolutely terrible idea from a security standpoint... If I take in user input and it contains '{{varname}}' and apply my scope to it, they can discover things I haven't explicitly disclosed.

@zellio
Copy link

zellio commented Mar 6, 2014

@weswigham - why would you blindly interpolate user input? that's a horrendous idea

@toroidal-code
Copy link
Member Author

@zellio from a syntax perspective there's no need for Char. At a lower level, there will be one. Strings are just containers of Char

@weswigham You shouldn't be using eval. There's safe_eval for that. (lol python)

@weswigham
Copy link
Contributor

  1. It's not eval. there is no eval here.
  2. You shouldn't, but sometimes you do. If I have a field that asks for a name, and then I want to display it back in an interpolated string, it's better if its safe.

@weswigham
Copy link
Contributor

I don't think Char should be a public type at all, really. Then I could get awkward circumstances where I need to explicitly convert a character to a string... which feels so bad.

@zellio
Copy link

zellio commented Mar 6, 2014

@weswigham - I'm still confused as to why you are interpolating user input.

def foo(val)
  x = 10
  "#{val}"
end

foo('#{x}') # => "#{x}"

@toroidal-code
Copy link
Member Author

Alright, so Char will probably not end up being exposed to the user. I'm okay with that. String will be the lowest level.

@toroidal-code
Copy link
Member Author

So I'm for having "{{somestring}}" be interpolated and 'somestring' not, as per @zellio's suggestion.

@toroidal-code
Copy link
Member Author

Okay. So what's the interpolation going to look like in the string?
Rust: "{:s} things".fmt!(some)
Ruby: "#{some} things"
Python: "%s things" % some"
Mustache: "{{some}} things"

We have to remember that only things that implement the ToStr trait will be able to be embedded in strings.

@nickserv
Copy link

Why not use printf-style interpolation? It's very widely known.

@weswigham
Copy link
Contributor

Or {{var}} templating because people use that all the time. And it makes more sense than nonsensical printf format strings like %20f

@toroidal-code
Copy link
Member Author

@nicolasmccurdy the problem with printf style interpolation is that printf is required to take a variable number of arguments depending on the content of the string. And that's a bit gross.

@weswigham
Copy link
Contributor

Whereas handlebars-type/rust-type format take one argument, and is, at its simplest, simply recursively called with partially parsed tokens.

@kristenmills
Copy link

@nicolasmccurdy why?

My vote is for Mustache or Ruby style

@zellio
Copy link

zellio commented Mar 10, 2014

@kristenmills and @nicolasmccurdy - I think having something akin to printf is nice. Formatter classes can be a pain and having an easy interface to them is always useful. That being said I don't know if it is a good idea to make that the default syntax. If we are going to have the formating stuff built into the interpolation then yes we should go with printf or list format synatx (something that is known / accepted).

Should we format when we interpolate?

@kristenmills
Copy link

@zellio - I don't think we should format when we interpolate. I agree formatting is a nice thing to have but I think we should take a similar approach to ruby and separate formatting from basic interpolation.

@nickserv
Copy link

@kristenmills Good point, I think that would make things simpler. And there could still be printf-style methods like how Ruby does it.

@toroidal-code
Copy link
Member Author

I would rather have interpolation and formatting separate.

I would prefer a post-processing method or macro to handle formatting, the way Rust or Python (minus the % sign). Formatting would provide more powerful options, while interpolation is for simple things

@weswigham
Copy link
Contributor

Templating, as with handlebars, handles both of those concerns.

Number -> Number -> (whatever puts returns)
fn printthing(a, b)
    puts "{{first}} / {{second}} = {{third}}".template({
        'first': a.to_fixed(3),
        'second': b.to_fixed(3), 
        'third': (a / b).to_fixed(3)
    })

Which when invoked with 2 and 3 would print 2 / 3 = 0.667. This requires functions on numbers/whatever for common formatting options, but hey, it makes sense, and is easy to both work with, understand, and extend.

@zellio
Copy link

zellio commented Mar 11, 2014

@kristenmills @nicolasmccurdy @toroidal-code - agreed, to recap

Interpolation syntax ({{which}} is searched for in the current ENV)

"This is a string {{which}} interpolates"
'This is a string {{which}} wont'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants