These are chat archives for ramda/ramda

22nd
Oct 2018
Anton
@AntonRich
Oct 22 2018 05:01
I didn't know Ramda had gitter.
Hello, functional people.
What are you working on?
This is so curios.
geganizharadzeatamido
@geganizharadzeatamido
Oct 22 2018 10:46

hi everyone. I have an array of objects and I want to evolve an object based on some predicate. for example

{
   foo: {
       bars: [ { id: 1, isActive: true }, { id: 2, isActive: true } ]
   }
}

so I want to do something like evolve -> set isActive to false if id is 2

I was wondering perhaps there's a built in function I could use. otherwise I'll create a custom one
Riku Tiira
@rikutiira
Oct 22 2018 10:58
what kind of custom function are you talking about?
you could for example do:
R.evolve({
    foo: {
        bars: R.map((o) => R.evolve({ isActive: R.always(o.id !== 2) }, o))
    }
}, obj)
although I’d personally opt for R.merge instead of R.evolve for the one inside R.map
geganizharadzeatamido
@geganizharadzeatamido
Oct 22 2018 10:59
yeah I ended up using map
I created a function that does this
const updateMediaState = curry((predicate, transform, collection) =>
  map(ifElse(predicate, transform, identity), collection)
);
and used it like this
      return evolve({
        [namespace]: {
          files: updateMediaState(
            propEq('id', payload.id),
            evolve({
              isUploading: T,
            })
          ),
        },
      })(state);
