asoffer / Icarus

An experimental general-purpose programming language
Apache License 2.0
9 stars 2 forks source link

Shorthand for typed numeric literals #51

Closed asoffer closed 3 years ago

asoffer commented 3 years ago

Currently, the way to get a u64 with the value 1 is the expression 1 as u64. Both 1 as u64 and 0 as u64 are sufficiently common that a shorthand for numeric literals would be valuable.

Proposal 1

Integer literals can have a type as a suffix, as in 1u64. This is a common approach in other languages and simple to implement, but comes with a few potential issues.

First, determining which suffixes to support is hard if we ever support platforms that have differently sized integer types. Should we allow or require 1u48 or 1u128 or 1u17?

Second, this doesn't work for any type, nor even for any spelling of an equivalent type. If someone were to int64 ::= i64, would we also allow 17int64?

Third the pattern does not work well with hexadecimal floating point numbers. In 0x1a.bcdef32, the f32 could be valid digits or the extension.

Proposal 2

Suffix the numeric literal with a colon followed by its type, as in 17:u64. This solves all of the problems above nicely, but realistically only saves 3 characters, which doesn't seem particularly valuable. It also uses : in a novel way.

Proposal 3

Allow more implicit casts so we don't need the type information. We will still need them in circumstances where generics and inference are present, but this could reduce the amount dramatically.

perimosocordiae commented 3 years ago

Can we make Proposal 3 work only for literals? I'd be much happier with implicit casting if it's restricted in that way.

wrhall commented 3 years ago

I don't think you're interested in proposal 2, but I'm curious why you wouldn't do u64:1 which looks like other uses of :

asoffer commented 3 years ago

@wrhall the typical pattern in Icarus is <variable>:<type> rather than <type>:<variable>. What's novel is that it isn't actually a declaration.

@perimosocordiae I want implicit casts to be based on the type of an object, so

ONE ::= 1
func(ONE)

should work identically to func(1).

This could be achieved by having literals have a "constant" type, but that also seems mildly surprising, especially in the context of type-inference. You probably don't want to infer the type is a constant. Maybe just set it up so that some types are marked as uninferable? In fact we already do this for null and [] (empty array) which in some sense are also constants that want to represent multiple types. The more I talk through this stream of consciousness, the more I like it.

wrhall commented 3 years ago

Good point. I was thinking of it being <type>:<value> but that's not even how := works (it's <var>:<type>=<value>)


Is the idea that constant types should be allowed to implicitly convert to multiple types? That is nice & means you don't need to push type info back into ONE once you figure out how it's used. e.g. you could use it both as a float and as an int (this could be surprising but is also, as you say, no different than passing 1 around instead)

It's worth noting that Effective Icarus will definitely instruct users to be specific with their const types if they want it to be a specific type.

asoffer commented 3 years ago

Yes, and in addition, it might also mean that if you ever infer the type constant, that's an error. So actually,

ONE ::= 1 // error. inferred
TWO :: i64 = 2 // ok

You can pass literals directly to functions and that's okay because of implicit conversions, but if the function is generic and infers a parameters type from an argument as constant, that's a problem.

This creates a refactoring hiccup, now migrating to generics from an overload set is harder, but it seems still doable atomically and I think worth the potential pain.

perimosocordiae commented 3 years ago

Very relevant reading: https://www.gingerbill.org/article/2021/03/07/untyped-types/

asoffer commented 3 years ago

https://github.com/asoffer/Icarus/commit/ff2e1683624d2cefdb7ed5e76345485c51f4c9d2

Provides the mechanism to allow arbitarry-precision integers (even though the implementation doesn't have arbitrary precision yet). There's still a bunch of usability work left in terms of allowing implicit casts, but I'm closing this issue because we've effectively implemented @perimosocordiae's proposal.