Skip to content

Commit 69c15cc

Browse files
ncavedbrattliclaude
authored
[All] Add missing Array, List, and Seq random choice/shuffle/sample members and tests (#4488)
Co-authored-by: Dag Brattli <dag@brattli.net> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cebb316 commit 69c15cc

59 files changed

Lines changed: 3179 additions & 1767 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Fable.Cli/CHANGELOG.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
* [All] Add support for `Guid.CreateVersion7()` and `Guid.CreateVersion7(DateTimeOffset)`
13-
* [Rust] Add missing `System.Random` implementation and tests (by @ncave)
14-
* [Rust] Add missing `Array`, `List` and `Seq` module members and tests: `randomChoice`, `randomChoiceBy`, `randomChoiceWith`, `randomChoices`, `randomChoicesBy`, `randomChoicesWith`, `randomSample`, `randomSampleBy`, `randomSampleWith`, `randomShuffle`, `randomShuffleBy`, `randomShuffleWith` (by @ncave)
15-
* [Beam] Implement missing DateTimeOffset members, add DateOnly and TimeOnly support
12+
* [All] Add support for `Guid.CreateVersion7()` and `Guid.CreateVersion7(DateTimeOffset)` (by @OnurGumus)
13+
* [All] Add missing `Array`, `List`, and `Seq` random choice/shuffle/sample members and tests (by @ncave)
14+
* [Dart/Rust] Add missing `System.Random` implementations and tests (by @ncave)
15+
* [Beam] Implement missing DateTimeOffset members, add DateOnly and TimeOnly support (by @dbrattli)
1616

1717
### Fixed
1818

1919
* [All] Fix unnecessary object allocations during AST traversal when visiting `Import` expressions (by Repo Assist)
20+
* [Beam] Fix `System.Random.Next(0)` implementation (by @ncave)
21+
* [Python] Fix `System.Random` seeded implementation (by @ncave)
22+
* [Beam] Fix `System.Random` seeded implementation to use per-instance state (by @dbrattli)
2023
* [Dart] Fix `Array.compareWith` comparing lengths before elements, producing wrong results for arrays with common prefixes (fixes #2961)
2124
* [Python] Fix unsafe option unwrapping in `DateTimeOffset.get_Offset` and regex replacements (by @dbrattli)
2225
* [All] Replace unsafe option `.Value` unwrapping with safe alternatives in Python/Replacements.fs and Rust/Fable2Rust.fs (code scanning alerts IONIDE-006)

src/Fable.Compiler/CHANGELOG.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
* [All] Add support for `Guid.CreateVersion7()` and `Guid.CreateVersion7(DateTimeOffset)`
13-
* [Rust] Add missing `System.Random` implementation and tests (by @ncave)
14-
* [Rust] Add missing `Array`, `List` and `Seq` module members and tests: `randomChoice`, `randomChoiceBy`, `randomChoiceWith`, `randomChoices`, `randomChoicesBy`, `randomChoicesWith`, `randomSample`, `randomSampleBy`, `randomSampleWith`, `randomShuffle`, `randomShuffleBy`, `randomShuffleWith` (by @ncave)
15-
* [Beam] Implement missing DateTimeOffset members, add DateOnly and TimeOnly support
12+
* [All] Add support for `Guid.CreateVersion7()` and `Guid.CreateVersion7(DateTimeOffset)` (by @OnurGumus)
13+
* [All] Add missing `Array`, `List`, and `Seq` random choice/shuffle/sample members and tests (by @ncave)
14+
* [Dart/Rust] Add missing `System.Random` implementations and tests (by @ncave)
15+
* [Beam] Implement missing DateTimeOffset members, add DateOnly and TimeOnly support (by @dbrattli)
1616

1717
### Fixed
1818

1919
* [All] Fix unnecessary object allocations during AST traversal when visiting `Import` expressions (by Repo Assist)
20+
* [Beam] Fix `System.Random.Next(0)` implementation (by @ncave)
21+
* [Python] Fix `System.Random` seeded implementation (by @ncave)
22+
* [Beam] Fix `System.Random` seeded implementation to use per-instance state (by @dbrattli)
2023
* [Dart] Fix `Array.compareWith` comparing lengths before elements, producing wrong results for arrays with common prefixes (fixes #2961)
2124
* [Python] Fix unsafe option unwrapping in `DateTimeOffset.get_Offset` and regex replacements (by @dbrattli)
2225
* [All] Replace unsafe option `.Value` unwrapping with safe alternatives in Python/Replacements.fs and Rust/Fable2Rust.fs (code scanning alerts IONIDE-006)

src/Fable.Transforms/Beam/Replacements.fs

Lines changed: 127 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1587,6 +1587,35 @@ let private listModule
15871587
| "OfArray", [ arr ] -> derefArr r arr |> Some
15881588
| "OfSeq", [ seq ] -> emitExpr r t [ seq ] "fable_utils:to_list($0)" |> Some
15891589
| "ToSeq", [ list ] -> Some(List.head args)
1590+
| "RandomShuffleBy", [ randomizer; list ] ->
1591+
Helper.LibCall(com, "fable_list", "random_shuffle_by", t, [ randomizer; list ])
1592+
|> Some
1593+
| "RandomShuffleWith", [ random; list ] ->
1594+
Helper.LibCall(com, "fable_list", "random_shuffle_with", t, [ random; list ])
1595+
|> Some
1596+
| "RandomShuffle", [ list ] -> Helper.LibCall(com, "fable_list", "random_shuffle", t, [ list ]) |> Some
1597+
| "RandomChoiceBy", [ randomizer; list ] ->
1598+
Helper.LibCall(com, "fable_list", "random_choice_by", t, [ randomizer; list ])
1599+
|> Some
1600+
| "RandomChoiceWith", [ random; list ] ->
1601+
Helper.LibCall(com, "fable_list", "random_choice_with", t, [ random; list ])
1602+
|> Some
1603+
| "RandomChoice", [ list ] -> Helper.LibCall(com, "fable_list", "random_choice", t, [ list ]) |> Some
1604+
| "RandomChoicesBy", [ randomizer; count; list ] ->
1605+
Helper.LibCall(com, "fable_list", "random_choices_by", t, [ randomizer; count; list ])
1606+
|> Some
1607+
| "RandomChoicesWith", [ random; count; list ] ->
1608+
Helper.LibCall(com, "fable_list", "random_choices_with", t, [ random; count; list ])
1609+
|> Some
1610+
| "RandomChoices", [ count; list ] ->
1611+
Helper.LibCall(com, "fable_list", "random_choices", t, [ count; list ]) |> Some
1612+
| "RandomSampleBy", [ randomizer; count; list ] ->
1613+
Helper.LibCall(com, "fable_list", "random_sample_by", t, [ randomizer; count; list ])
1614+
|> Some
1615+
| "RandomSampleWith", [ random; count; list ] ->
1616+
Helper.LibCall(com, "fable_list", "random_sample_with", t, [ random; count; list ])
1617+
|> Some
1618+
| "RandomSample", [ count; list ] -> Helper.LibCall(com, "fable_list", "random_sample", t, [ count; list ]) |> Some
15901619
| _ -> None
15911620

15921621
/// Beam-specific FSharpList instance method replacements.
@@ -2119,6 +2148,91 @@ let private arrayModule
21192148
let a1 = derefArr r a1
21202149
let a2 = derefArr r a2
21212150
Helper.LibCall(com, "fable_list", "compare_with", t, [ fn; a1; a2 ]) |> Some
2151+
| "RandomShuffleBy", [ randomizer; arr ] ->
2152+
let lst = derefArr r arr
2153+
2154+
Helper.LibCall(com, "fable_list", "random_shuffle_by", t, [ randomizer; lst ])
2155+
|> wrapArr com r t
2156+
|> Some
2157+
| "RandomShuffleWith", [ random; arr ] ->
2158+
let lst = derefArr r arr
2159+
2160+
Helper.LibCall(com, "fable_list", "random_shuffle_with", t, [ random; lst ])
2161+
|> wrapArr com r t
2162+
|> Some
2163+
| "RandomShuffle", [ arr ] ->
2164+
let lst = derefArr r arr
2165+
2166+
Helper.LibCall(com, "fable_list", "random_shuffle", t, [ lst ])
2167+
|> wrapArr com r t
2168+
|> Some
2169+
| "RandomShuffleInPlaceBy", [ randomizer; arr ] ->
2170+
let lst = derefArr r arr
2171+
2172+
let shuffled =
2173+
Helper.LibCall(com, "fable_list", "random_shuffle_by", Any, [ randomizer; lst ])
2174+
2175+
emitExpr r t [ arr; shuffled ] "begin erlang:put($0, $1), ok end" |> Some
2176+
| "RandomShuffleInPlaceWith", [ random; arr ] ->
2177+
let lst = derefArr r arr
2178+
2179+
let shuffled =
2180+
Helper.LibCall(com, "fable_list", "random_shuffle_with", Any, [ random; lst ])
2181+
2182+
emitExpr r t [ arr; shuffled ] "begin erlang:put($0, $1), ok end" |> Some
2183+
| "RandomShuffleInPlace", [ arr ] ->
2184+
let lst = derefArr r arr
2185+
let shuffled = Helper.LibCall(com, "fable_list", "random_shuffle", Any, [ lst ])
2186+
emitExpr r t [ arr; shuffled ] "begin erlang:put($0, $1), ok end" |> Some
2187+
| "RandomChoiceBy", [ randomizer; arr ] ->
2188+
let lst = derefArr r arr
2189+
2190+
Helper.LibCall(com, "fable_list", "random_choice_by", t, [ randomizer; lst ])
2191+
|> Some
2192+
| "RandomChoiceWith", [ random; arr ] ->
2193+
let lst = derefArr r arr
2194+
2195+
Helper.LibCall(com, "fable_list", "random_choice_with", t, [ random; lst ])
2196+
|> Some
2197+
| "RandomChoice", [ arr ] ->
2198+
let lst = derefArr r arr
2199+
Helper.LibCall(com, "fable_list", "random_choice", t, [ lst ]) |> Some
2200+
| "RandomChoicesBy", [ randomizer; count; arr ] ->
2201+
let lst = derefArr r arr
2202+
2203+
Helper.LibCall(com, "fable_list", "random_choices_by", t, [ randomizer; count; lst ])
2204+
|> wrapArr com r t
2205+
|> Some
2206+
| "RandomChoicesWith", [ random; count; arr ] ->
2207+
let lst = derefArr r arr
2208+
2209+
Helper.LibCall(com, "fable_list", "random_choices_with", t, [ random; count; lst ])
2210+
|> wrapArr com r t
2211+
|> Some
2212+
| "RandomChoices", [ count; arr ] ->
2213+
let lst = derefArr r arr
2214+
2215+
Helper.LibCall(com, "fable_list", "random_choices", t, [ count; lst ])
2216+
|> wrapArr com r t
2217+
|> Some
2218+
| "RandomSampleBy", [ randomizer; count; arr ] ->
2219+
let lst = derefArr r arr
2220+
2221+
Helper.LibCall(com, "fable_list", "random_sample_by", t, [ randomizer; count; lst ])
2222+
|> wrapArr com r t
2223+
|> Some
2224+
| "RandomSampleWith", [ random; count; arr ] ->
2225+
let lst = derefArr r arr
2226+
2227+
Helper.LibCall(com, "fable_list", "random_sample_with", t, [ random; count; lst ])
2228+
|> wrapArr com r t
2229+
|> Some
2230+
| "RandomSample", [ count; arr ] ->
2231+
let lst = derefArr r arr
2232+
2233+
Helper.LibCall(com, "fable_list", "random_sample", t, [ count; lst ])
2234+
|> wrapArr com r t
2235+
|> Some
21222236
// === Transform ops: deref input(s) AND wrap result ===
21232237
| "Map", [ fn; arr ] ->
21242238
let arr = derefArr r arr
@@ -3936,18 +4050,24 @@ let private randoms
39364050
match args with
39374051
| [ seed ] -> Helper.LibCall(com, "fable_random", "new_seeded", t, [ seed ], ?loc = r) |> Some
39384052
| _ -> Helper.LibCall(com, "fable_random", "new", t, [], ?loc = r) |> Some
3939-
| "Next", Some _ ->
4053+
| "Next", Some thisArg ->
39404054
match args with
3941-
| [] -> Helper.LibCall(com, "fable_random", "next", t, [], ?loc = r) |> Some
3942-
| [ maxVal ] -> Helper.LibCall(com, "fable_random", "next", t, [ maxVal ], ?loc = r) |> Some
4055+
| [] -> Helper.LibCall(com, "fable_random", "next", t, [ thisArg ], ?loc = r) |> Some
4056+
| [ maxVal ] ->
4057+
Helper.LibCall(com, "fable_random", "next", t, [ thisArg; maxVal ], ?loc = r)
4058+
|> Some
39434059
| [ minVal; maxVal ] ->
3944-
Helper.LibCall(com, "fable_random", "next", t, [ minVal; maxVal ], ?loc = r)
4060+
Helper.LibCall(com, "fable_random", "next", t, [ thisArg; minVal; maxVal ], ?loc = r)
39454061
|> Some
39464062
| _ -> None
3947-
| "NextDouble", Some _ -> Helper.LibCall(com, "fable_random", "next_double", t, [], ?loc = r) |> Some
3948-
| "NextBytes", Some _ ->
4063+
| "NextDouble", Some thisArg ->
4064+
Helper.LibCall(com, "fable_random", "next_double", t, [ thisArg ], ?loc = r)
4065+
|> Some
4066+
| "NextBytes", Some thisArg ->
39494067
match args with
3950-
| [ arr ] -> Helper.LibCall(com, "fable_random", "next_bytes", t, [ arr ], ?loc = r) |> Some
4068+
| [ arr ] ->
4069+
Helper.LibCall(com, "fable_random", "next_bytes", t, [ thisArg; arr ], ?loc = r)
4070+
|> Some
39514071
| _ -> None
39524072
| _ -> None
39534073

src/Fable.Transforms/Dart/Fable2Dart.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ module Util =
226226
| "Fable.Core.Dart.DartNullable`1", [ genArg ] -> Nullable genArg
227227
| Types.regexGroup, _ -> Nullable String
228228
| Types.regexMatch, _ -> makeTypeRefFromName "Match" []
229+
| "System.Random", _ -> libTypeRef com ctx "Random" "Random" []
229230
// We use `dynamic` for Exception because there is no single type that catches all errors in Dart
230231
// (except when inherited as base class, then it's getExceptionType, see transformInheritedClass)
231232
| Types.exception_, _ -> Dynamic // getExceptionType com ctx

src/Fable.Transforms/Dart/Replacements.fs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,7 @@ let arrayModule (com: ICompiler) (ctx: Context) r (t: Type) (i: CallInfo) (_: Ex
18161816
let t = genArg com ctx r 0 i.GenericArgs
18171817
makeArrayWithRange r t [] |> Some
18181818
| "IsEmpty", [ ar ] -> getFieldWith r t ar "isEmpty" |> Some
1819+
| "Contains", [ value; ar ] -> Helper.LibCall(com, "Array", "contains", t, [ value; ar ], ?loc = r) |> Some
18191820
| "CopyTo", args -> copyToArray com r t i args
18201821
| ("Distinct" | "DistinctBy" | "Except" | "GroupBy" | "CountBy" as meth), args ->
18211822
let meth = Naming.lowerFirst meth
@@ -3076,11 +3077,18 @@ let globalization
30763077
let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
30773078
match i.CompiledName, thisArg with
30783079
| ".ctor", _ ->
3079-
match args with
3080-
| [] -> Helper.LibCall(com, "Random", "nonSeeded", t, [], [], ?loc = r) |> Some
3081-
| args ->
3082-
Helper.LibCall(com, "Random", "seeded", t, args, i.SignatureArgTypes, genArgs = i.GenericArgs, ?loc = r)
3083-
|> Some
3080+
Helper.LibCall(
3081+
com,
3082+
"Random",
3083+
"Random",
3084+
t,
3085+
args,
3086+
i.SignatureArgTypes,
3087+
genArgs = i.GenericArgs,
3088+
isConstructor = true,
3089+
?loc = r
3090+
)
3091+
|> Some
30843092
// Not yet supported
30853093
| ("NextInt64" | "NextSingle"), _ -> None
30863094
| meth, Some thisArg ->

src/Fable.Transforms/Python/Replacements.fs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3254,30 +3254,32 @@ let globalization
32543254
ObjectExpr([], t, None) |> Some
32553255
| _ -> None
32563256

3257-
let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (_: Expr option) (args: Expr list) =
3258-
match i.CompiledName with
3259-
| ".ctor" -> ObjectExpr([], t, None) |> Some
3260-
| "Next" ->
3257+
let random (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
3258+
match i.CompiledName, thisArg with
3259+
| ".ctor", _ ->
3260+
Helper.LibCall(com, "util", "create_random", t, args, i.SignatureArgTypes, ?loc = r)
3261+
|> Some
3262+
| "Next", Some thisArg ->
32613263
let min, max =
32623264
match args with
32633265
| [] -> makeIntConst 0, makeIntConst System.Int32.MaxValue
32643266
| [ max ] -> makeIntConst 0, max
32653267
| [ min; max ] -> min, max
32663268
| _ -> FableError "Unexpected arg count for Random.Next" |> raise
32673269

3268-
Helper.LibCall(com, "util", "randint", t, [ min; max ], [ min.Type; max.Type ], ?loc = r)
3269-
|> Some
3270-
| "NextDouble" ->
3271-
let ranExpr = Helper.ImportedCall("random", "random", t, [], [])
3272-
Helper.LibCall(com, "core", "float64", t, [ ranExpr ], ?loc = r) |> Some
3273-
| "NextBytes" ->
3274-
let byteArray =
3275-
match args with
3276-
| [ b ] -> b
3277-
| _ -> FableError "Unexpected arg count for Random.NextBytes" |> raise
3270+
let args = [ thisArg; min; max ]
3271+
let argTypes = [ thisArg.Type; min.Type; max.Type ]
3272+
Helper.LibCall(com, "util", "random_int", t, args, argTypes, ?loc = r) |> Some
3273+
| "NextDouble", Some thisArg ->
3274+
let args = [ thisArg ]
3275+
let argTypes = [ thisArg.Type ]
32783276

3279-
Helper.LibCall(com, "util", "random_bytes", t, [ byteArray ], [ byteArray.Type ], ?loc = r)
3277+
Helper.LibCall(com, "util", "random_double", t, args, argTypes, ?loc = r)
32803278
|> Some
3279+
| "NextBytes", Some thisArg ->
3280+
let args = thisArg :: args
3281+
let argTypes = thisArg.Type :: i.SignatureArgTypes
3282+
Helper.LibCall(com, "util", "random_bytes", t, args, argTypes, ?loc = r) |> Some
32813283
| _ -> None
32823284

32833285
let cancels (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =

0 commit comments

Comments
 (0)