Making RANDOM Less Random 🤔

Historical RANDOM in Rebol is a hodgepodge, which Red carried forward:

red>> help random
USAGE:
     RANDOM value

DESCRIPTION: 
     Returns a random value of the same datatype; or shuffles series. 
     RANDOM is an action! value.

ARGUMENTS:
     value         "Maximum value of result (modified when series)."

REFINEMENTS:
     /seed        => Restart or randomize.
     /secure      => Returns a cryptographically secure random number.
     /only        => Pick a random value from a series.

RETURNS:
     [any-type!]

What the function does can vary significantly based on the refinements and argument:

>> random 10
== 5  ; just picks an integer from 1 to 10 inclusive

>> block: [a b c]

>> random block
== [c b a]  ; e.g. default behavior for block is SHUFFLE

>> block
== [c b a]  ; and it's a mutating shuffle.

>> random/only block
== b  ; so /only makes it pick an element out of blocks

>> random/seed [a b c] 
; no return value, just makes the next values deterministic

That's just crazy. :zany_face: Not only is it hard to predict what it's going to do, but the type specs don't tell you what the return values are.

As part of transitioning RANDOM to "New Generics" I decided to go ahead and split this up.

SHUFFLE, RANDOM, RANDOMIZE, RANDOM-PICK

  • RANDOM:SEED => RANDOMIZE
  • RANDOM:ONLY => RANDOM-PICK
  • RANDOM => if it shuffled a series, SHUFFLE, else RANDOM

Then I've added another routine SHUFFLE-OF which works on immutable types, making a new shuffled copy. And the way it works is that if you use SHUFFLE-OF on a type that doesn't have a tailored implementation of it, then it falls back and tries to do it as SHUFFLE COPY.

>> path: 'a/b/c/d

>> shuffle path
** Error: PATH! is immutable

>> shuffle of path
== c/d/a/b  ; uses the SHUFFLE-OF registered for ANY-SEQUENCE?

>> block: [a b c d]

>> shuffle of block
== [b d c a]  ; no SHUFFLE-OF for ANY-LIST?, falls back on SHUFFLE COPY

>> block
== [a b c d]

I also thought it would be useful to have RANDOM-BETWEEN, because then you can say what the min and max values are without needing to do math on a 1-based RANDOM result:

>> random-between 5 10
== 7

Further Directions: RANDOM Dialect?

In this formulation so far, RANDOM on a BLOCK! has no meaning.

That made me wonder about whether RANDOM should be dialected when you pass it a block:

random [between 10 and 20]

random [between 10 and 20 distribution 'normal]

random [weighted [["A" 0.7] ["B" 0.2] ["C" 0.1]]]

Dialects of this kind have proven very difficult to design, and we've only got a few of them. Once you move things out of the domain of simple functions you make them harder to specialize/adapt/cascade, and you wind up doing work that the evaluator takes care of for you with a function.

But I do think that looks nice, and is something to think about for the future.

3 Likes