These are chat archives for ramda/ramda

28th
Aug 2016
James Forbes
@JAForbes
Aug 28 2016 00:42
@FeliciousX do you think its funny to say "sorry to trigger u"? That sort of language just alienates people. Makes them afraid to participate in this community and this industry. Take responsibility for your actions and your words, and then we can actually get to the learning part.
Drew
@dtipson
Aug 28 2016 00:55
@alexandrebodin maybe something like this might work?
fn1(param)
  .chain( fn1Res => [Future.of(fn1Res),fn2(fn1Res)].sequence(Future.of))
  .chain( ([fn1Res, fn2Res]) => fn3(fn1Res) );
James Forbes
@JAForbes
Aug 28 2016 00:56
@dtipson :thumbsup:
Beat me to it
But how are using sequence on the array prototype?
Drew
@dtipson
Aug 28 2016 00:58
If there's two values to carry through, and one is a future, just make the non-future one a Future two, create an array of futures, then sequence to get a future of an array of resolved values
Ah, I mean you can add it to Array.prototype, and I've been indulging in doing that, but you shouldn't. Safer to use a pointfree sequence method that handles Arrays
James Forbes
@JAForbes
Aug 28 2016 00:59

So in vanilla ramda it would be:

const sequence = R.sequence(Future.of)

fn1(param)
  .chain( fn1Res => sequence([Future.of(fn1Res),fn2(fn1Res)]) )
  .chain( ([fn1Res, fn2Res]) => fn3(fn1Res) );

@dtipson No, I like it :)

Drew
@dtipson
Aug 28 2016 01:00
Don't actually know if the version I worked out and play with is right, though it works
Array.prototype.sequence = function(point){
    return this.reduceRight(
      function(acc, x) {
        return acc.map(arr => p => [p].concat(arr) ).ap(x);
      },
      point([])
    );
};
James Forbes
@JAForbes
Aug 28 2016 01:00
Been wondering if prototype hacking is actually so bad within applications that control the entire context, e.g. gitter/github/slack/twitter whatever
Drew
@dtipson
Aug 28 2016 01:01
there's also the folktale version which uses chain: https://github.com/folktale/control.monads/blob/master/lib/basic.js#L50
James Forbes
@JAForbes
Aug 28 2016 01:01
My opinion at the moment is libraries shouldn't hack on the prototype, because you can have clashes, but prototype hacking in userland in a non embeddable application should be fine
I see lots of examples of extends JS's prototypes in here, that would make the language feel a lot more functional
like extending Function's prototype to include ap/chain/curry/partial
@dtipson do you do it in application code?
Drew
@dtipson
Aug 28 2016 01:03
Nope. Too dangerous. For just learning and explaining, I've been showing how Promise and Array can be extended to support all the methods they should have. But I think my takeaway is that if you want FL-compliant interfaces, use actual FL types
James Forbes
@JAForbes
Aug 28 2016 01:03
@alexandrebodin sequence is like a generic Promise.all
But is it dangerous in all contexts? The debate needs to be revisited now that web apps w/o ads are so common.
It is a very powerful aspect of the language. And if you own the domain entirely, why would it be dangerous? You can even extend a global tsd in typescript and get typechecking for it.
Drew
@dtipson
Aug 28 2016 01:05
yeah. When a users chosen browser isn't involved, things are different
James Forbes
@JAForbes
Aug 28 2016 01:06
What do you mean by a users chosen browser?
Drew
@dtipson
Aug 28 2016 01:07
Like, what if ES... has an implementation of Array.flatten/flatMap with a crazy/broken signature, and then eventually browsers start supporting it
James Forbes
@JAForbes
Aug 28 2016 01:08
But if it is your application, what harm can there be overriding that broken signature?
I mean, I guess in some contexts it would still matter. You might use libraries that would break if you alter the prototype. But I think there are many genuine scenarios out there.
Drew
@dtipson
Aug 28 2016 01:09
Right. Though I feel like there was a problem of this sort in the past, and some aspect of it got still got messy, leading to "break the web" problems. Maybe I'm not thinking straight, haven't had a lot of sleep this week. :)
James Forbes
@JAForbes
Aug 28 2016 01:10
Yeah there was. But I think the web was a different beast back then
I was reflecting on this while watching Crockford talk about AdSafe
Drew
@dtipson
Aug 28 2016 01:10
Definitely an area of caution, but like you say, maybe now there should be more reexamination of what "safe" contexts are out there
James Forbes
@JAForbes
Aug 28 2016 01:11
embedding arbitrary ad code in your webpage was standard practice back then
but more and more, we either have no ads, or the ads are just resources, not code. Like svg/png,text e.g. twitter's ads are all just normal twitter cards
Yeah I agree with that sentiment
Drew
@dtipson
Aug 28 2016 01:12
I think this is what I was thinking of most specifically, though there it was sort of the opposite problem: https://esdiscuss.org/topic/having-a-non-enumerable-array-prototype-contains-may-not-be-web-compatible
James Forbes
@JAForbes
Aug 28 2016 01:12
And the other problem was, libraries were overriding the prototype, and different libraries had different interpretations of functionality
So I think libraries should never override the prototype, but in userland, in some contexts it seems safe.
Drew
@dtipson
Aug 28 2016 01:13
in that the language avoided a using pre-existing prototype extension namespace out in the wild to avoid breaking things
to everyone's loss
i.e. RIP Array.contains
James Forbes
@JAForbes
Aug 28 2016 01:14
right, but that is because a library chose to override the prototype, which will always be a bad idea
but lets say gitter overrides the prototype within their own app
totally safe
Drew
@dtipson
Aug 28 2016 01:15
yeah.
James Forbes
@JAForbes
Aug 28 2016 01:16
or at least, worth discussing / investigating
Drew
@dtipson
Aug 28 2016 01:22
Either way, I'd definitely advise someone looking to solve a problem to use folktale or ramda's pointfree sequence first.
James Forbes
@JAForbes
Aug 28 2016 01:22
yeah same
I mean I'm saying this stuff, but I've never done it
I have control over certain domains and the prototype is untouched
but I feel like it is mostly FUD at this point preventing me from doing it
Brad Compton (he/him)
@Bradcomp
Aug 28 2016 02:31
Is it necessary for Ramda test files to run in an ES5 environment?
Like, could I, for instance, use Map in a test?
Churchill Lee
@FeliciousX
Aug 28 2016 03:30
@JAForbes i really didn't mean to piss u off but u hv to think of it the other way too
i asked a question and u become so angry about it, that is a tad bit annoying no?
i already said yes i am aware of 2048 being a clone of Threes
but that has nothing to do with my question
as sad as it is, 2048 became more popular than Threes and believe it or not when people talk about 2048 in front of me in real life i tell them to check out Threes and told them Threes came out first before 2048
but i mentioned 2048 here because it's the more well known one (as sad as it may be)
James Forbes
@JAForbes
Aug 28 2016 14:28

