Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a parameterless function in Ramda in a point free style?

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.

update

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]))
like image 737
Jonah Avatar asked Mar 23 '16 20:03

Jonah


3 Answers

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.

like image 77
Scott Christopher Avatar answered Nov 20 '22 05:11

Scott Christopher


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.

like image 23
Iven Marquardt Avatar answered Nov 20 '22 06:11

Iven Marquardt


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>
like image 1
Denys Mikhalenko Avatar answered Nov 20 '22 06:11

Denys Mikhalenko