Crystal: Use single quotes for strings rather than characters?

Created on 23 Mar 2015  Â·  29Comments  Â·  Source: crystal-lang/crystal

Whenever I try to "port" a simple Ruby CLI script to Crystal, I immediately run across the problem that Crystal doesn't support single-quoted strings (i.e. ' ' is only allowed for single characters).

This is painful since I always use single quotes for non-interpolated strings. It's a common practice, which is enforced by RuboCop (by default), suggested by RubyMine, and accepted/recommended (formerly preferred) by the Ruby Style Guide.

Is the Char type so commonly used in Crystal that it's worth breaking lexical compatibility with Ruby? How about using Ruby's ?c (or maybe adding %c) instead? That way, a lot of simple scripts could be ported and people (like me) who are keen to start playing with Crystal can focus their energies on taking advantage of its features rather than working around one of its quirks.

Most helpful comment

@jeff-hykin @straight-shoota Can you create another issue for this? It has nothing to do with this (closed) issue.

All 29 comments

The consensus in the Ruby community of preferring ' is actually crumbling, since the proclaimed performance difference is parse time only and hardly measurable, while " provides a usage benefit of being able to add an interpolation any time without changing the quotes.

I dislike ?c, in Ruby too. Ruby introduced it back when String#[] would return codepoints instead of strings, and back then ?c would return the codepoint too, so you could do string[0] == ?a safely. I don't think we should carry that legacy construct over.

%c seems not much better, it conflicts with modulo (a % c) and percent literals %flag seperator content separator, the later mostly on a style basis which still adds confusion, and that's what you're actually trying to resolve here.

Note that the type difference between " and ' is only odd to Rubyists, it's already common practice among other languages, most notably Java and C.

Crystal is a different language, there are more things you need to adjust in about any Ruby script and those being far more than a simple search & replace operation. Most notably defining empty Array and Hash literals, but there's also no real equivalent to proc and lambda, only a very similar constructs

" is just a thing of getting used to, I raised the same question as you when I started writing Crystal. These days I'm actually adopting " by default in the Ruby I write.

I'm not arguing for or against single quotes. I'm just pointing out that they're common in Ruby and it's the first issue many people will encounter when diving in to Crystal:

> puts 'Hello, world!'
Syntax error in ./hello.rb:1: unterminated char literal

puts 'Hello, world!'
     ^

it conflicts with modulo (a % c)

Ruby seems to handle this OK:

puts 10 % q = 2
puts 10 % Q = 3
puts 10 % w = 4
puts 10 % r = 5
puts 10 % x = 6
1
2
0
4

" is just a thing of getting used to

I don't mind a divergence when it's an improvement. I can live with e.g. optparse becoming option_parser since it probably should have been called that in the first place, but I can't remember the last time I used a character type in Ruby, Java &c., so, from my perspective, this feature's cost outweighs its benefit.

but I can't remember the last time I used a character type in Ruby, Java &c., so, from my perspective, this feature's cost outweighs its benefit.

Opposed to Ruby, Crystal does have a focus on performance and efficiency and using a character instead of a string when you need to represent only a single character is a lot more efficient, since you don't need to store an associated length just to provide memory safe operations on it. See https://github.com/manastech/crystal/blob/master/src/string.cr#L89, whereas materializing a char is at most decoding a byte sequence into a number and casting that number to a Char.

Isn't this just habit and convention? Having strings denoted using a single solution instead of two is appealing to me: no more talks wether a project should use single or double quotes by default :-)

Opposed to Ruby, Crystal does have a focus on performance and efficiency and using a character instead of a string when you need to represent only a single character is a lot more efficient, since you don't need to store an associated length just to provide memory safe operations on it.

I'm not suggesting chars be removed, just that their syntax should reflect their usage and not get in the way of a core feature of related languages such as Perl, Ruby and Groovy. A quick grep through the Crystal codebase suggests that (lines containing) strings outnumber chars by a wide margin:

git ls-files | grep -P '\.cr$' | xargs grep -P '"(?:\\"|[^"])*"' | wc -l
11027

