How do you test pipeable operators in NgRx?
I have following operator that I do export in my selectors file and I am not sure how best to test it, as it behaves differently to a normal selector.
export const selectRehydratedPreferredLanguage = pipe(
select(selectLanguageState),
skipWhile((state) => !state.preferredLanguage && !state.rehydrationComplete),
map((state) => state.preferredLanguage)
);
Best regards,
Pascal
Related
I have two predicates
interface Foo {}
interface Bar {}
declare const isFoo: (a:unknown):a is Foo
declare const isBar: (a:unknown):a is Bar
What is the functional way to combine two predicates to create a new predicate (for simplicity, let's assume it's a => isFoo(a) && isBar(a)?
With fp-ts, I initially thought I could fold(monoidAll)([isFoo, isBar]), but fold expects the array to be of booleans, not of functions that evaluate to boolean.
This works
import { monoid as M, function as F, apply as A, identity as I, reader as R } from 'fp-ts'
interface Foo{}
interface Bar{}
declare const isFoo:(a:unknown) => a is Foo
declare const isBar:(a:unknown) => a is Bar
const isFooAndBar = F.pipe(A.sequenceT(R.reader)(isFoo, isBar), R.map(M.fold(M.monoidAll)))
But boy howdy is that convoluted. I thought there could be another way. I ended up writing my own monoid that takes two predicates and combines them, calling it monoidPredicateAll:
const monoidPredicateAll:M.Monoid<Predicate<unknown>> = {
empty: ()=>true,
concat: (x,y) => _ => x(_) && y(_)
}
Is there a canonical FP way of combining two predicates? I know I could do something like
xs.filter(x => isFoo(x) && isBar(x))
But it can get complicated with more predicates, and re-using a monoid makes it less likely I'll do a typo like isFoo(x) || isBar(x) && isBaz(x) when I meant all && (and that's where a xs.filter(fold(monoidPredicateAll)(isFoo,isBar,isBaz)) would help out.
I found a discussion about this on SO, but it was about Java and a built-in Predicate type, so didn't directly address my question.
Yes, I'm overthinking this :)
I ended up doing this:
export const monoidPredicateAll:Monoid<Predicate<unknown>> = {
empty: ()=>true,
concat: (x,y) => _ => x(_) && y(_)
}
Then I could do
import {monoid as M} from 'fp-ts'
declare const isFoo: Predicate<number>
declare const isBar: Predicate<number>
const isFooAndBar = M.fold(monoidPredicateAll)([isFoo,isBar])
For others looking for a working solution, based on #user1713450's answer
import * as P from 'fp-ts/lib/Predicate';
import * as M from 'fp-ts/Monoid';
const createMonoidPredicateAll = <T>(): M.Monoid<P.Predicate<T>> => ({
empty: () => true,
concat: (x, y) => (_) => x(_) && y(_),
});
export const combine = <T>(predicates: P.Predicate<T>[]) =>
M.concatAll(createMonoidPredicateAll<T>())(predicates);
I wanted to make a compose function for piping and im stuck. I managed to make a pointfree pipe but cant figure out composing.
// pointfree
const pipe = fn => future => future.pipe(fn)
// compose pipes // not working
const composePipe = (...fns) => (...args) => fns.reduceRight( (future, fn) => future.pipe(fn), args)[0];
I'll answer your question eventually, but let's take a step back first.
An important thing to understand is that the pipe method is just function application. In other terms: future.pipe (f) == f (future).
This means that your pipe function can be redefined as such:
const pipe = fn => future => future.pipe(fn)
//to:
const pipe = fn => value => fn (value)
This new version of pipe works exactly the same way, except that it works on any values, not just Futures. But let's take a step back further even.
The signature of this function is as follows: pipe :: (a -> b) -> a -> b. It takes a function from A to B, and returns a function from A to B.
Wait a minute....
const pipe = fn => value => fn (value)
//to:
const pipe = fn => fn
That new definition does the same thing. Except that it works on anything, not just Functions. Actually it's just the identity function. So a curried (you said point-free, but I think you meant curried) version of future.pipe is just the identity function.
So why is this? Because all .pipe does is function application. And you can apply your functions yourself.
Now to answer your next question about composing pipes. What you're actually looking for is something that takes a number of functions, and applies them in sequence.
If you're using Ramda, that's pipe. We can implement this ourselves though:
const pipe = (...fns) => (...args) => fns.reduce ((args, f) => [f (...args)], args)[0]
I am still new to functional programming and have been trying to learn how to use transducers. I thought I had a good use case but every time I attempt to write a transducer with Ramda for it, I get the following error:
reduce: list must be array or iterable
I have tried rewriting it several ways and looked at several explanations on the web of transduction but to no avail. Any suggestions?
const data = [{cost:2,quantity:3},{cost:4,quantity:5},{cost:1,quantity:1}];
const transducer = R.compose(R.map(R.product), R.map(R.props(['cost', 'quantity'])));
const result = R.transduce(transducer, R.add, 0)(data);
console.log(result)
In the context of a transducer, compose reads left to right. You just need to invert product and props:
const data = [
{cost:2,quantity:3},
{cost:4,quantity:5},
{cost:1,quantity:1}];
const transducer =
compose(
map(props(['cost', 'quantity'])),
map(product));
console.log(
transduce(transducer, add, 0, data)
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {compose, map, props, product, transduce, add} = R;</script>
The reason why the order reverses is that transducers utilize a property of function composition that is sometimes called abstraction from arity. It simply means that a function composition can return, well, another function:
const comp = f => g => x => f(g(x));
const mapTrace = tag => f => (console.log(tag), xs => (console.log(tag), xs.map(f)));
const sqr = x => x * x;
const main = comp(mapTrace("a")) (mapTrace("b")) (sqr); // returns another function
console.log(main); // logs the 2nd map and then the 1st one (normal order)
// pass an additional argument to that function
console.log(
main([[1,2,3]])); // logs in reverse order
Why returns the composition another function? Because map is a binary function that expects a function argument as its first argument. So when the composition is evaluated it yields another compositon of two partially applied maps. It is this additional iteration that reverses the order. I stop at this point without illustrating the evaluation steps, because I think it would get too complicated otherwise.
Additionally, you can see now how transducers fuse two iterations together: They simply use function composition. Can you do this by hand? Yes, you can absolutely do that.
Is there a way to achieve some kind of recursively-typed functions in Ceylon? For example, I can define combinatory logic in Ceylon in a type-safe way like so:
class Fi(shared Fi(Fi) o) { }
Fi veritas =
Fi((Fi t) =>
Fi((Fi f) =>
t));
Fi perfidas =
Fi((Fi t) =>
Fi((Fi f) =>
f));
Fi si =
Fi((Fi l) =>
Fi((Fi m) =>
Fi((Fi n) =>
l.o(m).o(n))));
print(si.o(veritas).o(veritas).o(perfidas) == veritas);
print(si.o(perfidas).o(veritas).o(perfidas) == perfidas);
print(si.o(veritas).o(perfidas).o(veritas) == perfidas);
print(si.o(perfidas).o(perfidas).o(veritas) == veritas);
This code works as intended. However, for clarity, brevity, and applicability to other problems, I would like to be able to do implement this behavior using only functions. Take something like the following (non-working) example:
alias Fi => Fi(Fi);
Fi veritas(Fi t)(Fi f) => t;
Fi perfidas(Fi t)(Fi f) => f;
Fi si(Fi l)(Fi m)(Fi n) => l(m)(n);
print(si(veritas)(veritas)(perfidas) == veritas);
print(si(perfidas)(veritas)(perfidas) == perfidas);
print(si(veritas)(perfidas)(veritas) == perfidas);
print(si(perfidas)(perfidas)(veritas) == veritas);
In the function alias version, the Fi type represents functions whose operands and return values can be composed indefinitely. Note that due to their recursive nature, the types Fi, Fi(Fi), and Fi(Fi)(Fi) could be considered functionally equivalent; all that the consumer of any of them knows is that if they have a function that, if called on a Fi, will given them another Fi.
Here is my understanding of what Ceylon currently supports:
Recursive aliases are not supported due to being erased during compilation.
I am unaware of any current Ceylon feature that could be used to specialize the Callable type recursively or otherwise get the needed kind of infinite chaining.
A possibly related question got a negative response. However, that was two and a half years ago, before some potentially related features like type functions were implemented in Ceylon 1.2 and a pair of blogs written by Gavin King on new type function support.
There is a github issue on higher order generics.
There is another github issue on allowing custom implementations of Callable.
Can the desired behavior be implemented in the current version of Ceylon? Or would it definitely require one or both of the aforementioned backlog features?
Here are four functions I am trying to compose into a single endpoint string:
const endpoint = str => `${str}` || 'default'
const protocol = str => `https://${str}`
const params = str => `${str}?sort=desc&part=true&`
const query = str => `${str}query={ some:'value', another:'value'}`
let finalEndpoint = R.compose(query, params, protocol, endpoint)
var result = finalEndpoint('api.content.io')
This composition works and returns the result I want which is:
https://api.content.io?sort=desc&part=true&query={ some:'value', another:'value'}
But notice how I have hard coded the values for params and query inside their function body. I see only one value going up the value in this R.compose chain.
How and where exactly do I pass in parameters to the params and query parameters?
UPDATE:
What I did was curried those functions like this:
var R = require('ramda');
const endpoint = str => `${str}` || 'default'
const protocol = str => `https://${str}`
const setParams = R.curry ( (str, params) => `${str}?${params}` )
const setQuery = R.curry ( (str, query) => `${str}&query=${JSON.stringify(query)}` )
and then
let finalEndpoint = R.compose(protocol, endpoint)
var result = setQuery(setParams(finalEndpoint('api.content.io'), 'sort=desc&part=true'), { some:'value', another:'value'})
console.log(result);
But the final call to get result still seems pretty hacked and inelegant. Is there any way to improve this?
How and where exactly do I pass in parameters to the params and query parameters?
Honestly, you don't, not when you're building a compose or pipe pipeline with Ramda or similar libraries.
Ramda (disclaimer: I'm one of the authors) allows the first function to receive multiple arguments -- some other libraries do, some don't -- but subsequent ones will only receive the result of the previous calls. There is one function in Sanctuary, meld, which might be helpful with this, but it does have a fairly complex API.
However, I don't really understand why you are building this function in this manner in the first place. Are those intermediate functions actually reusable, or are you building them on spec? The reason I ask is that this seems a more sensible version of the same idea:
const finalEndpoint = useWith(
(endpoint, params, query) =>`https://${endpoint}?${params}&query=${query}`, [
endpoint => endpoint || 'default',
pipe(toPairs, map(join('=')), join('&')),
pipe(JSON.stringify, encodeURIComponent)
]
);
finalEndpoint(
'api.content.io',
{sort: 'desc', part: true},
{some:'value', another:'value'}
);
//=> "https://api.content.io?sort=desc&part=true&query=%7B%22some%22%3A%22value%22%2C%22another%22%3A%22value%22%7D"
I don't really know your requirements for that last parameter. It looked strange to me without that encodeUriComponent, but perhaps you don't need it. And I also took liberties with the second parameter, assuming that you would prefer actual data in the API to a string encapsulating that data. But if you want to pass 'sort=desc&part=true', then replace pipe(toPairs, map(join('=')), join('&')) with identity.
Since the whole thing is far from points-free, I did not use a points-free version of the first function, perhaps or(__, 'default'), as I think what's there is more readable.
Update
You can see a version of this on the Ramda REPL, one that adds some console.log statements with tap.
This does raise an interesting question for Ramda. If those intermediate functions really are desirable, Ramda offers no way to combine them. Obviously Ramda could offer something like meld, but is there a middle ground? I'm wondering if there is a useful function (curried, of course) that we should include that works something like
someFunc([f0], [a0]); //=> f0(a0)
someFunc([f0, f1], [a0, a1]); //=> f1(f0(a0), a1)
someFunc([f0, f1, f2], [a0, a1, a2]); //=> f2(f1(f0(a0), a1), a2)
someFunc([f0, f1, f2, f3], [a0, a1, a2, a3]); //=> f3(f2(f1(f0(a0), a1), a2), a3)
// ...
There are some serious objections: What if the lists are of different lengths? Why is the initial call unary, and should we fix that by adding a separate accumulator parameter to the function? Nonetheless, this is an intriguing function, and I will probably raise it for discussion on the Ramda boards.
I wrote a little helper function for situations like this.
It is like compose, but with the rest params also passed in. The first param is the return value of the previous function. The rest params remain unchanged.
With it, you could rewrite your code as follows:
const compound = require('compound-util')
const endpoint = str => `${str}` || 'default'
const protocol = str => `https://${str}`
const params = (str, { params }) => `${str}?${params}`
const query = (str, { query }) => `${str}query=${query}`
const finalEndpoint = compound(query, params, protocol, endpoint)
const result = finalEndpoint('api.content.io', {
params: 'sort=desc&part=true&',
query: JSON.stringify({ some:'value', another:'value'})
})
If you have params and query as curried functions then you can:
EDIT: code with all the bells and whistles, needed to change parameter order or use R.__ and stringify object
const endpoint = R.curry( str => `${str}` || 'default' )
const protocol = R.curry( str => `https://${str}` )
const params = R.curry( (p, str) => `${str}?${p}` )
const query = R.curry( (q, str) => `${str}&query=${q}` )
let finalEndpoint =
R.compose(
query(JSON.stringify({ some:'value', another:'value' })),
params('sort=desc&part=true'),
protocol,
endpoint
)
var result = finalEndpoint('api.content.io')
console.log(result)