(String | Symbol) to Symbol

I have an array which holds both strings and symbols. The issue is when I try to grab a symbol of the array to use it as an argument, as it is of (String | Symbol) type now.

Can I turn it back to Symbol now or am I doomed?

Can you share more about your use case? I’m sure there’s a better way than a union of strings and symbols.

Symbol is replaced by Int32 at compile time.
When a Crystal program is compiled, all symbols and their string representations are collected and a symbol table is created.
Symbols cannot be dynamically generated at runtime for strings that do not exist at compile time.
(However, we need to see the sample code to see if this is really the case.)

I’d get this macro which been mentioned in my previous question:

macro article(word)
  {% word = word.downcase %}
  {% vowel_sound = ["hour", "honest", "honor", "heir"] %}
  {% consonant_sound = ["university", "unicorn", "use", "european", "one", "ouja"] %}
  {% result = nil %}
  {% result = :an if vowel_sound.includes? word %}
  {% result = :a if consonant_sound.includes? word %}
  {% if !result
       first_letter = word.chars[0]
       result = ['a', 'e', 'i', 'o', 'u'].includes?(first_letter) ? :an : :a
     end %}
  {{ [result, word] }}
end

art, word = article("test")

This will take a string and return it along with appropriate article in an array at compile time. I used symbols for articles to avoid duplicating them for each object.

I think if you just make it so you use a tuple instead of an array, it’ll just work how you want. E.g. {{ {result, word} }}.

2 Likes

That seems to do the job. Thank you!

Maybe this is what you mean?

a = article("apple")
p a[0].as(Symbol).class # Symbol
p a[1].as(String).class # String

That works too, thanks. But @Blacksmoke16’s solution works without casting.

The best advice is probably not to use symbols in the first place.

Symbols are internal identifiers. You’re using them as a kind of string. That’s not what they’re made for.

Also, the macro code generates string literals which are deduplicated. There’s only a single string instance for every "an" literal etc.

For example:

"foo".object_id # => 93891411413144
"foo".object_id # => 93891411413144

I would agree if it was only a literal, but those are stored as variables in objects, and there may be even few thousands of them. It seems kinda wasteful to have them duplicated like this, even if it’s just one or two characters.

But there is no duplication. All string literals with the same value point to the same memory in the data section of the process memory. If you assign that value to a variable or pass it around as method argument, it still points to that same, single memory address.

That’s interesting. So the advantage of symbols is mostly faster comparation?

Did you know, the String is immutable in Crystal?

If you’re from Ruby, you might have misunderstood this.

1 Like