git ls-files | grep -P '\.cr$' | xargs grep -P "'(?:\\'|[^'])*'" | wc -l
1607

Languages like C and Java use single quotes for chars because, unlike Crystal, they lack support for string interpolation and thus can afford to "waste" single quotes. IMO, it doesn't make sense for Crystal to blow such a large part of its syntactical budget on an (implementation) optimisation when it can be achieved just as well with a similar number of characters (or fewer!), and without introducing a syntactical incompatibility with Ruby which, IMO, is likely to surprise, confuse and deter many newcomers on their first contact with the language.

As other have said, I think using single quotes for Char and double quotes for String is OK. You mention Perl, Ruby and Groovy: those are dynamic languages. Crystal is not dynamic and is closer to languages like C, D, Go and Java. And in all of these a single quote denotes a character. When I learned Ruby I was actually surprised that they used both to denote strings, it seems like a waste of a symbol which could be used for something else. I guess that's why they ended with ?a, which still denotes a String in Ruby...

I think the language by now is already quite different and full of surprises if you come from Ruby: array and hash literals, generics, abstract methods, type restrictions, type check and type flow but only for variables, no dynamic features, etc. I also don't think it's feasible to port Ruby applications just by copy and pasting: Ruby programmers tend to abuse hashes, symbols and dynamic evaluation.

Although single quoted strings is something to change if you try to port the code, as @asterite mention, there are other issues that prevents direct port of ruby code.

An alternative to change the lexer/compiler to emit chars for single chars single quoted string will be rather confusing I bet. And, i think, there is no penalty to use double quoted strings without interpolations in crystal.

You mention Perl, Ruby and Groovy: those are dynamic languages.

I take your point, but, FWIW, Groovy is both (AKA optionally/gradually-typed), as are Groovy++, Perl 6, Dart, and TypeScript (and arguably C#, Go and Haxe, which all support some form of dynamic/duck typing), though Nim and Kotlin make the same distinction as Crystal. Swift doesn't appear to support single quotes for anything...

IMO, Crystal is both as well (in practice) — i.e. the safety of static typing with the convenience of dynamic typing — thanks to its unique type system :-)

Please reconsider support of single quoted strings. Sure this is matter of taste but I greatly dislike that strings in crystal are double quoted only.

Many who comes to crystal are ruby guys. And we love beauty of code above all other things.

Also please reopen this issue so more people are able to participate in the discussion easily.

@scaryzet if you want to revive this discussion you're going to need to be constructive. Respond to the points raised previously and show sensible alternatives to the shown issues.

@jhass Well, I'm a newcomer to crystal. I understand it is focused on performance and usage of the single character type is somewhat extensive.

It just feels weird from rubyist's point of view that "they're not letting you write fancy string literals". Really, single quotes are community choice in ruby. They're easier to type :) and look more elegant. Some people still use double, but they're not the majority.

Yes, I would prefer ?a or %c(a) syntax. Special syntax for those 5% of situations where single characters are really needed. The only argument against those styles so far is subjective dislike. How many other people would dislike ?a and %c(a)? Maybe run a poll or something?

Releasing the single quote mark for string usage really outweights, i think. Strings are more important. Respecting ruby code style standards is more important.

Crystal community is built mostly of rubyists i guess. Crystal community will grow mostly by absorbing more rubyists. Community must be considered first.

Yes, crystal is "a different language" than ruby. But it is mainly appealing as a "compilable version of ruby", not as "yet another language". Ruby-like syntax is the strongest reason one would start using crystal. Please, make it as much similar to ruby as it is possible.

You still have the time to change things while crystal is in its alpha stage.

Anyway, crystal is a great project. Good luck, and keep it going!

@scaryzet What's more _beautiful_ or _elegant_ about single quoted strings? I prefer double quoted string literals and single quoted character literals; all my Ruby strings actually use double quotes.

