Skip to content

fix(js/ts/python/dart/rust/beam): break min and max ties like FSharp.Core - #5198

Open
leonitousconforti wants to merge 5 commits into
fable-compiler:mainfrom
leonitousconforti:fix-min-tie-breaking
Open

leonitousconforti wants to merge 5 commits into
fable-compiler:mainfrom
leonitousconforti:fix-min-tie-breaking

Conversation

@leonitousconforti

Copy link
Copy Markdown
Contributor

The min/max collection functions and the max operator now pick the same element as FSharp.Core when values tie. One commit per target.

  • List/Array/Seq.minBy returned the last of tied minima and now return the first: [ "a", 1; "b", 0; "c", 0 ] |> List.minBy snd gave ("c", 0), now ("b", 0). minBy and maxBy also call the projection once per element, as FSharp.Core does.
  • List/Array/Seq.min returned the last of equal elements: List.min [ 1.0m; 1.00m ] gave 1.00, now 1.0.
  • The max operator returned its second argument on a tie (e.g. a type with a custom CompareTo). FSharp.Core returns the first. min already matched.
  • Python: the Rust-backed Array.max/min kept the last of equal elements too.
  • Rust: maxBy/max kept the last as well, and the decimal max/min operators broke ties the other way.
  • Beam: minBy/maxBy broke key ties by Erlang term order of the elements.

Notes:

  • Python is fixed in place, like fix(js/ts/python/dart/beam): add FSharp.Core's argument checks to collection functions #5154. I can switch to new names as AGENTS.md asks if you prefer.
  • NaN handling is unchanged, apart from which of several NaN keys minBy returns. The remaining NaN differences come from compare using a total order, which seems like a separate issue.
  • Not covered: Dart orders -0.0 below 0.0 (List.min [ 0.0; -0.0 ] gives -0.0), and Rust's float max/min operators use f64::max/min.

- List, Array and Seq minBy returned the last of the tied minima; they
  now return the first, and minBy and maxBy call the projection once per
  element instead of twice per step.
- List, Array and Seq min returned the last of equal minima, visible
  with 1.0m and 1.00m, -0.0 and 0.0 or a custom comparison. They now
  return the first, like max already did.
- The max operator returned its second argument when the values compare
  equal; it now returns the first like FSharp.Core (min already returned
  the second, as FSharp.Core does).
- List and Seq minBy returned the last of the tied minima; they now
  return the first. List, Array and Seq minBy and maxBy call the
  projection once per element instead of twice per step.
- List and Seq min, and Array min and max, returned the last of equal
  elements (e.g. -0.0 and 0.0); they now return the first.
- The max operator returned its second argument when the values compare
  equal; it now returns the first like FSharp.Core.
- List, Array and Seq minBy returned the last of the tied minima; they
  now return the first, and minBy and maxBy call the projection once per
  element instead of twice per step.
- List, Array and Seq min returned the last of equal elements (e.g. with
  a custom comparison); they now return the first, like max already did.
- The max operator returned its second argument when the values compare
  equal; it now returns the first like FSharp.Core.
- List, Array and Seq minBy, maxBy, min and max returned the last of
  tied elements; they now return the first, comparing each element with
  the current one like FSharp.Core. minBy and maxBy call the projection
  once per element instead of twice per step.
- The max operator returned its second argument when the values compare
  equal; it now returns the first like FSharp.Core.
- The decimal max and min operators used Rust's Ord::max and Ord::min,
  which break ties the other way round from FSharp.Core (max 1.0m 1.00m
  gave 1.00). They now go through Decimal max and min, which match it.
- List and Array minBy and maxBy broke key ties by comparing the
  elements themselves in Erlang term order, so they returned the
  smallest or largest of the tied elements. They now return the first,
  as they already did for union keys.
- Seq minBy returned the last of the tied minima, and Seq min the last
  of equal elements; they now return the first. Seq minBy and maxBy call
  the projection once per element instead of twice per step.
leonitousconforti added a commit to leonitousconforti/flip7 that referenced this pull request Nov 6, 2026
Only the min tie, fable-compiler/Fable#5198, is still listed. ParamName and
Convert.ToHexString come from Fable now, and Assert.Throws no longer singles
out a plain Exception.
leonitousconforti added a commit to leonitousconforti/flip7 that referenced this pull request Nov 6, 2026
Only the min tie, fable-compiler/Fable#5198, is still listed. ParamName and
Convert.ToHexString come from Fable now, and Assert.Throws no longer singles
out a plain Exception.
leonitousconforti added a commit to leonitousconforti/flip7 that referenced this pull request Nov 6, 2026
Fable's List.minBy keeps the last of a tie until fable-compiler/Fable#5198
ships, so until then the fable job fails on this game.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant