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)
Related
Heads-up: This is a cross-language question.
I will demonstrate the problem by means of implementing a difference list. Here is the Scott encoded List, which provides the basic type. I use it with a dynamic type validator, hence I need a wrapper to associate the type List a with (simplified in the example below):
// (forall r. r -> (a -> List a -> r) -> r) -> List a
const List = k =>
({[Symbol.toStringTag]: "List", run: k}); // type wrapper + namespace
// a -> List a -> List a
List.Cons = x => xs =>
List(nil => cons => cons(x) (xs));
// List a
List.Nil = List(nil => cons => nil);
// List a -> List a -> List a
List.append = xs => ys => function go(acc) {
return acc.run(ys)
(x => xs_ =>
List.Cons(x) (thunk(() => go(xs_)))); // A
} (xs);
// List a
List.empty = List.Nil;
The expression thunk(() => ...) in line A creates an implicit thunk, i.e. (except for ===) you can treat it as the expression the thunk is deferring. In this case it has type Last a.
This is pretty much standard in a eager language without ADT. Next I want to offer efficient append and snoc operations supplied by a difference list. At this point things are getting messy. In Haskell such a type is declared with a newtype wrapper, but I don't know how to implement it using Scott encoding. So I stick with the normal encoding:
// (forall r. ((List a -> List a) -> r) -> r) -> DList a
const DList = k =>
({[Symbol.toStringTag]: "DList", run: k}); // type wrapper + namespace
// (List a -> List a) -> DList a
DList.Cons = fun(
f => DList(cons => cons(f));
// DList<a> => DList<a> => DList<a>
DList.append = f => g => DList.Cons(
xs => f.run(
cons => cons(g.run( // B
cons_ => cons_(xs))))); // B
// DList a
DList.empty = DList.Cons(
xs => List.append(List.Nil) (xs));
Well, this works but the implementation of such an easy thing as the monoid instance is rather entangled. Having to pattern match (cons(...) and cons_(...) in line B) to get the partially applied List.append (List a -> List a) is redundant. and unsecessarily complicating.
Maybe it is as simple as dropping the Scott encoding altogether, but I don't want to lose the type abstraction from List a -> List a to DList a on the type level. Hopefully someone with more experience can point the right way.
Answers both using Haskell or JS are appreciated.
We can simplify the implementation of DList.append and DList.empty as follows.
const comp = f => g => x => f(g(x));
const id = x => x;
DList.append = xs => ys =>
xs.run(f =>
ys.run(g =>
DList.Cons(comp(f)(g))));
DList.empty = DList.Cons(id);
However, it would be even simpler if we didn't use CPS at all.
// (List a -> List a) -> DList a
const DList = run => ({ [Symbol.toStringTag]: "DList", run });
DList.append = xs => ys => DList(comp(xs.run)(ys.run));
DList.empty = DList(id);
I’ve been trying to implement an Apply/Applicative type based on Javascript Functor, Applicative, Monads in pictures and the blog series Fantas, Eel, and Specification.
I think I’m making good progress, but I ran in to a case I couldn’t really find in any of the articles.
TL;DR Question
If lift2 is
f -> A.of(x) -> A.of(y) -> A.of(f (x) (y)) -or- A(y).ap(A(x).map(f))
what is the theory/name/type behind
A.of(f) -> A.of(x) -> A.of(y) -> A(f (x) (y)) -or- A(y).ap(A(x).ap(A(f)))
Introduction
I’m working with knockout.js, which gives me observable values. I’m trying to use them in a kind of functional fashion, extending them when needed.
First, I implemented map to make myself a functor:
ko.subscribable.fn.map = function(f) {
// note: calling without arguments is knockout's
// way of "unwrapping"
return ko.pureComputed(
() => f(this())
);
}
This allows me to do things like:
// "Pure" part describing my app
const myValue = ko.observable(2);
const doubleThat = myValue.map(x => x * 2);
// In- and output (IO?)
doubleThat.subscribe(console.log);
myValue(3); // Logs 6
Then, I ran in to the problem of working with functions that take multiple arguments. For example:
const filter = (pred, xs) => xs.filter(pred);
I solved my issues by implementing ap and currying my functions:
ko.subscribable.fn.ap = function(sf) {
return ko.pureComputed(
() => sf () (this())
);
};
const filter = pred => xs => xs.filter(pred);
With these changes in place, I can do:
const odd = x => x % 2 === 1;
const myPred = ko.observable(odd);
const myValues = ko.observable([ 1, 2, 3 ]);
const myFilter = myPred.map(filter);
const myResult = myValues.ap(filter); // S([ 1, 3 ])
The definition of lift2 gives me another way of writing the same thing.
const myResult = lift2 (filter) (myPred) (myResult)
So far, so good. I can use the dot-calls if the interim result is reusable, and a liftN call if I only care about the final outcome.
The problem
The liftN way of chaining one map with N - 1 calls to ap only works if I use plain functions. In my app however, I often deal with functions that are themselves wrapped in subscribables! For example:
const sum = x => y => x + y;
const mathStrategy = ko.observable(sum);
const v1 = ko.observable(2);
const v2 = ko.observable(3);
My attempts
Chaining works, but quickly gets very hard to understand.
// Ugly...
const myResult = v2.ap(v1.ap(mathStrategy)); // S(5)
I can use liftN, but only if I make sure my first function is id.
// Also ugly...
const id = x => x;
const myResultL = lift3 (id) (mathStrategy) (v1) (v2); // S(5)
My questions
If lift2 handles f -> A.of(x) -> A.of(y) -> A.of(f (x) (y)), what is the theory/name/type behind A.of(f) -> A.of(x) -> A.of(y) -> A(f (x) (y))
If such a thing does not really “exist”, would it be okay to write an implementation of ap that unwraps A(f) on the go? (i.e. f => ko.unwrap (f) (x))
Code example
Object.assign(ko.subscribable, {
of: function(x) {
return ko.pureComputed(() => x)
}
});
Object.assign(ko.subscribable.fn, {
map: function(f) {
return ko.pureComputed(() => f(this()));
},
ap: function(sf) {
return ko.pureComputed(() => sf () (this()));
},
toString: function() {
return `S(${JSON.stringify(this())})`;
}
});
// Example code:
const sum = x => y => x + y;
const mult = x => y => x * y;
const mathStrategy = ko.observable(sum);
const v1 = ko.observable(1);
const v2 = ko.observable(3);
const result = v2.ap(v1.ap(mathStrategy));
console.log(result); // S(4)
v1(2);
mathStrategy(mult);
console.log(result); // S(6)
.as-console-wrapper { min-height: 100% !important; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
Note: This is kind of related to a question I asked earlier. Back then, I was focusing on lazily unwrapping A(x) and A(y), which I have given up on by now 😉.
I think the question you meant to ask, with proper notation for the types, is
If lift2 handles Apply f => (x -> y -> z) -> f x -> f y -> f z, what is the theory/name behind Apply f => f (x -> y -> z) -> f x -> f y -> f z?
Such a function is rare, because everyone just uses ap or the respective infix operator, like in Haskell
let az = af <*> ax <*> ay
However, I saw some two references on the internet to it being called ap2 [1][2], which makes sense as it is analogous to lift becoming lift2.
You can write this helper function as
const ap2 = af => ax => ay => ap(ap(af, ax), ay)
or like you already did
const ap2 = lift3(id)
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.
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])
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!