This way both remain "elegant," (I'd prefer to use the word efficient [for the programmer]) without the need for odd syntax. And more importantly, I believe many people who work with similar languages (C, C++, or Go) will expect this type of behaviour and be familiar with it.


Ruby-like syntax is the strongest reason one would start using crystal.

While this might true, I am still against changing the syntax for character literals, because it would break expectations.

I believe many people who work with similar languages (C, C++, or Go) will expect this type of behaviour and be familiar with it.

In what way is Crystal "similar" to these languages?

In what way is Crystal "similar" to these languages?

@chocolateboy Other than the Ruby-inspired syntax and standard library, I feel like the language is much more C-like than Ruby-like—in a good way. It takes the best from both worlds, and I think the char literal and string literals are better borrowed from C.

Yes, It's all a matter of style, but quite frankly I don't think using double quotes should be such an issue; especially considering _all_ the other stuff that doesn't make Crystal completely Ruby-compatible.

I feel like the language is much more C-like than Ruby-like

In what way?

@omninonsense: let me clarify: I'm not trying to be nitpicky (I am (was) over this issue, because there's no consensus for it, but IMO that doesn't justify broad statements about why Crystal _doesn't_ or _shouldn't_ support single-quoted strings)...

C is weakly and statically typed. Ruby is strongly and dynamically typed. Apart from one Go-like error message ("space required before colon in type restriction"), I can't see any way in which Crystal resembles Go (or C/C++) beyond the fact that they all aspire to be _fast_: they're literally opposed on _all_ axes.

The concurrency model is very Go-ish...

@kirbyfan64: awesomely true! :+1:

@chocolateboy Although [I think] I understand what you meant, weakly/strongly typed are surplus epithets here. Especially since nobody really agrees on what they mean (other than the vague concept of it). You could do same "type atrocities" in Crystal that you could do in C (albeit with a little more typing, probably). When I said similar, I meant… I suppose I meant that they're more similar in how Crystal types map directly to memory the same way C does, rather than being abstract representations (I'm generalising a bit) of it like in Ruby.

nobody really agrees on what they mean

The fact that they're relative terms (i.e. graded on a dial) doesn't mean nobody understands what they mean :-)

they're more similar in how Crystal types map directly to memory the same way C does, rather than being abstract representations

I don't think that's true in general. Crystal's fibers are easy to work with/reason about, precisely _because_ they shield you from the implementation details:

p spawn {}
#<Fiber:0x1c13d90 @cr=Pointer(Void)@0x7faf0d454000,
@proc=#<( -> Void):0x418d50>,
@stack=Pointer(Void)@0x7faf0d454000,
@stack_top=Pointer(Void)@0x7faf0dc54000,
@stack_bottom=Pointer(Void)@0x7faf0dc54000,
@next_fiber=nil,
@prev_fiber=#<Fiber:0x1c13e00 @cr=Pointer(Void)@0x7faf0dc54000,
@proc=#<( -> Void):0x418b60>,
@stack=Pointer(Void)@0x7faf0dc54000,
@stack_top=Pointer(Void)@0x7faf0e454000,
@stack_bottom=Pointer(Void)@0x7faf0e454000,
@next_fiber=#<Fiber:0x1c13d90 ...>,
@prev_fiber=#<Fiber:0x1c13e70 @cr=Pointer(Void)@0x7faf0e454000,
@proc=#<( -> Void):0x417be0:closure>,
@stack=Pointer(Void)@0x7faf0e454000,
@stack_top=Pointer(Void)@0x7faf0ec54000,
@stack_bottom=Pointer(Void)@0x7faf0ec54000,
@next_fiber=#<Fiber:0x1c13e00 ...>,
@prev_fiber=#<Fiber:0x1c13ee0 @cr=Pointer(Void)@0x7faf0ec54000,
@proc=#<( -> Void):0x4177c0>,
@stack=Pointer(Void)@0x7faf0ec54000,
@stack_top=Pointer(Void)@0x7faf0f454000,
@stack_bottom=Pointer(Void)@0x7faf0f454000,
@next_fiber=#<Fiber:0x1c13e70 ...>,
@prev_fiber=#<Fiber:0x1c13f50 @cr=Pointer(Void)@0x1bbf520,
@proc=#<( -> Nil):0x417730>,
@stack=Pointer(Void).null,
@stack_top=Pointer(Void)@0x7ffe3a0e2524,
@stack_bottom=Pointer(Void)@0x7ffe3a0e3000,
@next_fiber=#<Fiber:0x1c13ee0 ...>,
@prev_fiber=nil,
@resume_event=nil>,
@resume_event=nil>,
@resume_event=nil>,
@resume_event=nil>,
@resume_event=nil>