@FeliciousX I really believe you have the best of intentions. I'm sure you and I can figure this out as in a private message. Here is an implementation of the logic you were after.

var mergeRowLeft = R.pipe(
    R.splitEvery(2) //=> [[2,2],[2,2]]
    ,R.map(R.apply(R.add)) //=> [4,4]
    ,R.concat(R.__, [0,0,0,0]) //=> [4,4,0,0,0,0]
    ,R.take(4) // => [4,4,0,0]
)

mergeRowLeft([2,2,2,2]) //=> [4,4,0,0]

The idea is, process each merge as a pair instead of a complete list.
There are a lot more cases in the game that need to be handled.

So splitEvery gives us a list of pairs. We then map over each list and apply the pair to add. We then want to 0 fill the right hand side. To be safe, we concat an empty row and then take the first 4 from that list.

This function will accept invalid input though e.g. [2,5,2,5] will give you [7,7,0,0] when we actually want [2,5,2,5]. So you might want to handle that in a higher function, or you could have a more intelligent add function and keep the list processing the same.

And then you need to handle swiping right/up/down as well.

Swipe right could just be pipe(reverse, mergeRowLeft, reverse), which seems inefficient, but for this game, it really doesn't matter. You could also just define it again, but replace R.concat(R.__, [0,0,0,0]) with simply R.concat([0,0,0,0]) and replace R.take with R.takeLast

Rafe
@rjmk
Aug 28 2016 14:39
@JAForbes To fix the incorrect handling of [2,5,2,5] I think you could change mergeRowLeft to
compose(
  take(4),
  flip(concat)(repeat(0, 4)),
  map(reduce(add, 0)),
  chain(splitEvery(2)),
  groupWith(equals)
)
Basically just adding a groupWith, and the reduce deals with arrays of length 1 better than R.apply(R.add)
James Forbes
@JAForbes
Aug 28 2016 14:41
Yeah perfect
Really beautiful @rjmk I hadn't seen groupWith before!
James Forbes
@JAForbes
Aug 28 2016 14:54
Then I suppose you can just reuse mergeRowLeft for all the game logic
var board = [
[0,0,2,0]
,[2,2,2,2]
,[4,4,4,4]
,[8,8,8,8]
]


var mergeBoardLeft = R.map(compose(
  take(4),
  flip(concat)(repeat(0, 4)),
  map(reduce(add, 0)),
  chain(splitEvery(2)),
  groupWith(equals)
))


var mergeBoardRight = R.compose(
  R.map(R.reverse)
  ,mergeBoardLeft
  ,R.map(R.reverse)
)

var mergeColumnUp = R.compose(
  R.transpose
  ,mergeBoardLeft
  ,R.transpose
)

var mergeColumnDown = R.compose(
  R.tranpose
  ,mergeBoardRight
  ,R.transpose
)
I think Elm Arch would come in handy here though for animating the transitions. Instead of actually spitting out a board, spit out some Actions that describe the transformations that will happen. Then you can have a list of animations / computations that need to happen.
James Forbes
@JAForbes
Aug 28 2016 15:00
@rjmk why map(reduce(add,0)) instead of map(apply(add)) was that just a style thing? Or were you imagining merging more than 2 cards at once?
James Forbes
@JAForbes
Aug 28 2016 15:09
I suppose we need to handle the [1,2] case as well
James Forbes
@JAForbes
Aug 28 2016 15:23
Ok, so here is the full logic for threes rules
var emptyRow = [0,0,0,0]
var rowWidth = 4

