Hi there!
Got a function combination problem and was wondering if Ramda could help me write it in a point-free style.
Basically, I have 2 functions both which take the same input. One function returns a Promise which resolves to a string and the second function returns a string. I want to join the results of the two functions using path.join
.
I.e, non point-free:
const fn = obj => getString1(obj).then(s1 => path.join(s1, getString2(obj))
Using ramda's join
with a forward-slash instead of path.join
is acceptable. Is there a point-free way of combining these functions?
I thought perhaps lift
could help but I figure it out with the promise-returning function.
pipeWith(f, g, x)
i can write pipeWith(x, f, g)
i don't think so. maybe you can use R.reverse
for the arguments
const pipeWhileNotNil = R.pipeWith((f, res) => R.isNil(res) ? res : f(res));
const f = pipeWhileNotNil(R.reverse[Math.pow, R.negate, R.inc])
Note this code is only for demo, prob it will not work @theqp
:point_up: Edit: i don't think so. maybe you can use R.reverse
for the arguments
const pipeWhileNotNil = R.pipeWith((f, res) => R.isNil(res) ? res : f(res));
const f = pipeWhileNotNil(R.reverse[Math.pow, R.negate, R.inc])
Note this code is only for demo, prob it will not work but you got the idea 🙂 @theqp
const tagger = curry((tag, text, template) => pipe(
when(
includes(tag),
pipe(
replace(tag, text),
tagger(tag, text)
)
)
)(template))
const applyTags = (tags, template) =>
pipe(
toPairs,
map(over(lensIndex(0), s => `{${s}}`)),
reduce(pipe(append, apply(tagger)), template)
)(tags)
const tags = {
test: 'tasty test',
cheese: 'amazing smoked gouda'
}
const template = 'this is a {test}. I love {cheese}.'
applyTags(tags, template) // "this is a tasty test. I love amazing smoked gouda."
const includedIn: <T>(list: T[]) => (item: T) => boolean =
list => item => includes(item)(list)
Hi, I'm new to functional programming and Ramda. How might you improve these functions? Can any be made pointfree? Thank you!
//
// elementAfter(el, list) returns the element after el in the list
// (wapping to the beginning if needed)
// how would you improve these functions? can any be made pointfree?
//
const indexAfter = (index, list) => mathMod(inc(index), length(list))
const indexAfterElement = (el, list) => indexAfter(indexOf(el, list), list)
const elementAfter = (el, list) => nth(indexAfterElement(el, list), list)
elementAfter('c', ['a', 'b', 'c']) // 'a'
Ramda REPL: https://tinyurl.com/yhwpgykj
// This fails cause "reduce: list must be array or iterable"
R.transduce(mapfilterComp, R.groupBy(R.prop('programId')), {}, settings)
// 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!