Single quotes denote a single Char and double quotes denote a UTF-8 String. This is a design decision. It was taken unilateraly by the language creators, and chars provide a nice and free performance boost over strings in many string related operations (just change " for '). It's a strange difference when coming from a script language like Ruby, Python or JavaScript (I was too), so this thread was created, alternatives were proposed, but they didn't bring any better readability (quite the opposite, consider string.split(':') vs string.split(%c(:))), and the thread was closed. IMHO it won't be reconsidered.

I'm sorry if you can't stand double quote strings. Please consider that using chars may happen a bit more often than you could think at first, and you don't want your code to become harder to read, just because a single char is now a special construct like %c(:) instead of just ':'.

Can we consider the matter closed, now? Let's avoid some heated (and slightly off topic) discussion, please :-)

:clap: :clap: :clap:

I found a decent solution today, not for portability from Ruby, but for a literal strings (without adding/changing any features to crystal). I needed a raw string and the heredoc and other string syntaxes weren't going to work so I was pretty disappointed back when I originally read this issue.

My solution is just overloading the - operator for regular expressions, but it seems to work pretty well.
Interpolation still works though, thats the largest flaw

class Regex
    def -
        return self.inspect[1,self.inspect.size-2]
    end
end

puts -/literally\n/
#outputs:literally\n

puts -/literally\n\]\)\>\}/
#outputs:literally\n\]\)\>\}

Do note: I intended this solution to be for getting code working, not as a "best practice" solution.

@jeff-hykin i'm extremely confused - can't you use puts %q(literally\n)? https://carc.in/#/r/390r

@RX14
I need/needed all four of ()[]{}<> to be in the string (my literal string happened to be a regular expression). This: %q(literally\)\n) Doesn't run since the ) can't be escaped.

With the -/literally/ notation this: -/literally\/\n/ still allows for the / to be escaped, but everything else is raw. Literal heredocs are a functional solution, but they're often impractical.

This kind of behavior makes it difficult to port Ruby single quote strings to Crystal.

Possible solutions:

  1. Triple single quotes?
    ex: '''literally\n'''
    Probably not easy to add
  1. Unary prefix operator
    ex: !'literally\n' or -'literally\n' or r'literally\n'
    Not exactly intuitive or consistent

I've reposted this comment as #5403

If I understand your issue correctly, it is essentially that Crystal has no unescaped string literal that can hold really arbitrary strings.
Double-quoted strings are escaped and interpolated so you'd have to do a lot of escaping to use certain characters.
The percent string literals %q(), %q[], %q{}, %q<> and %q|| allow to write unescaped strings and offer some variaty for delimiters but they are still limited such that you can't use them for a string that contains all of these delimiters (or at least one per pair) because the delimiter can't be escaped either.

Their may be a way I'm unaware of to escape the ) inside of %q().

I don't think there is, but it should probably be escapable as \).

That's also supported in both Ruby and Perl (where this literal syntax was originally borrowed from):

%q(\))  # => ")"  (Ruby)
q(\))   # => ")"  (Perl)

Having this feature for percent strings in Crystal would be great. It would be relatively easy to implement and shouldn't break anything since escaped percent delimiters are currently invalid code.
And this is certainly better than rededicating single quote delimiters (which is completly infeasible) or introducing an additional delimiter syntax.

@jeff-hykin @straight-shoota Can you create another issue for this? It has nothing to do with this (closed) issue.

no problem

Edit: well @straight-shoota beat me too it so I'll close mine and we can use his

are you going to invent a magic char that act as delimiter but not string content?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RX14 picture RX14  Â·  3Comments

ArthurZ picture ArthurZ  Â·  3Comments

relonger picture relonger  Â·  3Comments

will picture will  Â·  3Comments

lbguilherme picture lbguilherme  Â·  3Comments