From 093a3ec11d78a88aaa88d3d0fca0d592a812a304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Mon, 28 Apr 2025 22:24:07 +0200 Subject: [PATCH 01/12] Add a PRNG-based 'random' module --- examples/stdlib/acme.effekt | 1 + libraries/common/random.effekt | 97 ++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 libraries/common/random.effekt diff --git a/examples/stdlib/acme.effekt b/examples/stdlib/acme.effekt index 7ae412dfa..cffcd9178 100644 --- a/examples/stdlib/acme.effekt +++ b/examples/stdlib/acme.effekt @@ -24,6 +24,7 @@ import map import option import process import queue +import random import ref import regex import resizable_array diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt new file mode 100644 index 000000000..57a849666 --- /dev/null +++ b/libraries/common/random.effekt @@ -0,0 +1,97 @@ +module random + +import stream + +/// A streaming source of byte-level randomness. +def minstd(seed: Int): Unit / emit[Byte] = { + // Initialize state with seed, ensuring it's not zero + var state = if (seed == 0) 1 else seed + + def nextInt(): Int = { + // Park-Miller minimal standard PRNG + // Uses only at most 32-bit integers internally + val a = 48271 + val m = 2147483647 + + val q = m / a // 44488 + val r = m.mod(a) // 3399 + + val div = state / q // max: M / Q = A = 48,271 + val rem = state.mod(q) // max: Q - 1 = 44,487 + + val s = rem * a; // max: 44,487 * 48,271 = 2,147,431,977 = 0x7fff3629 + val t = div * r; // max: 48,271 * 3,399 = 164,073,129 + + val result = s - t + // keep the state positive + if (result < 0) result + m else result + } + + while (true) { + state = nextInt() + val b = state.mod(256).toByte + do emit(b) + } +} + +/// A think wrapper over `minstd` and `stream::source` +def minstd(seed: Int) { randomnessReader: () => Unit / read[Byte] }: Unit = + source[Byte] { minstd(seed) } {randomnessReader} + +// randomness functions + +def randomBool(): Bool / {read[Byte], stop} = { + val b = do read[Byte]() + b.toInt.mod(2) == 1 +} + +def randomInt32(): Int / {read[Byte], stop} = { + var result = 0 + repeat(4) { + val b = do read[Byte]() + result = result.bitwiseShl(8).bitwiseOr(b.toInt) + } + result +} + +def randomInt64(): Int / {read[Byte], stop} = { + var result = 0 + repeat(8) { + val b = do read[Byte]() + result = result.bitwiseShl(8).bitwiseOr(b.toInt) + } + result +} + +/// `max` is _inclusive_! +def randomInt(min: Int, max: Int): Int / {read[Byte], stop} = { + if (min > max) { + randomInt(max, min) + } else { + val range = max - min + 1 + val bytesNeeded = (log(range.toDouble) / log(256.0)).ceil + + var result = 0 + repeat(bytesNeeded) { + val b = do read[Byte]() + result = result.bitwiseShl(8).bitwiseOr(b.toInt) + } + + min + (abs(result).mod(range)) + } +} + +namespace examples { + def main() = { + with boundary; + with minstd(1337); + + repeat(10) { + val a = randomInt(1, 10) + val b = randomInt(1, 100) + val c = randomInt(1, 1000) + println(a.show ++ " " ++ b.show ++ " " ++ c.show) + println(a + b + c) + } + } +} \ No newline at end of file From e9ae19a9fb3c30dbaf6ae91079fd823999017b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 13:32:49 +0200 Subject: [PATCH 02/12] Finalise the module --- libraries/common/random.effekt | 131 ++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 28 deletions(-) diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt index 57a849666..4a5d5a965 100644 --- a/libraries/common/random.effekt +++ b/libraries/common/random.effekt @@ -1,8 +1,16 @@ module random import stream +import io/error -/// A streaming source of byte-level randomness. +/// Infinite pull stream of random bytes. +effect random(): Byte + +// --------------------- +// Sources of randomness + +/// A streaming source (push stream) of byte-level randomness. +/// Deterministic: needs a 32bit `seed` -- you can use `bench::timestamp`. def minstd(seed: Int): Unit / emit[Byte] = { // Initialize state with seed, ensuring it's not zero var state = if (seed == 0) 1 else seed @@ -13,14 +21,14 @@ def minstd(seed: Int): Unit / emit[Byte] = { val a = 48271 val m = 2147483647 - val q = m / a // 44488 - val r = m.mod(a) // 3399 + val q = m / a // 44488 + val r = m.mod(a) // 3399 - val div = state / q // max: M / Q = A = 48,271 - val rem = state.mod(q) // max: Q - 1 = 44,487 + val div = state / q // max: M / Q = A = 48271 + val rem = state.mod(q) // max: Q - 1 = 44487 - val s = rem * a; // max: 44,487 * 48,271 = 2,147,431,977 = 0x7fff3629 - val t = div * r; // max: 48,271 * 3,399 = 164,073,129 + val s = rem * a; // max: 44487 * 48271 = 2147431977 + val t = div * r; // max: 48271 * 3399 = 164073129 val result = s - t // keep the state positive @@ -34,37 +42,67 @@ def minstd(seed: Int): Unit / emit[Byte] = { } } -/// A think wrapper over `minstd` and `stream::source` -def minstd(seed: Int) { randomnessReader: () => Unit / read[Byte] }: Unit = - source[Byte] { minstd(seed) } {randomnessReader} - -// randomness functions +/// A thin wrapper over `minstd`, handling a reader of random bytes. +/// Deterministic: needs a 32bit `seed` -- you can use `bench::timestamp`. +/// +/// Implementation is something like `stream::source`, specialized for bytes and the `random` effect. +def minstd(seed: Int) { randomnessReader: () => Unit / random }: Unit = { + var next = box { 255.toByte } // sentinel value + next = box { + try { + minstd(seed) + <> // safe: randomness generator cannot run out of numbers... + } with emit[Byte] { v => + next = box { resume(()) } + v + } + } -def randomBool(): Bool / {read[Byte], stop} = { - val b = do read[Byte]() - b.toInt.mod(2) == 1 + try randomnessReader() with random { + resume(next()) + } } -def randomInt32(): Int / {read[Byte], stop} = { - var result = 0 - repeat(4) { - val b = do read[Byte]() - result = result.bitwiseShl(8).bitwiseOr(b.toInt) +/// CSPRNG from `/dev/urandom`, handling a reader of random bytes. +/// Only works on Unix-like OSes! +def devurandom { randomnessReader: () => Unit / random }: Unit / {Exception[IOError], stop} = { + with readFile("/dev/urandom") + try randomnessReader() with random { + resume(do read[Byte]()) } - result } -def randomInt64(): Int / {read[Byte], stop} = { +// ------------------------ +// Functions using `random` +// +// Always two variants: +// - readType(): Type / random +// - readTypes(): Unit / {emit[Type], random} + +def randomByte(): Byte / random = do random() +def randomBytes(): Unit / {emit[Byte], random} = + while (true) do emit(do random()) + +def randomBool(): Bool / random = { + val b = do random() + b.toInt.mod(2) == 1 +} +def randomBools(): Unit / {emit[Bool], random} = + while (true) do emit(randomBool()) + +def randomInt32(): Int / random = { var result = 0 - repeat(8) { - val b = do read[Byte]() + repeat(4) { + val b = do random() result = result.bitwiseShl(8).bitwiseOr(b.toInt) } result } +def randomInt32s(): Unit / {emit[Int], random} = + while (true) do emit(randomInt32()) /// `max` is _inclusive_! -def randomInt(min: Int, max: Int): Int / {read[Byte], stop} = { +def randomInt(min: Int, max: Int): Int / random = { if (min > max) { randomInt(max, min) } else { @@ -73,7 +111,7 @@ def randomInt(min: Int, max: Int): Int / {read[Byte], stop} = { var result = 0 repeat(bytesNeeded) { - val b = do read[Byte]() + val b = do random() result = result.bitwiseShl(8).bitwiseOr(b.toInt) } @@ -81,11 +119,38 @@ def randomInt(min: Int, max: Int): Int / {read[Byte], stop} = { } } +/// `max` is _inclusive_! +def randomInts(min: Int, max: Int): Unit / {emit[Int], random} = + while (true) do emit(randomInt(min, max)) + +def randomIntWidth(bits: Int): Int / random = { + def divCeil(n: Int, m: Int) = (n + m - 1) / m + + val max = 1.bitwiseShl(bits) + + val bytesNeeded = bits.divCeil(4) + var result = 0 + repeat(bytesNeeded) { + val b = do random() + result = result.bitwiseShl(8).bitwiseOr(b.toInt) + } + + abs(result).mod(max) +} + +/// Random double between 0.0 and 1.0 +def randomDouble(): Double / random = + (randomInt32().toDouble / 1.bitwiseShl(31).toDouble).abs + // This is not perfect, but it will do for now. + namespace examples { - def main() = { - with boundary; +def main() = { with minstd(1337); + repeat(10) { + println(randomDouble()) + } + repeat(10) { val a = randomInt(1, 10) val b = randomInt(1, 100) @@ -94,4 +159,14 @@ namespace examples { println(a + b + c) } } + + def unixRandom(): Unit = { + with boundary; + with on[IOError].report; + with devurandom; + + repeat(2) { + println(randomInt32()) + } + } } \ No newline at end of file From 39163f5e104f54fd7bd4071bb3114b4e5f126d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 13:35:32 +0200 Subject: [PATCH 03/12] Add a simple unit test --- examples/stdlib/random.check | 30 ++++++++++++++++++++++++++++++ examples/stdlib/random.effekt | 3 +++ 2 files changed, 33 insertions(+) create mode 100644 examples/stdlib/random.check create mode 100644 examples/stdlib/random.effekt diff --git a/examples/stdlib/random.check b/examples/stdlib/random.check new file mode 100644 index 000000000..e6d7375c5 --- /dev/null +++ b/examples/stdlib/random.check @@ -0,0 +1,30 @@ +0.31433934113010764 +0.5641868240199983 +0.42061583511531353 +0.22948364494368434 +0.5833548144437373 +0.6392239071428776 +0.19745560782030225 +0.9339752634987235 +0.24995529558509588 +0.9832920236513019 +9 100 131 +240 +7 53 537 +597 +2 16 167 +185 +10 78 307 +395 +7 6 52 +65 +4 1 407 +412 +6 91 617 +714 +8 74 725 +807 +9 21 810 +840 +7 26 60 +93 diff --git a/examples/stdlib/random.effekt b/examples/stdlib/random.effekt new file mode 100644 index 000000000..c75109486 --- /dev/null +++ b/examples/stdlib/random.effekt @@ -0,0 +1,3 @@ +import random + +def main() = random::examples::main() From ba3efe138a0290a64fdd46ce304641f028784a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 13:45:53 +0200 Subject: [PATCH 04/12] Try to fix float division on Chez --- libraries/common/effekt.effekt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index 06e271db0..67d0391df 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -344,7 +344,7 @@ extern pure def infixSub(x: Double, y: Double): Double = extern pure def infixDiv(x: Double, y: Double): Double = js "(${x} / ${y})" - chez "(/ ${x} ${y})" + chez "(fl/ ${x} ${y})" llvm "%z = fdiv %Double ${x}, ${y} ret %Double %z" vm "effekt::infixDiv(Double, Double)" @@ -442,7 +442,7 @@ extern pure def toInt(d: Double): Int = extern pure def toDouble(d: Int): Double = js "${d}" - chez "${d}" + chez "(fixnum->flonum ${d})" llvm "%z = sitofp i64 ${d} to double ret double %z" vm "effekt::toDouble(Int)" From 386903c181c1542bf36693520caf7ddcc6c0fc85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 13:48:11 +0200 Subject: [PATCH 05/12] Modify tests --- examples/stdlib/random.check | 73 ++++++++++++++++++++-------------- libraries/common/random.effekt | 15 +++++-- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/examples/stdlib/random.check b/examples/stdlib/random.check index e6d7375c5..8a39272c1 100644 --- a/examples/stdlib/random.check +++ b/examples/stdlib/random.check @@ -1,30 +1,43 @@ -0.31433934113010764 -0.5641868240199983 -0.42061583511531353 -0.22948364494368434 -0.5833548144437373 -0.6392239071428776 -0.19745560782030225 -0.9339752634987235 -0.24995529558509588 -0.9832920236513019 -9 100 131 -240 -7 53 537 -597 -2 16 167 -185 -10 78 307 -395 -7 6 52 -65 -4 1 407 -412 -6 91 617 -714 -8 74 725 -807 -9 21 810 -840 -7 26 60 -93 +int32s: +-675038595 +-1211581979 +-903265628 +492812375 +-1252744925 +1372722888 +-424032689 +2005696606 +536774910 +2111603542 +doubles: +0.8376555209979415 +0.22971605509519577 +0.24265884514898062 +0.2055133944377303 +0.3873926908709109 +0.8858895925804973 +0.6668298579752445 +0.05998786725103855 +0.8370264428667724 +0.28201948618516326 +randomInt: +4 8 8 +488 +6 9 7 +697 +1 8 1 +181 +2 1 4 +214 +4 9 0 +490 +0 6 2 +62 +4 2 4 +424 +9 7 9 +979 +7 4 3 +743 +5 3 9 +539 diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt index 4a5d5a965..2411bc5d8 100644 --- a/libraries/common/random.effekt +++ b/libraries/common/random.effekt @@ -147,16 +147,23 @@ namespace examples { def main() = { with minstd(1337); + println("int32s:") + repeat(10) { + println(randomInt32()) + } + + println("doubles:") repeat(10) { println(randomDouble()) } + println("randomInt:") repeat(10) { - val a = randomInt(1, 10) - val b = randomInt(1, 100) - val c = randomInt(1, 1000) + val a = randomInt(0, 9) + val b = randomInt(0, 9) + val c = randomInt(0, 9) println(a.show ++ " " ++ b.show ++ " " ++ c.show) - println(a + b + c) + println(a*100 + b*10 + c) } } From 9d9b8b9d3e2ef3a363426ed86b90b8e9998b9a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 14:21:05 +0200 Subject: [PATCH 06/12] Sync JS and LLVM --- libraries/common/random.effekt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt index 2411bc5d8..a12b9e68e 100644 --- a/libraries/common/random.effekt +++ b/libraries/common/random.effekt @@ -94,9 +94,10 @@ def randomInt32(): Int / random = { var result = 0 repeat(4) { val b = do random() - result = result.bitwiseShl(8).bitwiseOr(b.toInt) + result = result * 256 + b.toInt } - result + val signBit = result.bitwiseShr(31).bitwiseAnd(1) == 0 + result.mod(1.bitwiseShl(31)).abs * if (signBit) 1 else -1 } def randomInt32s(): Unit / {emit[Int], random} = while (true) do emit(randomInt32()) @@ -112,7 +113,7 @@ def randomInt(min: Int, max: Int): Int / random = { var result = 0 repeat(bytesNeeded) { val b = do random() - result = result.bitwiseShl(8).bitwiseOr(b.toInt) + result = result * 256 + b.toInt } min + (abs(result).mod(range)) @@ -152,9 +153,15 @@ def main() = { println(randomInt32()) } + println("int32s, part2:") + repeat(10) { + println(randomInt(0, 2147483647)) + println(randomInt(-2147483648, 0)) + } + println("doubles:") repeat(10) { - println(randomDouble()) + println(randomDouble().round(3)) } println("randomInt:") From 5745f599ef980783f81583eb4f4d0f325c962074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 14:22:07 +0200 Subject: [PATCH 07/12] Remove unused function, add randomDoubles --- libraries/common/random.effekt | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt index a12b9e68e..50b15ed29 100644 --- a/libraries/common/random.effekt +++ b/libraries/common/random.effekt @@ -119,30 +119,18 @@ def randomInt(min: Int, max: Int): Int / random = { min + (abs(result).mod(range)) } } - /// `max` is _inclusive_! def randomInts(min: Int, max: Int): Unit / {emit[Int], random} = while (true) do emit(randomInt(min, max)) -def randomIntWidth(bits: Int): Int / random = { - def divCeil(n: Int, m: Int) = (n + m - 1) / m - - val max = 1.bitwiseShl(bits) - - val bytesNeeded = bits.divCeil(4) - var result = 0 - repeat(bytesNeeded) { - val b = do random() - result = result.bitwiseShl(8).bitwiseOr(b.toInt) - } - - abs(result).mod(max) -} /// Random double between 0.0 and 1.0 def randomDouble(): Double / random = (randomInt32().toDouble / 1.bitwiseShl(31).toDouble).abs // This is not perfect, but it will do for now. +def randomDoubles(): Unit / {emit[Double], random} = + while (true) do emit(randomDouble()) + namespace examples { def main() = { From dccf51fcde4390b718a6c744035177dd17c10091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 14:28:40 +0200 Subject: [PATCH 08/12] Fix 'round' on Chez --- libraries/common/effekt.effekt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/common/effekt.effekt b/libraries/common/effekt.effekt index 67d0391df..e34c6804c 100644 --- a/libraries/common/effekt.effekt +++ b/libraries/common/effekt.effekt @@ -449,7 +449,7 @@ extern pure def toDouble(d: Int): Double = extern pure def round(d: Double): Int = js "Math.round(${d})" - chez "(round ${d})" + chez "(flonum->fixnum (round ${d}))" llvm """ %i = call %Double @llvm.round.f64(double ${d}) %z = fptosi double %i to %Int ret %Int %z From e97cd3f4e3a94a4d96cff158b6e2eb94f469905a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 14:29:56 +0200 Subject: [PATCH 09/12] Regenerate tests (oops) --- examples/stdlib/random.check | 91 ++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/examples/stdlib/random.check b/examples/stdlib/random.check index 8a39272c1..5ed46cb56 100644 --- a/examples/stdlib/random.check +++ b/examples/stdlib/random.check @@ -1,43 +1,64 @@ int32s: --675038595 --1211581979 --903265628 +-1472445053 +-935901669 +-1244218020 492812375 --1252744925 +-894738723 1372722888 --424032689 +-1723450959 2005696606 536774910 2111603542 +int32s, part2: +348632114 +-493311473 +521105902 +-441336655 +1315564179 +-245050234 +1432006216 +-2018660684 +349983049 +-1541851413 +1242068606 +-953174617 +728164170 +-558026150 +812040776 +-225070679 +125608749 +-1547184487 +2026319992 +-627925429 doubles: -0.8376555209979415 -0.22971605509519577 -0.24265884514898062 -0.2055133944377303 -0.3873926908709109 -0.8858895925804973 -0.6668298579752445 -0.05998786725103855 -0.8370264428667724 -0.28201948618516326 +0.009 +0.758 +0.769 +0.032 +0.15 +0.118 +0.03 +0.946 +0.049 +0.565 randomInt: -4 8 8 -488 -6 9 7 -697 -1 8 1 -181 -2 1 4 -214 -4 9 0 -490 -0 6 2 -62 -4 2 4 -424 -9 7 9 -979 -7 4 3 -743 -5 3 9 -539 +3 4 3 +343 +0 6 8 +68 +2 3 0 +230 +6 1 0 +610 +4 0 1 +401 +2 3 4 +234 +9 3 2 +932 +8 2 2 +822 +3 3 2 +332 +8 5 1 +851 From bcdcf2878c556c0a4ce915d14201232d1741c3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 14:50:02 +0200 Subject: [PATCH 10/12] Improve doc comments --- libraries/common/random.effekt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt index 50b15ed29..dd75a81f1 100644 --- a/libraries/common/random.effekt +++ b/libraries/common/random.effekt @@ -9,15 +9,17 @@ effect random(): Byte // --------------------- // Sources of randomness -/// A streaming source (push stream) of byte-level randomness. +/// A streaming source (push stream) of byte-level randomness +/// based on Park and Miller's MINSTD with revised parameters. +/// /// Deterministic: needs a 32bit `seed` -- you can use `bench::timestamp`. def minstd(seed: Int): Unit / emit[Byte] = { // Initialize state with seed, ensuring it's not zero var state = if (seed == 0) 1 else seed def nextInt(): Int = { - // Park-Miller minimal standard PRNG // Uses only at most 32-bit integers internally + // (Schrage's method: https://en.wikipedia.org/wiki/Lehmer_random_number_generator#Schrage's_method) val a = 48271 val m = 2147483647 @@ -43,9 +45,10 @@ def minstd(seed: Int): Unit / emit[Byte] = { } /// A thin wrapper over `minstd`, handling a reader of random bytes. +/// /// Deterministic: needs a 32bit `seed` -- you can use `bench::timestamp`. /// -/// Implementation is something like `stream::source`, specialized for bytes and the `random` effect. +/// Implementation is similar to `stream::source`, specialized for bytes and the `random` effect. def minstd(seed: Int) { randomnessReader: () => Unit / random }: Unit = { var next = box { 255.toByte } // sentinel value next = box { From 9058eca0f4e5c12ef1c2faf649e32215c323b73c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 15:33:45 +0200 Subject: [PATCH 11/12] Address review comments --- examples/stdlib/random.check | 3 +++ libraries/common/random.effekt | 30 +++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/examples/stdlib/random.check b/examples/stdlib/random.check index 5ed46cb56..66528de96 100644 --- a/examples/stdlib/random.check +++ b/examples/stdlib/random.check @@ -1,3 +1,4 @@ +prng int32s: -1472445053 -935901669 @@ -62,3 +63,5 @@ randomInt: 332 8 5 1 851 +unix +true diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt index dd75a81f1..f566460d7 100644 --- a/libraries/common/random.effekt +++ b/libraries/common/random.effekt @@ -68,12 +68,15 @@ def minstd(seed: Int) { randomnessReader: () => Unit / random }: Unit = { /// CSPRNG from `/dev/urandom`, handling a reader of random bytes. /// Only works on Unix-like OSes! -def devurandom { randomnessReader: () => Unit / random }: Unit / {Exception[IOError], stop} = { - with readFile("/dev/urandom") - try randomnessReader() with random { - resume(do read[Byte]()) +def devurandom { randomnessReader: () => Unit / random }: Unit / Exception[IOError] = + try { + with readFile("/dev/urandom") + try randomnessReader() with random { + resume(do read[Byte]()) + } + } with stop { + do raise(io::error::EOF(), "Unexpected EOF when reading /dev/urandom!") } -} // ------------------------ // Functions using `random` @@ -137,6 +140,14 @@ def randomDoubles(): Unit / {emit[Double], random} = namespace examples { def main() = { + println("prng") + prngRandom() + + println("unix") + unixRandom() + } + + def prngRandom(): Unit = { with minstd(1337); println("int32s:") @@ -166,12 +177,13 @@ def main() = { } def unixRandom(): Unit = { - with boundary; with on[IOError].report; with devurandom; - repeat(2) { - println(randomInt32()) - } + val a = randomInt32() + val b = randomInt32() + + // This is just to use the generated numbers :) + println((a.show ++ b.show).length != 0) } } \ No newline at end of file From d58e39fc445273915bde124e07dedce6ca98f485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Tue, 29 Apr 2025 15:39:51 +0200 Subject: [PATCH 12/12] Don't run /dev/urandom in tests --- examples/stdlib/random.check | 2 -- libraries/common/random.effekt | 3 --- 2 files changed, 5 deletions(-) diff --git a/examples/stdlib/random.check b/examples/stdlib/random.check index 66528de96..cd10a502c 100644 --- a/examples/stdlib/random.check +++ b/examples/stdlib/random.check @@ -63,5 +63,3 @@ randomInt: 332 8 5 1 851 -unix -true diff --git a/libraries/common/random.effekt b/libraries/common/random.effekt index f566460d7..b1998aec7 100644 --- a/libraries/common/random.effekt +++ b/libraries/common/random.effekt @@ -142,9 +142,6 @@ namespace examples { def main() = { println("prng") prngRandom() - - println("unix") - unixRandom() } def prngRandom(): Unit = {