Riku Tiira
@rikutiira
Oct 22 2018 11:02
:thumbsup:
not sure how familiar you are with Ramda but you could also use R.over(R.lensPath([namespace, ‘files']), updateMediaState, state) if you are simply focusing to one thing
geganizharadzeatamido
@geganizharadzeatamido
Oct 22 2018 11:04
ah yeah I could do that. is that better than evolve though?
Riku Tiira
@rikutiira
Oct 22 2018 11:05
Not really, just a bit less verbose perhaps
geganizharadzeatamido
@geganizharadzeatamido
Oct 22 2018 11:05
cool, thanks :)
Johnny Hauser
@m59peacemaker
Oct 22 2018 22:23
I'm trying to get away from class "is-a" thinking and do something better like this https://medium.com/code-monkey/object-composition-in-javascript-2f9b9077b5e6
If this isn't a good place to ask questions about it, is there somewhere better I can find like-minded devs?
I'm wondering why he returns the object of functions and then assigns them to the state object externally rather than assigning the functions to the state object in the functions like canFight and such.
const canFight = (state) => ({
    fight: () => {
        console.log(`${state.name} slashes at the foe!`);
        state.stamina--;
    }
})
const canFight = (state) => Object.assign(state, {
    fight: () => {
        console.log(`${state.name} slashes at the foe!`);
        state.stamina--;
    }
})
I don't even know if this is a good paradigm. It's the best I know of so far.
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:17
@m59peacemaker It's odd, because FP to me is all about values that "are things". They don't "do" anything (there's usually no temporal element wrt which they can do anything), instead things that are one thing can be transformed into other things using pure functions. Interesting to see a different perspective though
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:18
That is the goal, but lower level... can that be true?
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:19
the extreme case of this is that even impure effects like opening files or making a web request are things, they don't do anything. an effect like opening a file can be transformed purely into a side effect that opens a file and then makes a web request with the url it contained
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:20
mostjs is theoretically a likeminded project, right?
Not only does it do all kinds of things, it's riddled with this keyword
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:20
in this case state.stamina-- and () => ... suggest that some state internal to the object is being mutated. i'm not very familiar with good practices with this sort of paradigm, maybe someone else can chime in
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:21
Ohhh, I didn't mean to get hung up on that
I wouldn't actually build the example that way
You would want to do something like that with all the cool functional things, sure.
But those functional things are things that do things.
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:23
in this case I would have values of some type PlayerState that contains a player's health and whatnot. Then a function slash :: Player -> PlayerState -> PlayerState can represent how a particular player slashing at another player affects their health
Ben Briggs
@ben-eb
Oct 22 2018 23:26
If you don't assign the functions to the state then it means you could continue to work with that state as a thing without those behaviours later on. In your second example that's not a pure function because you're overriding state (if you wanted a pure function you would do Object.assign({}, state, { fight }))
Brad Compton (he/him)
@Bradcomp
Oct 22 2018 23:27

I think in the end you'll end up with something similar to the Elm architecture / redux. A player slashing doesn't just effect that player, it effects the player and the thing being slashed (at the least). So one could generate an action

{
  action: 'slash',
  subject: playerOne,
  object: thatRopeOverThere
}

And then pass that into the game interpreter, which has functions to handle the slash action for both the player and the rope being slashed

Johnny Hauser
@m59peacemaker
Oct 22 2018 23:27
So, it's just to keep the mutation optional, is the point?
Ben Briggs
@ben-eb
Oct 22 2018 23:27
Yeah I think so
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:28
I'm rewriting my frp stream/observable lib and trying to find a better way to compartmentalize all the logic, btw
for context
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:28
it also keeps things extensible. the data doesn't have to be tagged up front with the entire universe of possible operations on it
there's a good talk about this sort of thing by Rich Hickey
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:28
There are multiple behaviors that make up what a stream is... I keep getting sucked into thinking in terms of classes and it gets ugly.
Brad Compton (he/him)
@Bradcomp
Oct 22 2018 23:29
I think in the original example, the functions canSlash and canCast or whatever are just meant to provide functionality, while it's up to the mage factory to actually build those functionalities into a Player. If canCast was returning the new modified state, it would, in some sense, be mixing up the concerns
Ben Briggs
@ben-eb
Oct 22 2018 23:30
@masaeedu Thanks, saved for later :+1:
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:30
@m59peacemaker One alternative approach is to define a stream independently of all the operations possible on it
in other words, what is a good mathematical/programmatic/philosophical representation of a stream?
the operations possible on it often fall out nicely from that
don't want to spam too many talks, but here's another one about this style of design (called "denotational design"): https://www.youtube.com/watch?v=bmKYiUOEo2A
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:33
Where I am especially hitting a dead end is that it makes sense to me to put the propagation logic in one thing, like canPropagate, which returns the functionality necessary to propagate changes....
{ registerDependant, getDependants, propagate }
the dependants have to be things that have that functionality also
All that works well enough and is simple to me
The ironic problem is that the very stream trying to propagate can't actually exist / be referenced during propagation
Brad Compton (he/him)
@Bradcomp
Oct 22 2018 23:35
@masaeedu I've been meaning to learn more about denotational design!
Thanks for posting that
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:36
The thought just occurred to me, that I can solve this by having propagate take an argument of the stream doing the propagating
kinda awkward, though, right?
stream.propagate(stream)
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:37
@m59peacemaker I think there's a bit too much of a domain knowledge gap to follow what you mean. Is it possible to specify the type of these functions?
are registerDependant etc. from the article you linked?
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:39
const canPropagate = (state) => {
  const propagate = () => {
    state.dependants.forEach(dependant => {
      dependant.emit('propagation', { sourceStream: WTF })
    })
  }
  return {
    propagate
  }
}
I think that demonstrates the issue
There's no way for this function to know what it is being applied to
like sourceStream: this
Since I believe this is a massive code smell... I don't want to go that route
But does it make sense why I end up there?
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:43
i'm still not 100% sure what is being propagated here, but you can always accept more parameters where you don't have enough information
const canPropagate = state => foo => bar => baz => ...
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:44
const a = Stream()
const b = combine (Add1CombineFn) ([ a ])
Stream.set(123)
b.get() // 124
behind the scenes, combine will do a.registerDependant(b)
and then a.on('set', a.propagate) will get the propagation event to b so it can update
It's necessary for a to be in charge of this for purposes of topological sorting and some other complex stuff
Asad Saeeduddin
@masaeedu
Oct 22 2018 23:47

ok, so Stream is some sort of thing for which the following operations can be defined:

combine :: ? -> [Stream] -> Stream
set :: Stream x -> a -> m ()
get :: Stream a -> m a

I am speculatively assuming the proper way to model the fact that Stream.set and b.get are side effects with a temporal dependency is a monad

i'm not sure what sort of thing Add1CombineFn is
Johnny Hauser
@m59peacemaker
Oct 22 2018 23:47
eh, it was arbitrary
My thing is very similar to flyd
I really like flyd, but the code is spaghetti and there are some API decisions that kill it for me. A little too much magic, like dealing with promises, and how streams are getters or setters depending on whether you pass an argument.