I have a situation where I need to do this:
const f = (obj) => assoc('list', createList(obj), obj)
Due to the fact that I need the argument for the second and the third parameter, prohibits me from doing something like:
const f = assoc('list', somehowGetObj())
I also tried this, but that didn't work:
const f = assoc('list', createList(__))
const f = converge(assoc, [createList, identity])
Is there a proper way to do this by currying?
Another option is
chain(createList, assoc('list'))
which you can see in action on the Ramda REPL.
Update
For further explanation of how this works, I'll use the variation which will work with the next release of Ramda:
chain(assoc('list'), createList)
to show how it matches the current signature:
chain :: Chain m => (a -> m b) -> m a -> m b
Ramda treats functions as FantasyLand Monads, and therefore thus also as Chains. So to specialize the above to functions, we have
chain :: (a -> Function x b) -> Function x a -> Function x -> b
but Function x y can be written more simply as x -> y, so the above can written more simply as
chain :: (a -> x -> b) -> (x -> a) -> (x -> b)
Then you can use these (specialized) types:
createList :: OriginalData -> YourList (x -> a)
assoc :: String -> YourList -> OriginalData -> EnhancedData
assoc('list') :: YourList -> OriginalData -> EnhancedData (a -> x -> b)
and hence
chain(assoc('list'), createList) :: OriginalData -> EnhancedData (x -> b)
const f = converge(assoc('list'), [createList, identity])
Related
I've kind of grasped some knowledge on functional programming but can't really wrap my head around this function programming block of code. I didn't really knew where I should ask something like this I couldn't figure out so asked it here. So I would really appreciate if someone will help me understand what this higher order function, or a monads example doing?
P.S this code is from composing software book by Eric Elliot
const f = n => n+1;
const g = n => n*2;
This composeM functions is made to compose and map or over numbers of functions? I know reduce but really have no idea how this function should be working.
const composeM = (...mps) => mps.reduce((f, g) => x => g(x).map(f));
const h = composeM(f,g);
h(20)
Then, the function composeM was more generalized by doing:
const compose = methods => (...mps) => mps.reduce((f, g) => x => g(x)[method](f));
Then, I could create composedPromises or composedMaps like
const composePromises = compose("then")(f,g);
How is the g(x)[method](f) even working? it should be g(x).then(f).
Update above map composeM function not working
const f = n => Promise.resolve( n+1 );
const g = n => Promise.resolve( n*2 );
const composePromises = (...mps) => mps.reduce((f, g) => x => g(x).then(f))
const h = composePromises(f, g)
h(20)
Consider function composition, which has the following type signature.
// compose :: (b -> c) -- The 1st argument is a function from b to c.
// -> (a -> b) -- The 2nd argument is a function from a to b.
// -> (a -> c) -- The final result is a function from a to c.
// +-----------------b -> c
// | +---------a -> b
// | | +-- a
// | | |
const compose = (f, g) => x => f(g(x));
// |_____|
// |
// c
The composeP function is similar to the compose function, except it composes functions that return promises.
// composeP :: (b -> Promise c) -- The 1st argument is a function from b to promise of c.
// -> (a -> Promise b) -- The 2nd argument is a function from a to promise of b.
// -> (a -> Promise c) -- The final result is a function from a to promise of c.
// +-------------------------b -> Promise c
// | +-------- a -> Promise b
// | | +-- a
// | | |
const composeP = (f, g) => x => g(x).then(f);
// |__________|
// |
// Promise c
Remember that the then method applies the callback function to the value of the promise. If we replace .then with [method] where method is the name of the bind function of a specific monad, then we can compose functions that produces values of that monad.
For example, .flatMap is the bind function of arrays. Hence, we can compose functions that return arrays as follows.
// composeA :: (b -> Array c) -- The 1st argument is a function from b to array of c.
// -> (a -> Array b) -- The 2nd argument is a function from a to array of b.
// -> (a -> Array c) -- The final result is a function from a to array of c.
const composeA = (f, g) => x => g(x).flatMap(f);
// f :: Bool -> Array String
const f = x => x ? ["yes"] : ["no"];
// g :: Int -> Array String
const g = n => [n <= 0, n >= 0];
// h :: Int -> Array String
const h = composeA(f, g);
console.log(h(-1)); // ["yes", "no"]
console.log(h(0)); // ["yes", "yes"]
console.log(h(1)); // ["no", "yes"]
That was a very contrived example but it got the point across.
Anyway, the generic compose function composes monads.
const compose = method => (f, g) => x => g(x)[method](f);
const composeP = compose("then");
const composeA = compose("flatMap");
Finally, the various monad compose functions only compose two functions at a time. We can use reduce to compose several of them at once. Hope that helps.
Is the following attempt at the Hindley-Milner Type Signature for the compose function correct?
// compose :: (f -> [f]) -> (f -> f -> f) -> [f] -> f
const compose = (...fns) => fns.reduce((f,g) => (...args) => f(g(...args)));
No, that's not correct. Your compose function takes an array of functions as input, and produces one (composed) function as the output, so that signature is clearly wrong. On the other hand, I don't think it's possible to write that function using the Hindley-Milner type system unless you make the assumption that all of the functions in fns are unary functions of the same type: a -> a (i.e. an array of endomorphisms).
compose :: [a -> a] -> (a -> a)
JavaScript is dynamically typed, so it actually allows each function in fns to be a different type (JS doesn't require arrays to be homogeneous). This means that you may need to invent some new syntax in order to express the type of compose as you have it. Here's how Ramda (a functional utility library for JS) describes the type of R.compose:
((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z)
It uses this … syntax in order to represent a variadic function. The right-most function in the parameter list is of type ((a, b, …, n) → o), meaning that it is a variadic function that returns an o. This o is then used as the input of the next function which is of type (o → p). This continues down the list of parameters until the left-most function is of type (y → z), where z becomes the resultant type of calling the returned function: ((a, b, …, n) → z).
The type of binary function composition is much easier to express in Hindley–Milner notation:
// compose :: (b -> c) -> (a -> b) -> a -> c
const compose = f => g => x => f (g (x));
I'm trying to figure out if there is a pattern for writing pointfree composed function when the arguments should be spreaded in curried composing functions
i.e (with Ramda):
add_1_and_multiply = (add, mul) => R.compose(R.multiply(mul), R.add(1))(add)
add_1_and_multiply(3, 5) // 20
How to write add_1_and_multiply in pointfree style?
I'm not sure if you can easily combine pointfree style and non-unary arity.
Think first what should be the type of the resulting and composed functions:
// Compose: (B -> C) -> (A -> B) -> A -> C
const compose = f => g => x => f(g(x))
// Add: A -> A -> A
const add = x => y => x + y
// Mul: A -> A -> A
const mul = x => y => x * y
// Add1: A -> A
const add1 = add(1)
// Add1AndMul: A -> A -> A
// because:
// Add1: A -> A
// Mul: A -> A -> A
const add_1_and_mul = compose(mul)(add1)
// Mul4: A -> A
const mul_4 = add_1_and_mul(3)
const result = mul_4(5) //> 20
Ramda has uncurryN so you can wrap it around the compose and get rid of the currying of the resulting function.
const add_1_and_multiply = R.uncurryN(2, R.compose(R.multiply, R.add(1)))
let result2 = add_1_and_multiply(3, 5) //> 20
To add another function to the "chain" you need to compose it with previous function.
// Add1AndMul: A -> A -> A
const add1_mul = compose(mul)(add1)
This is our desired signature.
// 1 2 3
// Add1AndMulAndAdd: A -> A -> A -> A
// which is: | | |
// Add1: A -> A | |
// Mul: A -> A -> A |
// Add: A -> A -> A
So somehow we have to pass those A2 and A3 without any "points".
Let's try just simple composition and analyze it:
let add1_mul_add = compose(add)(add1_mul)
Remeber signature of compose: (E -> F) -> (D -> E) -> D -> F!
Analyzing it in steps:
We supply our add function signature instead of (E -> F)
(E -> F )
(A -> A -> A)
We conclude that
E = A
F = A -> A
We do the same to (D -> E) and add1_mul
(D -> E )
(A -> A -> A)
We conclude that
D = A
E = A -> A
But we can already see a contradiction there!
Conclusion in step 2 contradicts conclusion in step 1:
E cannot be A and A -> A at the same time.
Therefore we cannot compose add and add1_mul and our add1_mul_add will throw an error.
Let's try to workaround the problem and fix it breaking our promise of pointfree style.
const add1_mul_add = x => compose(add)(add1_mul(x))
I'm going to break some rules and mix signatures with code to illustrate my point:
x -> (A -> A -> A) -> (x -> A -> A) -> A -> A -> A
||
\/
x -> (A -> A -> A) -> (A -> A) -> A -> A -> A
(E -> F ) -> (D -> E) -> D -> F
So we got our correct compose signature! How to get rid of the x variable to go back to pointfree?
We can try to look for obvious patterns, like for example... our ye olde function composition!
f(g(x)) => compose(f)(g)
And we find this pattern in our new add1_mul_add -
f = compose(add)
g = add1_mul
f(g(x)) = compose(add)(add1_mul(x))
And we reduce it to pointfree and we got our new add1_mul_add function:
const add1_mul_add = compose(compose(add))(add1_mul)
But hey - we can reduce it even more!
const add1_mul_add = compose(compose)(compose)(add)(add1_mul)
And there we have found something that already exists in haskell under the name of The Owl.
We can define it in Javascript simply as:
const owl = compose(compose)(compose)
But now, with every new function in the chain, you would have to create a higher order of the owl operator.
const owl2 = compose(compose)(owl)
const add1_mul_add_mul = owl2(mul)(add1_mul_add)
const owl3 = compose(compose)(owl2)
const add1_mul_add_mul_add = owl3(add)(add1_mul_add_mul)
So I really recommend having your functions unary in pointfree style. Or use other constructs like lists:
const actions = [ add, mul, add, mul ]
const values = [ 1, 2, 3, 4 ]
const add_mul_add_mul = (...values) => zip(actions, values).reduce((acc, [action, value]) => action(acc, value), 0)
Below is a syntactically valid javascript program – only, it doesn't behave quite the way we're expecting. The title of the question should help your eyes zoom to The Problem Area
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const repeat = n => f => x =>
loop ((n = n, f = f, x = x) => // The Problem Area
n === 0
? x
: recur (n - 1, f, f (x)))
console.time ('loop/recur')
console.log (repeat (1e6) (x => x + 1) (0))
console.timeEnd ('loop/recur')
// Error: Uncaught ReferenceError: n is not defined
If instead I use unique identifiers, the program works perfectly
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const repeat = $n => $f => $x =>
loop ((n = $n, f = $f, x = $x) =>
n === 0
? x
: recur (n - 1, f, f (x)))
console.time ('loop/recur')
console.log (repeat (1e6) (x => x + 1) (0)) // 1000000
console.timeEnd ('loop/recur') // 24 ms
Only this doesn't make sense. Let's talk about the original code that doesn't use the $-prefixes now.
When the lambda for loop is being evaluated, n as received by repeat, is available in the lambda's environment. Setting the inner n to the outer n's value should effectively shadow the outer n. But instead, JavaScript sees this as some kind of problem and the inner n results in an assignment of undefined.
This seems like a bug to me but I suck at reading the spec so I'm unsure.
Is this a bug?
I guess you already figured out why your code doesn't work. Default arguments behave like recursive let bindings. Hence, when you write n = n you're assigning the newly declared (but yet undefined) variable n to itself. Personally, I think this makes perfect sense.
So, you mentioned Racket in your comments and remarked on how Racket allows programmers to choose between let and letrec. I like to compare these bindings to the Chomsky hierarchy. The let binding is akin to regular languages. It isn't very powerful but allows variable shadowing. The letrec binding is akin to recursively enumerable languages. It can do everything but doesn't allow variable shadowing.
Since letrec can do everything that let can do, you don't really need let at all. A prime example of this is Haskell which only has recursive let bindings (unfortunately called let instead of letrec). The question now arises as to whether languages like Haskell should also have let bindings. To answer this question, let's look at the following example:
-- Inserts value into slot1 or slot2
insert :: (Bool, Bool, Bool) -> (Bool, Bool, Bool)
insert (slot1, slot2, value) =
let (slot1', value') = (slot1 || value, slot1 && value)
(slot2', value'') = (slot2 || value', slot2 && value')
in (slot1', slot2', value'')
If let in Haskell wasn't recursive then we could write this code as:
-- Inserts value into slot1 or slot2
insert :: (Bool, Bool, Bool) -> (Bool, Bool, Bool)
insert (slot1, slot2, value) =
let (slot1, value) = (slot1 || value, slot1 && value)
(slot2, value) = (slot2 || value, slot2 && value)
in (slot1, slot2, value)
So why doesn't Haskell have non-recursive let bindings? Well, there's definitely some merit to using distinct names. As a compiler writer, I notice that this style of programming is similar to the single static assignment form in which every variable name is used exactly once. By using a variable name only once, the program becomes easier for a compiler to analyze.
I think this applies to humans as well. Using distinct names helps people reading your code to understand it. For a person writing the code it might be more desirable to reuse existing names. However, for a person reading the code using distinct names prevents any confusion that might arise due to everything looking the same. In fact, Douglas Crockford (oft-touted JavaScript guru) advocates context coloring to solve a similar problem.
Anyway, back to the question at hand. There are two possible ways that I can think of to solve your immediate problem. The first solution is to simply use different names, which is what you did. The second solution is to emulate non-recursive let expressions. Note that in Racket, let is just a macro which expands to a left-left-lambda expression. For example, consider the following code:
(let ([x 5])
(* x x))
This let expression would be macro expanded to the following left-left-lambda expression:
((lambda (x) (* x x)) 5)
In fact, we can do the same thing in Haskell using the reverse application operator (&):
import Data.Function ((&))
-- Inserts value into slot1 or slot2
insert :: (Bool, Bool, Bool) -> (Bool, Bool, Bool)
insert (slot1, slot2, value) =
(slot1 || value, slot1 && value) & \(slot1, value) ->
(slot2 || value, slot2 && value) & \(slot2, value) ->
(slot1, slot2, value)
In the same spirit, we can solve your problem by manually "macro expanding" the let expression:
const recur = (...args) => ({ type: recur, args });
const loop = (args, f) => {
let acc = f(...args);
while (acc.type === recur)
acc = f(...acc.args);
return acc;
};
const repeat = n => f => x =>
loop([n, f, x], (n, f, x) =>
n === 0 ? x : recur (n - 1, f, f(x)));
console.time('loop/recur');
console.log(repeat(1e6)(x => x + 1)(0)); // 1000000
console.timeEnd('loop/recur');
Here, instead of using default parameters for the initial loop state I'm passing them directly to loop instead. You can think of loop as the (&) operator in Haskell which also does recursion. In fact, this code can be directly transliterated into Haskell:
import Prelude hiding (repeat)
data Recur r a = Recur r | Return a
loop :: r -> (r -> Recur r a) -> a
loop r f = case f r of
Recur r -> loop r f
Return a -> a
repeat :: Int -> (a -> a) -> a -> a
repeat n f x = loop (n, f, x) (\(n, f, x) ->
if n == 0 then Return x else Recur (n - 1, f, f x))
main :: IO ()
main = print $ repeat 1000000 (+1) 0
As you can see you don't really need let at all. Everything that can be done by let can also be done by letrec and if you really want variable shadowing then you can just manually perform the macro expansion. In Haskell, you could even go one step further and make your code prettier using The Mother of all Monads.
Given the following:
var average = R.lift(R.divide)(R.sum, R.length)
How come this works as a pointfree implementation of average? I don't understand why I can pass R.sum and R.length when they are functions and therefore, I cannot map the lifted R.divide over the functions R.sum and R.length unlike in the following example:
var sum3 = R.curry(function(a, b, c) {return a + b + c;});
R.lift(sum3)(xs)(ys)(zs)
In the above case the values in xs, ys and zs are summed in a non deterministic context, in which case the lifted function is applied to the values in the given computational context.
Expounding further, I understand that applying a lifted function is like using R.ap consecutively to each argument. Both lines evaluate to the same output:
R.ap(R.ap(R.ap([tern], [1, 2, 3]), [2, 4, 6]), [3, 6, 8])
R.lift(tern)([1, 2, 3], [2, 4, 6], [3, 6, 8])
Checking the documentation it says:
"lifts" a function of arity > 1 so that it may "map over" a list, Function or other object that satisfies the FantasyLand Apply spec.
And that doesn't seem like a very useful description at least for me. I'm trying to build an intuition regarding the usage of lift. I hope someone can provide that.
The first cool thing is that a -> b can support map. Yes, functions are functors!
Let's consider the type of map:
map :: Functor f => (b -> c) -> f b -> f c
Let's replace Functor f => f with Array to give us a concrete type:
map :: (b -> c) -> Array b -> Array c
Let's replace Functor f => f with Maybe this time:
map :: (b -> c) -> Maybe b -> Maybe c
The correlation is clear. Let's replace Functor f => f with Either a, to test a binary type:
map :: (b -> c) -> Either a b -> Either a c
We often represent the type of a function from a to b as a -> b, but that's really just sugar for Function a b. Let's use the long form and replace Either in the signature above with Function:
map :: (b -> c) -> Function a b -> Function a c
So, mapping over a function gives us a function which will apply the b -> c function to the original function's return value. We could rewrite the signature using the a -> b sugar:
map :: (b -> c) -> (a -> b) -> (a -> c)
Notice anything? What is the type of compose?
compose :: (b -> c) -> (a -> b) -> a -> c
So compose is just map specialized to the Function type!
The second cool thing is that a -> b can support ap. Functions are also applicative functors! These are known as Applys in the Fantasy Land spec.
Let's consider the type of ap:
ap :: Apply f => f (b -> c) -> f b -> f c
Let's replace Apply f => f with Array:
ap :: Array (b -> c) -> Array b -> Array c
Now, with Either a:
ap :: Either a (b -> c) -> Either a b -> Either a c
Now, with Function a:
ap :: Function a (b -> c) -> Function a b -> Function a c
What is Function a (b -> c)? It's a bit confusing because we're mixing the two styles, but it's a function that takes a value of type a and returns a function from b to c. Let's rewrite using the a -> b style:
ap :: (a -> b -> c) -> (a -> b) -> (a -> c)
Any type which supports map and ap can be "lifted". Let's take a look at lift2:
lift2 :: Apply f => (b -> c -> d) -> f b -> f c -> f d
Remember that Function a satisfies the requirements of Apply, so we can replace Apply f => f with Function a:
lift2 :: (b -> c -> d) -> Function a b -> Function a c -> Function a d
Which is more clearly written:
lift2 :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)
Let's revisit your initial expression:
// average :: Number -> Number
const average = lift2(divide, sum, length);
What does average([6, 7, 8]) do? The a ([6, 7, 8]) is given to the a -> b function (sum), producing a b (21). The a is also given to the a -> c function (length), producing a c (3). Now that we have a b and a c we can feed them to the b -> c -> d function (divide) to produce a d (7), which is the final result.
So, because the Function type can support map and ap, we get converge at no cost (via lift, lift2, and lift3). I'd actually like to remove converge from Ramda as it isn't necessary.
Note that I intentionally avoided using R.lift in this answer. It has a meaningless type signature and complex implementation due to the decision to support functions of any arity. Sanctuary's arity-specific lifting functions, on the other hand, have clear type signatures and trivial implementations.
As I have hard time understanding the same issue, I decided to take a peek from Ramda's source code. Will write a blogpost about this in the future. Meanwhile—I made a commented gist how Ramda's lift work step by step.
from: https://gist.github.com/philipyoungg/a0ab1efff1a9a4e486802a8fb0145d9e
// Let's make an example function that takes an object and return itself.
// 1. Ramda's lift level
lift(zipObj)(keys, values)({a: 1}) // returns {a: 1}
// this is how lift works in the background
module.exports = _curry2(function liftN(arity, fn) {
var lifted = curryN(arity, fn);
return curryN(arity, function() {
return _reduce(ap, map(lifted, arguments[0]), Array.prototype.slice.call(arguments, 1)); // found it. let's convert no 1 to no 2
});
});
// 2. Ramda's reduce level
reduce(ap, map(zipObj, keys))([values])
// first argument is the function, second argument is initial value, and the last one is lists of arguments. If you don't understand how reduce works, there's a plenty of resources on the internet
// 3. Ramda's ap level
ap(map(zipObj, keys), values)
// how ap works in the background
module.exports = _curry2(function ap(applicative, fn) {
return (
typeof applicative.ap === 'function' ?
applicative.ap(fn) :
typeof applicative === 'function' ? //
function(x) { return applicative(x)(fn(x)); } : // because the first argument is a function, ap return this.
// else
_reduce(function(acc, f) { return _concat(acc, map(f, fn)); }, [], applicative)
);
});
// 4. Voilà. Here's the final result.
map(zipObj, keys)({a: 1})(values({a: 1}))
// Hope it helps you and everyone else!