Atom.to_charlist/1 allocates a new list per atom key when checking
if a keyword list contains module names. Atom.to_string/1 produces
a binary that can be pattern matched directly, avoiding O(n) temporary
allocations when inspecting large maps and keyword lists.
Benchmark (n=10):
keyword?/1 (10K): 0.62ms -> 0.48ms (1.3x)
keyword?/1 (50K): 2.72ms -> 1.69ms (1.6x)
inspect map (10K): 2.59ms -> 1.52ms (1.7x)
inspect map (50K): 8.85ms -> 5.94ms (1.5x)
Assisted-by: opencode:deepseek-v4-flash
Only preserve a shared union when absorption removes all
residual union terms. Normalize other equal-head intersections
eagerly to prevent exponential growth.
Closes#15629.
Assisted-by: Codex:GPT-5
- bdd_map on pure positive disjunction
- expand to the exact negation-free positive DNF, insert and union on negations and/or implicit :bdd_top
Fixes#15517
The guide pages under lib/elixir/pages are not doctests, so some
example outputs drifted from what current Elixir prints. I re-ran
each example on main (1.21.0-dev, OTP 29) with a clean IEx and
updated the blocks to match the actual output. Also fixes two typos.
Map ordering examples were left untouched since small-map print
order is not guaranteed across OTP versions.
Signed-off-by: Patric Vinicios <psdev7@gmail.com>
`Duration.t` and `Duration.unit_pair` referenced `Calendar.microsecond()`,
whose value is a `non_neg_integer`. However, durations may represent
negative amounts of time, negative microsecond values are advertised
by the module documentation, produced by parsing, and returned by the
module's own doctests:
Duration.from_iso8601!("PT-0.5S")
#=> %Duration{microsecond: {-500000, 1}}
Introduce a dedicated `Duration.microsecond()` type whose value is an
integer and whose precision is 0..6, matching what `new!/1` validates.
The range clause of `aggregate/3` only compared the two endpoints of the
range, which is correct solely for sorters consistent with the natural
integer order. Any other comparator returned a different result than
the identical call on the equivalent list or stream:
sorter = fn a, b -> rem(a, 5) >= rem(b, 5) end
Enum.max(1..10, sorter) #=> 1, expected 4
Enum.max(Enum.to_list(1..10), sorter) #=> 4
Once the range is known to be non-empty, take the endpoint shortcut
only when the sorter is one of the defaults (`&>=/2` or `&<=/2`, both
order-consistent). Any other sorter traverses the elements through
`reduce_range/5`, seeded with the first element.
Both functions document updating "in one pass, deleting duplicate keys",
but the `:pop` branch left any later duplicates in place:
Keyword.get_and_update([a: 1, a: 2], :a, fn _ -> :pop end)
#=> {1, [a: 2]}, expected {1, []}
The `Version` moduledoc and `Version.match?/3` documentation stated
unconditionally that, with `allow_pre: false`, a pre-release version
would not match unless the operand was itself a pre-release. This holds
for the `~>`, `>`, and `>=` operators, which carry the `allow_pre`
guard, but not for `<` and `<=`, which have no such guard and match
pre-releases according to precedence regardless of the option:
Version.match?("1.9.0-rc", ">= 1.0.0", allow_pre: false) # => false
Version.match?("1.9.0-rc", "< 2.0.0", allow_pre: false) # => true
The zero base-case clauses guarded only on comparison with 0, so under
Erlang term ordering non-integer arguments (floats, atoms) matched when
the other argument was 0, e.g. `Integer.extended_gcd(2.5, 0)` returned
`{2.5, 1, 0}` instead of raising, violating the spec and diverging from
`Integer.gcd/2`.
Candidates found by cross-checking @specs against the type signatures
inferred by the compiler (see #15559): these specs declared strictly
wider return types than the functions can produce, where the gap
carries no intentional API room (deliberately abstract contracts, such
as atom() covering future values, are left untouched):
* File.Stream.t/0: type the struct fields (mirrors IO.Stream.t/0)
* Map.new/0: %{} instead of map()
* Logger.levels/0: Exclude deprecated :warn
* Macro.Env.location/1 and Macro.Env.stacktrace/1: precise shapes