int
stdlib/int.hk: inherent assoc fns on int.
Associated functions on a built-in type live in an impl <Type> block alongside the type's inherent methods. Reading an int from text is FromString (below) and no inherent fn, which lets generic code reach it through the bound; _parse is the runtime seam it is built on.
impl int
_parse
def _parse(s: string) -> Option<int>
The runtime seam under [FromString]: the raw yes-or-no parse, with no room to say why. parse below is the surface, and turns the None into a reason.
to_i64
def to_i64(self) -> i64
Narrow to i64, truncating (modular: the low 64 bits at the target width), total: matches the fixed-width narrowing family (HANKI.md §3). The in-band sentinels saturate (-inf -> i64 MIN, +inf/undefined -> i64 MAX).
7.to_i64() => 7i64
(-7).to_i64() => -7i64
to_rational
def to_rational(self) -> rational
Widen to the rational tier (exact; self/1; sentinels pass through).
6.to_rational() / 3.to_rational() => 2.to_rational()
to_decimal
def to_decimal(self) -> decimal
Widen to the decimal tier (exact, scale 0; sentinels pass through).
3.to_decimal() + 4.to_decimal() => 7.to_decimal()
to_f64
def to_f64(self) -> f64
Widen to f64, rounded to nearest, lossy beyond 2^53 (f64 has a 53-bit mantissa). Sentinels map to IEEE: inf/-inf become the float infinities, undefined becomes NaN.
42.to_f64() => 42.0f64
(-42).to_f64() => -42.0f64
todecimalscaled
def to_decimal_scaled(self, scale: int) -> decimal
This value as the mantissa of a decimal scaled by 10^-scale: the decimal equal to self * 10^(-scale) (a negative scale scales up). The fractional-decimal constructor decimal otherwise lacks, since its / widens to rational.
scale is the same int that decimal.scale answers and decimal.from_parts accepts, and this takes from_parts's exact contract: undefined when scale is not one any decimal can hold (non-finite, or past i64).
12345.to_decimal_scaled(2) => 1234500.to_decimal_scaled(4)
to_i8
def to_i8(self) -> i8
Narrow to i8, truncating (modular, low 8 bits): i64.to_i8 reached without the detour through to_i64 a caller had to write.
This is the tier values arrive in: every unsuffixed integer literal is an int, and so is every count the index surface reports, so int is what a caller has where a fixed width is wanted. It is self.to_i64().to_i8() and no more: the low bits of a number are the low bits of its low 64 bits, and the two agree for every finite value, and a sentinel saturates in to_i64 as it always did.
127.to_i8() => 127i8
200.to_i8() => -56i8
(-1).to_i8() => -1i8
to_i16
def to_i16(self) -> i16
Narrow to i16, truncating (modular, low 16 bits). See to_i8.
32767.to_i16() => 32767i16
32768.to_i16() => -32768i16
to_i32
def to_i32(self) -> i32
Narrow to i32, truncating (modular, low 32 bits). See to_i8.
2147483647.to_i32() => 2147483647i32
2147483648.to_i32() => -2147483648i32
to_u64
def to_u64(self) -> u64
Reinterpret the low 64 bits as u64: the same bits, total. See to_i8.
9.to_u64() => 9u64
(-1).to_u64() => 18446744073709551615u64
to_u32
def to_u32(self) -> u32
Narrow to u32, truncating (modular, low 32 bits). See to_i8.
4294967295.to_u32() => 4294967295u32
4294967296.to_u32() => 0u32
(-1).to_u32() => 4294967295u32
to_u16
def to_u16(self) -> u16
Narrow to u16, truncating (modular, low 16 bits). See to_i8.
65535.to_u16() => 65535u16
65536.to_u16() => 0u16
to_u8
def to_u8(self) -> u8
Narrow to u8, truncating (modular, low 8 bits). See to_i8.
255.to_u8() => 255u8
256.to_u8() => 0u8
_fits?
def _fits?(self, low: int, high: int) -> bool
Whether self lies in the inclusive range [low, high], the shared test under the try_to_ family. Every fixed width's bounds are representable here, and the comparison therefore happens in this tier and nothing is narrowed before the answer is known. The in-band sentinels fail it through the tier's total order (-inf sits below every finite bound, inf and undefined above every one), which is the reading the twins want: no width covers an infinity.
trytoi8
def try_to_i8(self) -> Option<i8>
Narrow to i8, or None where the value does not fit: the checked twin of to_i8 (HANKI.md §3).
Unlike the bare family this is not self.to_i64().try_to_i8(). That composition is unsound: an int past i64's range has already wrapped by the time the check runs, and (2.pow(70).to_int() + 5).to_i64().try_to_i8() would answer Some(5i8) for a value that never fit, the wrapping the twin exists to detect. The bounds test therefore runs against the full-precision value.
127.try_to_i8() => Some(127i8)
128.try_to_i8() => None
(-129).try_to_i8() => None
trytoi16
def try_to_i16(self) -> Option<i16>
Narrow to i16, or None when the value doesn't fit. See try_to_i8.
32767.try_to_i16() => Some(32767i16)
32768.try_to_i16() => None
trytoi32
def try_to_i32(self) -> Option<i32>
Narrow to i32, or None when the value doesn't fit. See try_to_i8.
2147483647.try_to_i32() => Some(2147483647i32)
2147483648.try_to_i32() => None
trytoi64
def try_to_i64(self) -> Option<i64>
Narrow to i64, or None when the value doesn't fit. See try_to_i8. The checked twin of to_i64, and the one crossing where the bare form's saturating sentinels are most likely to surprise: None says the value never had a 64-bit reading, sentinels included.
9223372036854775807.try_to_i64() => Some(9223372036854775807i64)
9223372036854775808.try_to_i64() => None
trytou8
def try_to_u8(self) -> Option<u8>
Narrow to u8, or None when the value doesn't fit. See try_to_i8. A negative value fits no unsigned width, and it answers None in place of reinterpreting its bits.
255.try_to_u8() => Some(255u8)
256.try_to_u8() => None
(-1).try_to_u8() => None
trytou16
def try_to_u16(self) -> Option<u16>
Narrow to u16, or None when the value doesn't fit. See try_to_u8.
65535.try_to_u16() => Some(65535u16)
65536.try_to_u16() => None
trytou32
def try_to_u32(self) -> Option<u32>
Narrow to u32, or None when the value doesn't fit. See try_to_u8.
4294967295.try_to_u32() => Some(4294967295u32)
4294967296.try_to_u32() => None
trytou64
def try_to_u64(self) -> Option<u64>
Narrow to u64, or None when the value doesn't fit. See try_to_u8. The checked twin of to_u64, which reinterprets the low 64 bits and so turns -1 into u64's maximum; this one rejects it.
18446744073709551615.try_to_u64() => Some(18446744073709551615u64)
18446744073709551616.try_to_u64() => None
(-1).try_to_u64() => None
abs
def abs(self) -> int
The absolute value. int is arbitrary-precision, and every magnitude is representable, with no wrap case.
(-5).abs() => 5
5.abs() => 5
0.abs() => 0
pow
def pow(self, exp: int) -> rational
self raised to exp, exact for every exponent, and it therefore answers in rational and not in int. A negative exponent is a reciprocal, and 2.pow(-1) is 1/2; answering 0 there would be the unannounced loss of value this tier exists to prevent. The common case adds a crossing back: 10.pow(3).to_int().
0.pow(exp) for a negative exp divides by zero and so lands on the tier's own p/0 sentinel, and never on a failure.
10.pow(3) => 1000.to_rational()
2.pow(-1) => 1 / 2
(-2).pow(3) => (0 - 8).to_rational()
7.pow(0) => 1.to_rational()
0.pow(-1) => 1 / 0
upto
def upto(self, hi: int) -> Range
The inclusive range [self, hi] as a half-open Range: 1.upto(3) iterates 1, 2, 3. The inclusive counterpart to the half-open Range.new; empty when hi < self.
1.upto(3).length => 3
1.upto(3).fold(0, |acc, i| acc + i) => 6
3.upto(1).length => 0
times!
def times!(self, f: (int) -> () [e]) -> () [e]
Runs the effectful f self times, with the index 0 … self-1. A zero or negative count runs f no times. @no-doctest: effectful iteration returning unit; no value to assert
impl FromString<int>
parse
def parse(s: string) -> Result<int, ParseError>
Parses a base-10 int. It is arbitrary precision, and the only way to fail is for the text not to denote a whole number at all; there is no range to fall outside of. Surrounding whitespace is trimmed. Underscore separators belong to the literal grammar, for reading source; text being parsed is data, and no numeric parse accepts one.
int.parse("42") => Ok(42)
int.parse("-1") => Ok(-1)
int.parse("1_000") => Err(ParseError(input="1_000", reason="not a base-10 integer"))
int.parse("nope") => Err(ParseError(input="nope", reason="not a base-10 integer"))