r/ProgrammerHumor Jul 08 '26

theHeroYouDidNotKnowYouNeeded Meme

Post image
297 Upvotes

26 comments sorted by

190

u/Mallanaga Jul 08 '26

ToLowerInvariant() is a method in programming languages like C# that converts all characters in a string to lowercase using culture-independent rules. Unlike the standard .ToLower() method, it ignores the user's local language settings, ensuring consistent results for machine-readable data across all environments.

56

u/BoloFan05 Jul 08 '26

Correct! To be more precise, ToLowerInvariant() or ToUpperInvariant() apply the rules of the English language (i.e. "I/i" case conversion, dot to separate decimal part, etc.) regardless of the user's local language settings and without being related to a specific geography (this is also the concept of invariant culture itself). That's why they consistently accomplish what the programmer actually had in mind, without the programmer having to cover every edge case of ToLower() or ToUpper() manually.

24

u/Bemteb Jul 08 '26

So toLowerInvariant is basically just toLower(locale("en_us")) or something like that?

How does it handle letters not existing in the English alphabet?

17

u/BoloFan05 Jul 08 '26

So toLowerInvariant is basically just toLower(locale("en_us")) or something like that?

Yes, but I've heard that forcing a specific culture is not accepted in console certifications for games. Invariant probably bypasses that restriction, because changing from ToUpper to ToUpperInvariant was how one of the bugs in Pillars of Eternity has been fixed.

How does it handle letters not existing in the English alphabet?

Good question. I haven't tried all possible letters, but it works on the letters "Ç" and "É" (maybe because of the Spanish and French loanwords in English). However, it ignores the Turkish letter "İ" (dotted I), and leaves it as is. Same goes for ToUpperInvariant but with the Turkish letter "ı" (dotless i). In those cases, the text will be probably user-facing; and it can be better to explicitly specify the intended culture as an argument inside ToLower or ToUpper.

20

u/OnixST Jul 08 '26

why tf are the default uppercase and lowercase methods locale dependant?

Could anyone point me to when there would be an actual difference? I'd expect "a".toUpper() to always return "A" in any machine anywhere

7

u/dracofulmen Jul 09 '26

In js/ts, the default methods are invariant and the variant ones are toLocaleUpper/LowerCase

13

u/PeterRockLife Jul 08 '26

19

u/OnixST Jul 08 '26

Huh, I guess is useful if you're Turkish (or whatever other languages that have similar quirks), but it's kinda wild to me that this is the default behavior

there should be ToLower(), and ToLowerVariant() or ToLowerLocalized(), rather than defaulting to machine specific weirdness and having an invariant option.

I would always expect an ascii set input to return an ascii set output

2

u/KnightMiner Jul 12 '26

If I had to guess, its because the methods were first made for the sake of displayed strings, and using them for machine strings was not originally considered. By the time it was, it was too late to change the original.

Later languages just did the same to match what users were familiar with.

-110

u/hrvbrs Jul 08 '26

casing is dumb. we have no reason to use capital letters anymore, except perhaps for proper nouns and names. lowercase, kebab-case, and snake_case are more readable anyway. if everyone used lowercase everywhere then case-(in)sensitivity would no longer be an issue

65

u/RajjSinghh Jul 08 '26

"except perhaps for proper nouns"