var threequals = (a,b) =>
  a == b
  || a == 1 && b == 2
  || a == 2 && b == 1

var mergeRow = pipe(
  groupWith(threequals) // group into list of addable cards
  ,chain(splitEvery(2)) //process each each 2 at a time
  ,map(reduce(add, 0)) //add each pair
)

var mergeBoardLeft = map( // process every row
  pipe(
    mergeRow // merges a row
    ,flip(concat)(emptyRow)  //append [0,0,0,0]
    ,take(rowWidth) // keep the first 4 items
  )
)

var mergeBoardRight = map( // process each row
  pipe(
    mergeRow // merge a row
    ,concat(emptyRow) // prepend [0,0,0,0]
    ,takeLast(rowWidth) //keep the last 4 items
  )
)

var mergeBoardUp = pipe(
  transpose //rotate the board
  ,mergeBoardLeft // merge all the rows
  ,transpose //rotate the board back again
)

var mergeBoardDown = pipe(
  transpose
  ,mergeBoardRight
  ,transpose
)
Rafe
@rjmk
Aug 28 2016 15:47
@JAForbes reduce(add, 0) was because [1,2,3] would be [[1], [2], [3]] by that point in the composition. So apply(add) would take us to [ add(1), add(2), add(3) ]
Rafe
@rjmk
Aug 28 2016 16:01
Very nice implementations!
James Forbes
@JAForbes
Aug 28 2016 17:01

Just realised there is a bug in threequals, it will let you add 1,1 and 2,2, so it'd need to be something like

var threequals = (a,b) =>
  a > 2 && a == b
  || a == 1 && b == 2
  || a == 2 && b == 1

I'm sure there are other bugs in there too :D

@rjmk re: reduce, that makes a lot of sense! awesome
Michael Hurley
@buzzdecafe
Aug 28 2016 19:07
map(reduce(add,0)) could be map(sum)
Rafe
@rjmk
Aug 28 2016 19:08
@buzzdecafe Nice! I'm pretty bad at keeping track of all the functions in Ramda
Denis Stoyanov
@xgrommx
Aug 28 2016 19:22
I realise this one! monadic bind is W**Q in combinators (monadic bind for function)
because join is just W combinator
and functor is B
monadic bind will be B(B W)(C B) but BW is W** and CB is Q
point free monadic bind for function!)
Rick Medina
@rickmed
Aug 28 2016 19:38
question: how time dependent things handled in fp generally? things like throttle, debounce, settiemouts...?
Drew
@dtipson
Aug 28 2016 21:32
Just to clarify the discussion that @JAForbes and I had last night, I think that the main problem with modifying prototypes in utility libraries (like prototype, mootools, etc.) came because they adopted a policy of checking for native implementations (assuming that they'd come one day and have the same type signatures, and more importantly to them at the time: be faster). So what would happen would be that a native implementation of Array.[somemethod] would come along and have a different type signature than anticipated (or even a whole different purpose). It wasn't just that the code was rewriting native prototypes after the fact: that alone would not have caused problems. You can overwrite Promise.resolve all you want and make it do crazy things if you please. And if all your site code expects that crazy thing, the ES folks can define it any way they want, and your code will still work just fine.
It was, rather, that these libraries fell back to anticipating a native implementation that matched their api.
Fast-forward a couple of years, and ES committee members are considering creating some new language interface on some native type, and suddenly then realizing that this change would potentially break websites across the web. Because if they implemented an Array.contains with a different native signature than library A it'd break sites relying on library A. Those sites would see that Array.contains was implemented, fall back to it the native version (for speed!), and then code relying on the original library signature would break. And if they did it in such a way that broke library B, which had a different type signature from library A all along, that would also break the web. So they couldn't even make the api match library A (even at the cost of falling short of their TS vision) without then breaking library B. No avenue was open except for to just name the prototype method something else
Of course, what came later was the realization that utility libraries were often faster than native implementations anyhow, due to them not having to handle obscure edge-cases.
But this still leaves me uncertain, pedagogically, whether or not to advise people to "enhance" the native JS prototypes (WITHOUT the dangerous check for native implementations) or not.
Drew
@dtipson
Aug 28 2016 21:38
If you define Array.prototype.chain to confirm to the FL spec, it seems very very unlikely that the language will ever implement Array.chain with a different type signature on the same methodname such that it harms the code on your site (a site you might create today but will exist long after you've moved on to other jobs)
The only thing you might miss out on is a future potential speed enhancement on a native Array.chain method, right? Is that minor, maybe-not-ever-even-realized potential loss worth worrying about (given that by the time it becomes a performance issue, the planet may not even be using javascript or your site any longer)? Or are there other problems worth fearing I'm not thinking of?