Consider the working code below:
var randN = x => () => Math.floor(x*Math.random());
var rand10 = randN(10)
times(rand10, 10) // => [6, 3, 7, 0, 9, 1, 7, 2, 6, 0]
randN
is a function that takes a number and returns an RNG that, when called, will return a random int in the range [0, N-1]. So it's a factory for specific RNGs.
I've been using ramda.js, and learning functional programming theory, and my question is: Is it possible to rewrite randN
in a point free style using ramda?
For example, I could write:
var badAttempt = pipe(multiply(Math.random()), Math.floor)
This would satisfy the "point-free style" requirement, but fails to behave the same way as randN
: calling badAttempt(10)
simply returns a single random number between 1 and 10, rather than a function that generates a random number between 1 and 10 when called.
I have not been able to find a combination of ramda functions that enables me to do the rewrite in a point-free style. I can't tell if this is just a failure on my part, or something special about using random
, which breaks referential transparency and therefore may be incompatible with a point free style.
my own slight variation on the solution, after discussing it with Denys:
randN = pipe(always, of, append(Math.random), useWith(pipe(multiply, Math.floor)), partial(__,[1,1]))
This would help with an extra function for abstracting a function to re-evaluate its arguments each time it is called.
thunk = fn => R.curryN(fn.length, (...args) => () => fn(...args))
The only purpose of this function would be to cause some side effect within the given fn
function.
Once we have thunk
function, we can define randN
like so:
randN = thunk(R.pipe(S.S(R.multiply, Math.random), Math.floor))
R.times(randN(10), 5) // e.g. [1, 6, 9, 4, 5]
Note: S.S
here is the S combinator from Sanctuary which does effectively the same thing as R.converge(multiply, [Math.random, identity])
.
I do however only recommend going with a point-free solution if it actually improves the readability of a function.
I don't know if it's a good idea to learn functional programming using a specific library, because the characteristics of a lib and the functional paradigm will mix inevitably. In practice, however, Ramda is incredibly useful. It bridges the gap between the imperative reality and the functional Fantasy Land in Javascript :D
Here's a manual approach:
// a few generic, reusable functions:
const comp = f => g => x => f(g(x)); // mathematical function composition
const comp2 = comp(comp)(comp); // composes binary functions
const flip = f => x => y => f(y)(x); // flips arguments
const mul = y => x => x * y; // first class operator function
// the actual point-free function:
const randN = comp2(Math.floor)(flip(comp(mul)(Math.random)));
let rand10 = randN(10); // RNG
for (let i = 0; i < 10; i++) console.log(rand10());
It's worth mentioning that randN
is impure, since random numbers are impure by definition.
var randN = R.converge(R.partial, [R.wrap(R.pipe(R.converge(R.multiply, [Math.random, R.identity]), Math.floor), R.identity), R.of])
var rand10 = randN(10)
alert(R.times(rand10, 10)) // => [3, 1, 7, 5, 7, 5, 8, 4, 7, 2]
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.19.1/ramda.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With