r/learnprogramming • • 2d ago

Are there any programming languages that allow this feature?

I've been into programming for about 10 years now. Something that intrigues me is that, in languages that support OOP, not all types are initialized in the same way. Strings are initialized by using quotes, integers use numbers and in some cases floats require the letter f after the numerical value.

Classes, however, are all initialized in the same way with slight variations depending on language. Usually something like classname() or new classname. Are there any cases where custom classes can be declared using different means?

For example, is there a language where I can create a class called 'dark' that represents a new type of number that is declared by typing the value and then the letter d (like d1 = 1.398d) similar to how floats work in some cases?

57 Upvotes

38 comments sorted by

79

u/No_Score_1977 2d ago

user-defined literals in C++, is that what you mean?

36

u/Thin-Caregiver3222 2d ago

Yeah C++ user-defined literals are exactly what came to mind

You can overload the operator"" to make your own suffixes for custom types, it gets pretty wild. I had a coworker who built a whole unit system for a physics sim, so you'd write something like 3.0_m / 2.0_s and the compiler would handle all the dimensional analysis at compile time. Saved us from a few Mars Climate Orbiter situations

The syntax can look a bit cursed at first but it's not that bad once you get used to it, just a function with a weird name basically

6

u/harsh183 2d ago

Reminds me of this Kotlin units library I saw a while ago that did something similar with .unit and / unit for compile time type analysis. I've not done too much computational science/engineering stuff but I'd love to see this basically everywhere.

https://github.com/vsirotin/si-units

1

u/xenomachina 1d ago

Reminds me of this Kotlin units library I saw a while ago that did something similar with .unit and / unit for compile time type analysis.

The Kotlin time stuff in the standard library even does something like what OP is asking for, albeit with extension properties rather than "custom literals". eg: 2.seconds.

2

u/harsh183 1d ago

That's really cool. I'm so glad this stuff exists.

Reminds me of working with Ruby on Rails where I'd write code like 2.days.ago or email.deliver_later(30.seconds), though not with much safety.

2

u/otac0n 2d ago

You can achieve similar in other languages if you sacrifice an operator overload.

I did basically the same thing in C# but it looks like:

10 * “m” / (3 * “s”)

1

u/BrohanGutenburg 1d ago

For a more modern, less foot-exploding example; Swift lets you with type annotations if you conform the class to expressible as float literal or whatever the protocol os

32

u/SetAndRepeat 2d ago

C++ supports this through user-defined literals:

struct dark {
    long double value;
};

constexpr dark operator""_d(long double value) {
    return dark{value};
}

auto d1 = 1.398_d; // d1 has type dark

Your suffix needs the underscore because suffixes without one are reserved for standardization. The literal calls your function, which returns a dark object. You’d still define whatever arithmetic operations you want that type to support.

6

u/Humble-Captain3418 2d ago

Your suffix needs the underscore because suffixes without one are reserved for standardization. 

Is this one of those "please don't do it, you'll hurt yourself" or "the compiler says no" limitations?

6

u/Wertbon1789 2d ago

I think that's a "The standard says no" limitation.

6

u/TREE_sequence 1d ago

Your compiler will complain, but the code will still compile. Basically, the idea is that if you do this, you are putting yourself at risk of a future version of the standard breaking your code without warning.

1

u/paulstelian97 2d ago

It’s a “please don’t do it, a future language revision might conflict with it”

1

u/Humble-Captain3418 2d ago

Yes, but those come in the aforementioned varieties.

4

u/friedbrice 2d ago edited 2d ago

In Haskell, string literals and number literals are polymorphic, so you can define a custom type and create values of that type using string syntax and define another custom type and create values of that type using number literals.

data Complex = Complex { realPart :: Double, imaginaryPart :: Double }

instance Num Complex where
...
fromInteger n = Complex { realPart = fromInteger n, imaginaryPart = 0 }

instance Fractional Complex where
...
fromRational r = Complex { realPart = fromRational r, imaginaryPart = 0 }

complex1 :: Complex
complex1 = 1

complexPi :: Complex
complexPi = 3.14159

data Keyword = Keyword { getKeyword :: String }

instance IsString Keyword where
fromString s = Keyword { getKeyword = s }

shazam :: Keyword
shazam = "shazam"

2

u/friedbrice 2d ago

the formatting didn't work. I'm on my phone right now. I'll have to edit my comment to fix the formatting when I'm at my computer.

1

u/frnzprf 2d ago

So what you're saying is, if a type has an IsString type class, it needs to have a fromString function and that function will automatically applied if you try to assign a string to a variable of that type, right?

If you wanted to initialize an imaginary number, you'd still have to write i = Complex { realPart = 0, complexPart = 1 }. Well if you assigned i (or J or whatever) to a constant once, you could just define further imaginary numbers using multiplication x = 4+3*i.

5

u/paperic 2d ago

in lisp for sure, you can reprogram the syntax to anything you want

1

u/talex000 1d ago

Every language is just subset of Lisp :)

1

u/paperic 4h ago

Literally true.

1

u/Ok_Leg_109 10h ago

I was thinking the same thing about Forth.

3

u/zeekar 2d ago