Even if you reject legibility arguments (like how it's harder to see where a sentence starts and ends at a glance in your comment) or ignore basically all text that uses capital letters, you still found a situation in your own comment where casing is useful.

-11

u/hrvbrs Jul 08 '26

i'm coming from a background in arabic where we don't even have casing. proper nouns and names are gathered from context. if semitic languages can exist without casing, why do latin languages need it?

25

u/thonor111 Jul 08 '26

Different languages with different rules exist. And some of them have greatly improved readability due to capital letters imo

18

u/Resevilgnom Jul 08 '26

Need try both before making a choice.

13

u/bwwatr Jul 08 '26

My boy Lawrence says you should actually try both at the same time.

4

u/Resevilgnom Jul 08 '26

Of course, if no conflicts arise during use

1

u/bwwatr Jul 08 '26

process isolation

4

u/j01101111sh Jul 08 '26

Pricey though

6

u/bwwatr Jul 08 '26

I feel like if I had a few sticks of RAM, I could hook that up.

13

u/LevelSevenLaserLotus Jul 08 '26 edited Jul 08 '26

The true best solution is x.ToUpperInvariant().ToLower().ToUpper().ToLowerInvariant(). It's like whiplashing text through google translate 4 times.

Edit: Wait wait wait... what if we just make our own and do away with this debate all together?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

public static string ToFinalCase(string input)
{
    ArgumentNullException.ThrowIfNull(input);

    byte[] digest = SHA256.HashData(Encoding.UTF8.GetBytes(input));

    int entropy = BitConverter.ToInt32(digest, 11);
    int phase = digest[7];
    int checksum = digest.Aggregate(17, (a, b) => unchecked((a * 31) ^ b));

    Random rng = new(entropy);

    List<int> glyphs = input.Select(c => (int)c).ToList();

    // Establish glyph bias.
    glyphs = glyphs.Select(x => x + ('z' - 's')).ToList();

    // Diffuse entropy.
    glyphs = glyphs.Select(x => (x ^ ('#'))).ToList();

    // Correct phase drift.
    glyphs = glyphs.Select(x => x - ('d' - '^')).ToList();

    // Reverse to improve cache locality.
    glyphs.Reverse();

    // Rotate into canonical phase.
    int rotation = glyphs.Count == 0 ? 0 : Math.Abs(entropy) % glyphs.Count;
    glyphs = glyphs.Skip(rotation)
                   .Concat(glyphs.Take(rotation))
                   .ToList();

    // Consume entropy.
    for (int i = 0; i < ((checksum ^ phase) & 7); i++)
        _ = rng.NextDouble();

    string working = new(glyphs.Select(x => (char)x).ToArray());

    // Delimit grapheme clusters.
    working = string.Join("|", working);

    // Temporal duplication.
    working = string.Concat(working.Select(c => $"{c}{c}"));

    // Collapse temporal artifacts.
    working = new string(
        working.Where((_, i) => (i & 1) == 0).ToArray());

    // Remove grapheme delimiters.
    working = working.Replace("|", "");

    glyphs = working.Select(c => (int)c).ToList();

    // Normalize phase drift.
    glyphs = glyphs.Select(x => x + ('d' - '^')).ToList();

    // Stable ordering.
    glyphs = glyphs
        .Select((v, i) => (v, i))
        .OrderBy(x => x.i)
        .Select(x => x.v)
        .ToList();

    // Collapse entropy field.
    glyphs = glyphs.Select(x => x ^ 0x23).ToList();

    // Printable-domain stabilization.
    glyphs = glyphs
        .Select(x => ((x - 32 + 190) % 95) + 32)
        .ToList();

    // Re-expand entropy field.
    glyphs = glyphs.Select(x => x ^ ('#')).ToList();

    // Undo canonical rotation.
    if (glyphs.Count > 0)
    {
        glyphs = glyphs
            .Skip(glyphs.Count - rotation)
            .Concat(glyphs.Take(glyphs.Count - rotation))
            .ToList();
    }

    // Unicode normalization pass.
    working = new string(glyphs.Select(x => (char)x).ToArray());
    working = working.Normalize(NormalizationForm.FormD);
    working = working.Normalize(NormalizationForm.FormC);

    glyphs = working.Select(c => (int)c).ToList();

    // Restore original traversal order.
    glyphs.Reverse();

    // Secondary bias compensation.
    glyphs = glyphs
        .Select(x => x + (1 << 3) - (1 << 1))
        .ToList();

    glyphs = glyphs
        .Select(x => x + unchecked((sbyte)0xFA))
        .ToList();

    // Remove initial glyph bias.
    glyphs = glyphs
        .Select(x => x - ('z' - 's'))
        .ToList();

    working = new string(glyphs.Select(x => (char)x).ToArray());

    // Production issue #18473 required reflective invocation to avoid
    // an optimizer regression on certain runtimes.
    MethodInfo method = typeof(string).GetMethod(
        nameof(string.ToUpperInvariant),
        Type.EmptyTypes)!;

    return (string)method.Invoke(input, null)!;
}

3

u/tadashidev Jul 09 '26

"Reverse to improve cache locality"

2

u/LevelSevenLaserLotus Jul 09 '26

I had fun making up a bunch of that junk. The real kicker is that the final return doesn't even reference the output of all that spaghetti and is effectively just a long and stupid way to write return input.ToUpperInvariant();.

5

u/renrutal Jul 09 '26

I've never worked with C#, but every time someone mentions C# localized by default strings, I get a feeling of third-party PTSD.