@piet-v

```
const secretMap2 = compose(
([pirates, gold]) => ('count of pirates: ' + pirates.length + ' and the count of gold piles: ' + gold.length),
props(['pirate', 'pile_of_gold']),
groupBy(identity))
```

@xgrommx my bad, should've mentioned that key can be missing from the array

that code will fail on

`undefined.length`

hence my `R.map(R.defaultTo(0))`

@piet-v why are you doing the

`R.flatten`

though?
in the input that you provided the array is flattened already.

pasted the wrong input, it can be nested one level

ok, that makes sense

fastest I could make it till now, though the usage of default values seems hacky at best lol

```
const secretMap = R.pipe(
R.flatten,
R.groupBy(R.identity),
({pirate = [], pile_of_gold = []}) => ('count of pirates: ' + pirate.length + ' and the count of gold piles: ' + pile_of_gold.length)
);
```

I don't know, there are a thousand ways to crack an egg, I don't see anything wrong with your implementation. Although, maybe I would have done a straightforward

`reduce`

...
maybe it's a lot more imperative... I don't know:

```
R.reduce(
(result, entry) => (
entry === 'pirate' ? R.evolve({ pirates: R.inc }) :
entry === 'pile_of_gold' ? R.evolve({ gold: R.inc }) :
R.identity
)(result),
{ pirates: 0, gold: 0 }
)
```

sec, I've done that as well but more ugly lol

```
const reducer = (acc, value) => R.evolve({[value]: R.inc})(acc);
const accumulator = {pirate: 0, pile_of_gold: 0};
const secretMap = R.pipe(
R.flatten,
R.reduce(reducer, accumulator),
({pirate, pile_of_gold}) => ('count of pirates: ' + pirate + ' and the count of gold piles: ' + pile_of_gold)
);
```

well, you will have to pipe it... first flatten, then the reduce, and then the string transformation.

In that way, I agree is uglier :smile:

:D

Some people hate ternary operators... Specially if they are nested. I personally think that the formatting can help them be a lot more readable. Again, your original implementation seems pretty elegant :+1:

`R.cond`

can come to the rescue though
lol, just saw a previous solution of someone else, he went plain javascript though :D

```
function secretMap(sp) {
const getCount = (item) => sp.filter(v => v[0] === item).length;
return `count of pirates: ${getCount('pirate')} and the count of gold piles: ${getCount('pile_of_gold')}`;
}
```

@piet-v

```
const secretMap2 = compose(
([pirates, gold]) => ('count of pirates: ' + pirates.length + ' and the count of gold piles: ' + gold.length),
map(defaultTo([])),
props(['pirate', 'pile_of_gold']),
groupBy(identity))
```

@piet-v I think your original was good :D

`const secretMap_ = lift((p, g) => `count of pirates: ${p} and the count of gold piles: ${g}`)(compose(length, filter(equals('pirate'))), compose(length, filter(equals('pile_of_gold'))))`

I like the latest one suggested by @xgrommx a lot. Although I would reformat it a little:

```
const getCountOf = prop => R.compose(R.length, R.filter(R.equals(prop)));
const getSecretMap = R.lift(
(p, g) => `count of pirates: ${p} and the count of gold piles: ${g}`
)(
getCountOf('pirate'),
getCountOf('pile_of_gold')
);
```