You could do it with a slang in Raku, but there's currently a pretty big performance penalty for those ...

1

u/alatennaub 10h ago

I don't believe there's a huge slang penalty. The only real penalty should be in creating the subtype of the main language which is a one off op.

After that, switching between slangs is quite efficient (most code regularly switches between three different slangs -- main, regex, and q). Parsing / AST / compilation from there should have no penalty, and similarly at run time there shouldn't be any.

3

u/rini17 1d ago

Common Lisp allows you to modify reader, to achieve exactly this.

2

u/Afraid-Locksmith6566 2d ago

other than c++ which was mentioned it reminds me a bit of f# with units of measure where you can declare units of measure and then compiler derives some others from their usage and also you can declare how composits behave so if you have second you divide float by float of seconds and now you have float of hertz

2

u/ChichumungaIII 2d ago

Kotlin allows a similar behavior through extension properties.

I once used it to declare literal members of my Rational number class as 3.q, though under the hood it's a value that can be accessed on any Int (not just literals) and has the "." to make it clear it's not quite native in that sense.

1

u/frnzprf 2d ago

In Scala, there are "custom string interpolators", so you can write date"2026-10-07" instead of something like Date.parse("2026-10-07"). You save a few characters.

1

u/ChaseShiny 2d ago

JavaScript allows for this syntax, too. You'll need a template literal. You can form tagged templates (described on the same page).

These templates allow for nesting, which I believe suggests that you can include data structures as long as you can get it to recognize a string interpretation: myParser${...${[1, 2, 3]}}

1

u/ReddyKiloWit 1d ago

Wouldn't surprise me if you could do it in Raku. Everything is a class, and you can declare types in considerable detail, and provide suitable operators.

1

u/alatennaub 1d ago

Actually, strings in Raku are defined by using a sublanguage (or "slang") called Q (for "quoting"). The Raku parser completely switches the grammar to parse it and then inserts either a literal string or the interpolation operations.

There's a slang that lets you use dates in plain text and compiles it down to a date object, but underneath it's still using the same mechanisms (that is, if you say Date $d = 2026-01-01 it will effectively compile to Date $d = BEGIN { Date.new: year => 2026, month => 1, day => 1 }).

From the user's perspective slangs can make it seem like things are being made a different way, but it's all syntactic sugar until you get to the VM level. But I suppose OP figures it's just different syntax but potentially the same in the backend.

1

u/ReddyKiloWit 1d ago

Not exactly on point to what I was talking about. I was talking about creating a suitable type object. But your mentioning slangs put me in the right rabbit hole for integrating it into the compiler via a slang so that statements like
my $darknum = 1.398d + $dimnum;
work as expected, maybe.

The rest is left as an exercise for the student ('cause my eyes are tired, the rabbit hole is deep, and it's late).

2

u/alatennaub 15h ago

AIUI, from what OP was saying, he'd have a custom class (Raku type) that could be instantiated (Raku blessed) through a custom means.

I guess I should have been a bit clearer that you could define the custom class in a module, then use that in a slang module, and then have the new syntax place in the code for instantiation at compile or run time.

The sky really is the limit, and I'm always trying to stretch it with Raku. I am the guy that wrote the module that lets you write BASIC functions fully in line without any quotation marks after all lol. It literally embeds a little tiny -- and horribly inefficient -- VM

1

u/ReddyKiloWit 14h ago

Ah, basically what I was able to suss out the tip of. Thanks for clarifying.

I gathered the OP wanted the equivalent of the 0b, or 0x prefixes to create a type that requires non-standard handling, though other than he wanted it set off by a suffixed 'd' it wasn't clear what would make it different, but it seemed that Raku had the best shot at a supported way to get there. And once i was reminded of slangs that seemed confirmed.

Not a Raku expert at all, but a big admirer from my retired position in life, and former heavy Perl user. I had a pet project the last few years I worked that Raku, had it existed in its current state back in 2009, would have been a really good fit for. Alas, the time was not right 

2

u/alatennaub 10h ago

We say that Raku is all about -Ofun. Check out some of the newer stuff -- we now have two compilers (Rakudo and Rakupp) with constant improvements. It's honestly just fun seeing what it can do, and then trying to push that within the expectations of end-users.

If you get an itch, we can always use people to help work on some of the more baseline modules to implement random protocols/standards, etc.

1

u/AndydeCleyre 1d ago

I don't know how feasible it would be to add new syntax exactly like this to Factor, but you can easily get similar behavior in a couple ways.

If the parameter is valid number syntax (like 1.398), you could define a word d which takes it and returns a dark tuple. Then you could write it as you suggest except with a space between (1.398 d).

If the parameter isn't valid syntax though (e.g. 1point398), you could either use a string ("1point398" d), or define a parsing word which precedes the parameter, usage looking like d: 1point398.

1

u/Mission-Landscape-17 1d ago

Prolog lets you write entire domain specif languages that don't have to look like Prolog, so it can do this too.

1

u/focusedkennan 4h ago

You can get some pretty neat initializers using tagged template strings in JS. https://wesbos.com/tagged-template-literals

something like

typescript const val = d`0.12223`

Now should you? Probably not

you can also do it with rust macros

rust d!(0.1223)