// This is a function that returns a function with it's arguments re-arranged.
// I'm not sure how useful it is, i just wanted to make one.
// You specify the new argument order with an integer, with each digit from 1-9
// symbolising the original order.
// for example, to switch the order of a binary function like divide, we use 21
// we're counting from 1.
// swap(21, divide)(4,2) //--> 0.5
//
// More complex now
// swap(42133, foo)(these, are, some, args, wow)
// equiv. to: foo(args, are, these, some, some)
// Note you can repeat numbers to pass to the same value multiple times.
// The function applies curryN to the result based on how many digits there are,
// giving you partial application, of sorts.
const swap = curry((n, f) => {
const argMap = Array.from(n.toString()).map(x => parseInt(x) - 1)
return curryN(argMap.length, (...args) =>
f(
...addIndex(map)((a, i) => {
const newPos = argMap.indexOf(i)
return newPos !== -1 ? args[newPos] : a
}, args)
)
)
})
Hi, I have the following code that is returning true when it should return false:
const isNotEmpty = R.complement(R.isEmpty)
const isNotNil = R.complement(R.isNil)
const isNotEmptyFeed = R.and(R.pathSatisfies(isNotEmpty, ['data', 'children']), isNotNil)
isNotEmptyFeed({
subreddit: 'abruptchaos',
feedCategory: 'posts_Default',
feedUrl: 'https://www.reddit.com/r/abruptchaos/.json?limit=100&count=100&after=null',
data: { children: [] },
})
https://tinyurl.com/ydmqb9ls
Can anyone tell me where I've gone wrong here? data.children
is empty, so that should be false right?
Hi there, does the following combinator have a name? Or, on the other hand, how we usually deal this pattern instead of composing with the combinator? For example:
// ???
const combinator = R.curry((f, g, x, y) => f ( g(x) ) ( y ) )
// findById :: ((Record K V -> Boolean) -> [Record K V] -> Maybe Record K V) -> (V -> Record K V -> Boolean) -> V -> [Record K V] -> Maybe Record K V
const findById = combinator(R.find, R.propEq("id"))
findById(1)([{id: 1}])
//> {id: 1}
I know the combinator can be derived from the following 2 functions, where g
is identity
.
https://github.com/ramda/ramda/blob/ed191e6a476330e37db259a5c2d04bfba0b2d63d/source/useWith.js#L21
https://github.com/fantasyland/fantasy-birds/blob/6f37c6e7daf31f1c31860f0f307555f4c7922ec2/src/dovekie.js#L4
But, is there a more elegant way to deal with it? Any suggestions or perspectives are welcome.
@zxol Thank you, it is exactly the same beta-reduction as mine. And then my following question comes up:
If the function is variadic, then I need a I**
combinator to achieve the same functionality, does it make sense?
const idstarstar = R.curry((f, x, y) => f(x)(y))
// right associative
// findById :: (Pred -> [a] -> Maybe a) -> (v -> Pred) -> v -> ([a] -> Maybe a)
const findById = R.compose(R.find, R.propEq("id"))
idstarstar(findById)(1, [{id: 1}])
https://github.com/fantasyland/fantasy-birds/blob/6f37c6e7daf31f1c31860f0f307555f4c7922ec2/src/idstarstar.js#L4
I think the problem is that f(g(x))
returns another function, so I can not give 2 arguments at once and apply to v -> ([a] -> Maybe a)
.
Is there a more elegant way to deal with it? Any suggestions or perspectives are welcome.
Hi, I'm creating a simple tic-tac-toe style game and game state is stored in a game object.
I've noticed two problems:
1- most of my functions need access to the game object so I need to repeatedly pass it down.
2- because of #1 when I try to compose with a pipeline I often need to curry some functions with the game object.
Both seem like code smells. Is this typical? Any advice to improve?
(This code uses folktale Result)
import {
pipeWith,
chain,
curry,
add,
multiply,
update,
evolve,
} from "ramda";
import folktale from "folktale";
const { result: Result } = folktale;
const createGame = ({ width, height } = {}) => ({
size: {
width,
height,
},
grid: Array(width * height), // game is a grid but stored as a flat array
// additional game state goes here
});
//
// 1) most of my functions need access to the game object so I'm repeatedly passing it down. is this typical?
//
const gridIndex = (game, pos) => add(pos[0], multiply(pos[1], game.size.width));
const cell = (game, pos) => game.grid[gridIndex(game, pos)];
const updateCell = (game, pos, val) =>
evolve({
grid: (g) => update(gridIndex(game, pos), val, g),
})(game);
// two contrived validators that don't require the game object
const validateColGte0 = (pos) =>
pos[0] >= 0 ? Result.Ok(pos) : Result.Error(`col must be >= 0: ${pos[0]}`);
const validateRowGte0 = (pos) =>
pos[1] >= 0 ? Result.Ok(pos) : Result.Error(`row must be >= 0: ${pos[1]}`);
// three validators that need the game object
const validateColLtWidth = curry((game, pos) =>
pos[0] < game.size.width
? Result.Ok(pos)
: Result.Error(`col must be < ${game.size.width}: ${pos[0]}`)
);
const validateRowLtHeight = curry((game, pos) =>
pos[1] < game.size.height
? Result.Ok(pos)
: Result.Error(`row must be < ${game.size.height}: ${pos[1]}`)
);
const validateCellIsEmpty = curry((game, pos) =>
!cell(game, pos)
? Result.Ok(pos)
: Result.Error(`position is not empty: ${pos}`)
);
//
// 2) To compose functions in a pipline I need to curry them with the game object
// would it be better to make the validator signatures take a single object
// with all possible paramaters? e.g. { game, pos }
//
const validatePosition = (game, pos) =>
pipeWith(chain)([
validateColGte0,
validateRowGte0,
validateColLtWidth(game),
validateRowLtHeight(game),
validateCellIsEmpty(game),
])(pos);
const performMove = (game, pos, val) =>
validatePosition(game, pos).matchWith({
Ok: ({ value: validatedPosition }) =>
updateCell(game, validatedPosition, val),
Error: ({ value: error }) => {
console.log(error);
return game;
},
});
// create and run the game
let game = createGame({ width: 3, height: 3 });
console.log(game);
game = performMove(game, [1, 1], "X"); // works
console.log(game);
game = performMove(game, [1, 1], "O"); // position is not empty: 1,1
console.log(game);
export function cond<A, B>(fns: Array<[SafePred<A>, (...a: readonly A[]) => B]>): (...a: readonly A[]) => B | undefined;
export function cond<A, B>(
fns: [
...[SafePred<A>, (...a: readonly A[]) => B],
[(...a: readonly A[]) => true, (...a: readonly A[]) => B],
...[SafePred<A>, (...a: readonly A[]) => B]
]
): (...a: readonly A[]) => B;
any
makes no sense to me, B can be any
Looks like Ramda doesn't include implementations of the various Fantasy Land "algebraic structures" like
Result, Either, Maybe, State, Reader, Future
Do folks typically just use the libraries listed on the Fantasy Land website here:
https://github.com/fantasyland/fantasy-land/blob/master/implementations.md
So If I wanted all of the above structures maybe I'd include and use:
Folktale: Result, Future
Sanctuary: Either, Maybe
Monastic: State
Zion: Reader
Just want to make sure there is no obvious single library that I should be using.
Thanks!
const seq = R.curry((list, fns) => R.pipe(...fns)(list))
writing simple and readable code is more important than pursuing for point-free/curry code.
@adispring Absolutely! However in my case it's just about embracing the pointfree style to fully grok it. This is just for a small utility package in a side project of mine that I specifically started to learn the ins and outs of FP. :D Using spread gave me a TS Error earlier, going to try yours after lunch! Thanks!
I have a question about how to combine multiple functions with compose/pipe,
Is there any possibility to combine funcA
and funcB
in ramda way?
(or any reason we shouldn't do something like this.
Thanks in advance!
const funcA = (a, b) => a + b;
const funcB = (ab, c) => ab * c;
const funcC = (a, b, c) => {
const ab = funcA(a, b);
return funcB(ab, c)
}
funcC(1,2,3) // 9
@zydmayday if we look at funcB
we see it's a 2-arity function. Remember that piping or composing functions only works when they're 1-arity or curried. But lets assume funcb
is curried, then using compose would work, with one caveat, you have to call it like funcC(1,2)(3)
. That's because the result of compose is a 2-arity function (the same as funcA) - it'll just just throw away the 3rd argument. This will often work in the context of a larger program, if you're always calling it partially.
You can use a combinator to get around this. in this case it's a (f, g) => (a, b, c) => g(f(a,b), c)
I don't think that one has a special name in FPland.
The ramda version of that combinator would be something like converge(funcB, [funcA, nthArg(2)])
converge(funcB, [funcA, nthArg(2)])
is good enough for me, I will try this.I finished working on an initial version of a pattern matching library and wanted to share it with you guys. Please share your thoughts if you have the time and are interested. Yes this is a bit of a shameless plug, but I don't know of other communities where I can share this.
Hi, I'm trying to make this peace of code more functional like, and more elegant, so this:
checkActive(service).then(
isActive => isActive
? stopService(service)
: startService(service)
)
Became this:
checkActive(service).then(
ifElse(
identity,
partial(stopService, [service]),
partial(startService, [service])
)
)
But to me, the first one seems easier to understand. Maybe there's a better approach? Would love any insights on this... thanks!