@josepot I have 1-line (doesn't matter) :smile:

:joy: Well, sorry, I'm an "80 characters limit" nazi... I value short lines a lot, even if they make they increase the total number of lines.

but that's me

BTW: thanks a lot @xgrommx . Thanks to your code I've finally understood how

`lift`

works. :tada:
not that I've ever spend any time trying to figure it out, but a few time that I saw the function in the docs I was like: "what is this?" and I was like: "whatever, I probably don't need it". Now, I've seen the light.

Question for @xgrommx : in the past I've used

`converge`

for accomplishing the same thing. Is there anything that can be done with `lift`

that can't be done with `converge`

?
@josepot

I try to make the code on my blog render perfectly on a phone without any line wrapping/scrolling etc.

Which has led to me being a 40-50 character kind of guy :D

new lines are great if you don't like merge conflicts too

@josepot almost the same, converge can apply several arguments, but lift is applicative construction.

then I think that perhaps the documentation should link them in the "See also", do you know what I mean?

@josepot converge works only with functions

@xgrommx right!

now I get it, thanks @xgrommx

for example cartesian product

@ram-bot

@ram-bot

`liftN(2, unapply(identity))([1,2,3], [4,5,6])`

@josepot u can create

`converge`

from `ap`

https://github.com/xgrommx/from-combinators-to-fp/blob/master/src/index.js#L26
@JAForbes short lines are great for readability... but I also have a selfish reason: I work a lot in my laptop and I use vim, I usually have 3 vertical splits... So short lines are perfect for my workflow. Not the strongest argument, but I have to admit that I became a lot more intolerant with long lines when I started working with vim...

That's awesome! I get it now @xgrommx . Thanks a lot!

@josepot

`liftA2`

=> `ap(map(x => y => [x, y], [1,2,3]), [4,5,6])`

`liftA3 = liftA2 + ap`

and etc
in haskell it is really nicely :smile:

`s <$> fn <*> s1 <*> s2 <*> ...`

@josepot also u can get zip behavior from applicative https://github.com/xgrommx/practical-functional-programming/blob/master/zip-list.js#L60

@xgrommx thanks, though it still feels too long and complicated for what it is. the distance between thought and code is too wide. compare the solution in J:

`,.(+/@(={:)\)`

Here’s one more alternative:

```
converge(zip, [
identity,
pipe(
scan(flip(append), []),
tail,
map(x => filter(equals(last(x)), x)),
map(length)
)
])
```

It gets a little nicer if you break out some reasonable utility functions:

```
const scans = pipe( scan(flip(append), []), tail );
const frequencyOf = curry(
(pickFn, xs) => filter(equals(pickFn(xs)), xs).length
);
const occurenceCounts = pipe( scans, map(frequencyOf(last)) );
const withOccurenceCounts = converge(zip, [identity, occurenceCounts]);
```

how can I cast to bool in ramda?

@ram-bot

`map(Boolean, [0, 1, 2])`

sorry, the vps bot is on is dead. i've put it somewhere else for now

@ram-bot status

All systems operational.

I swear I think I've read this before, but the reason Ramda calls

`map`

on things instead of `fmap`

is because it's more familiar to javascript coders, right?
See the link in the edit

@gabejohnson So what I'm getting from that is that basically says Haskell has map and fmap, and at first just had map on everything, then went and changed it so "it was easier for beginners to realize they screwed up on something", but that everyone really just wants map. So it was supposed ot be map, but someone said "Let their be confusion and hopefully better error messages!" and thus came about fmap?

I think it had

`map`

on `List`

s and then generalized it to `Functor`

s
but to do it over again would prbly call

`fmap`

`map`

that's what the consensus is. The best solution they seem to have come up with in mailing list(found in that stackoverflow thread, thanks) is that they should go about depreciating fmap by defining map, and redoing fmap in terms of it on functors

but from what comes after I have no idea

the final resting place of why fmap and map exist in haskel

I don't think PureScript has

`fmap`

So to answer your original question I would say because

`fmap`

is unnecessary and confusing
:+1:

What's the word for the relationship between

`fromFoo`

and `toFoo`

?`input === fromFoo(toFoo(input))`

natural transformation?

isomorphism?

It looks like complimentary transformers?

I think isomorphic

I never really understood that word before so I'm not sure =D

This is isomorphic (taking from Dr. Boolean’s egghead lesson)

```
// from(to(x)) == x
// to(from(y)) == y
```

and natural transformation:

`// nt(x).map(f) == nt(x.map(f))`

isomorphic: of similar structure

```
const fromInt = x => {int:x}
const toInt = x => x.int
toInt(fromInt(5)) == 5
```

another example would be `fromCharCode`

and `charChodeAt`

which can be composed to get back the original character from a string at an index

Does my test make sense, then? (it does pass)

```
test('qs.parse and qs.stringify are isomorphic', function (t) {
t.deepEqual(
qs.parse(qs.stringify({a: 'a'})),
{a: 'a'}
)
})
```

`JSON.parse`

and `JSON.stringify`

are isomorphic, yes
mine is query string

but yeah, I'm just making sure I get it

Quite simple for such an intimidating word!

isomorphic functions: https://en.wikipedia.org/wiki/Isomorphism

tow functions are considered isomorphic IFF

`F(f(x)) == f(F(x)) == x`

meaning in mathematical terms `f == inverese F && F == inverse f`

. An example being exponants and logarithms for math terms
for REALLY confusing example:

`not`

in boolean is the invertor operator(`!`

) is isomorphic to itself ( `! ! TRUE && TRUE`

) XD
haha

If you want a real(er) example: trigonometry

the great page of isomorphic relations: https://www.mathsisfun.com/algebra/trig-inverse-sin-cos-tan.html

Thanks!

@m59peacemaker reminder: don't get

`Algebra`

confused with `algebras`

compose(g, h) = id

Any practical or theoretical explanation to why the aggregator in mapAccum returns a tuple, and why the final returned value includes the final accumulator?