These are chat archives for ramda/ramda

17th
Feb 2017
Rick Medina
@rickmed
Feb 17 2017 00:02
@galileopy not there yet in terms of what? it depends on why do you want to learn it

I'm reading into composeP, is that something like compose with auto liftiing to promises?

yes. Although, I highly recommend to use tasks/futures and composeK if interested

Johnny Hauser
@m59peacemaker
Feb 17 2017 00:08
I fixed it, but it's not as pretty. It merges rather than overwrites the lensed object
const moveProps = (toMove, fromLens, toLens) => converge(
  (result, input) => over(toLens, merge(__, result))(input),
  [
    pipe(view(fromLens), pick(toMove)),
    over(fromLens, omit(toMove))
  ]
)
Johnny Hauser
@m59peacemaker
Feb 17 2017 00:19
What's the function that does (...args) => args ?
Galileo Sanchez
@galileopy
Feb 17 2017 00:25
@rickmed I usually just like learning, and not there yet means that I don't feel ready yet, and I don't have a week long holiday to just start reading about it non stop and then try to make use of it.
also, I feel like I will better learn it if I am a bit more familiar with haskell, since by the looks of it haskell has a lot of this concepts embedded, so having a way to learn abstract stuff is greatly improved if you can somehow materialize and play with those abstractions.
Bravi
@Bravilogy
Feb 17 2017 00:38
@ram-bot
const obj = {
    id: 1,
    active: true,
    name: 'J. Smith'
};

const addType = chain(compose(type, last), append);
const objToList = compose(map(addType), toPairs);
objToList(obj);
ram-bot
@ram-bot
Feb 17 2017 00:38
[ [ 'id', 1, 'Number' ],
  [ 'active', true, 'Boolean' ],
  [ 'name', 'J. Smith', 'String' ] ]
Bravi
@Bravilogy
Feb 17 2017 00:41
hmm
it doesn't work on my local
is rambot running on a different version or something? :D
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 00:48
R.unapply
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 00:48
R.apply
Johnny Hauser
@m59peacemaker
Feb 17 2017 00:50
I was thinking I could use it to improve this, but probably not
 (result, input) => over(toLens, merge(__, result), input),
Denis Stoyanov
@xgrommx
Feb 17 2017 00:51
unapply and apply is isomorphic)
Bravi
@Bravilogy
Feb 17 2017 00:52
guys, is there a reason why rambot is working for that example and repl is not?
Rick Medina
@rickmed
Feb 17 2017 00:57
@galileopy if you are interested in the math background, go for it. If you are interested in the programming applications there is no need (at least not for now) to go directly into the math stuff. I consider myself a newbie in all of this, but my experience was that when I started learning haskell I grabbed it relatively quickly because many concepts I already was familiar with from applying them in js. I'm not sure if I would have been a bit overwhelmed otherwise. I'd learn the fundamental concepts ("pure" functions, currying, partial application, algebras, its laws; why are they important, what problems they solve...)
Denis Stoyanov
@xgrommx
Feb 17 2017 00:57
I love haskell and other FP languages
Bravi
@Bravilogy
Feb 17 2017 00:58

hmm looks like for rambot, the correct syntax of chain is

chain(compose(type, last), append);

and for repl

chain(append, compose(type, last));
Denis Stoyanov
@xgrommx
Feb 17 2017 00:59
Function also monad
Johnny Hauser
@m59peacemaker
Feb 17 2017 01:25
I can never let things go, gotta learn!
I'm exploring the possibility of taking (result, input) and doing something such that a function is called with result and then the return value is called with input
Johnny Hauser
@m59peacemaker
Feb 17 2017 01:48
I got rid of one that way
const oneArgThing = (fn) => {
  return (...args) => args.reduce((fn, arg) => fn(arg), fn)
}

   (result, input) => over(toLens, merge(__, result), input)
oneArgThing(result => over(toLens, merge(__, result)))
Johnny Hauser
@m59peacemaker
Feb 17 2017 04:24
How's this? https://goo.gl/XpP83J
const mergeProps = (props, dest) => converge(
  R.set(dest),
  [
    data => mergeAll(props.map(prop(__, data))),
    omit(props)
  ]
)

var data = {
  foo: {a: 'a', b: 'b'},
  bar: {c: 'c'},
  qux: {x: 'x'}
}

mergeProps(['foo', 'bar'], lensProp('merged'))(data)
/* {
  "merged": {"a": "a", "b": "b", "c": "c"},
  "qux": {"x": "x"}
} */
Keith Alexander
@kwijibo
Feb 17 2017 04:52

@m59peacemaker I meant

({foo: {a , b, c, d}}) => ({foo: {a, b}, qux: {c, d}})

it depends whether you need to generalise it

Johnny Hauser
@m59peacemaker
Feb 17 2017 04:58
Yeah, it has to be more dynamic for my uses.
There may be properties I'm not aware of in advance
Keith Alexander
@kwijibo
Feb 17 2017 06:25
this isn't very fancy and doesn't try to be point free, but it's how I'd do it https://goo.gl/0bXoBo
Bravi
@Bravilogy
Feb 17 2017 09:42
guys, is chain like a reducer of sorts if I pass multiple functions to it?
I'm trying to understand how exactly does it handle multiple functions
Aldwin Vlasblom
@Avaq
Feb 17 2017 09:49
I think chain will "flat map" the function on the right using the function on the left: https://github.com/sanctuary-js/sanctuary-type-classes/blob/master/index.js#L792-L796
Mick Dekkers
@mickdekkers
Feb 17 2017 14:23
I have a rather interesting problem which I'm not sure how to approach
I have an object representing a set of options; their keys and an array of their allowed values
const options = {
  // These options must always be one of their values; they cannot be omitted
  required: {
    actual: [true, false]
  },
  // These options can also be omitted
  optional: {
    station: ['Foo'],
    unplanned: [true, false]
  }
}
I want to produce an array of every combination of the options in this format:
[
  {
    actual: true,
    station: 'Foo',
    unplanned: true
  },
  {
    // note how the station property is not included here, since it is optional
    actual: true,
    unplanned: true
  },
  {
    actual: false,
    unplanned: true
  },
  {
    actual: false,
    station: 'Foo'
  }
  // etc.
]
I'm hoping someone here can point me in the right direction
I'd like to use this output to thoroughly test a function which takes an options object
Mick Dekkers
@mickdekkers
Feb 17 2017 14:32
If I'm looking at this right, the input above should produce an array of length (2 (actual true/false) * 2 (station 'Foo'/omitted) * 3 (unplanned true/false/omitted)) = 12
Galileo Sanchez
@galileopy
Feb 17 2017 14:34
@SoullessWaffle is that to produce a list of possible options for an end user? or you plan to use it for something like validation?
Mick Dekkers
@mickdekkers
Feb 17 2017 14:35
@galileopy I'd like to use it in my tests, essentially for validation yeah
well more accurately to test a poorly documented API and inspect its response (while respecting rate limits naturally)
Galileo Sanchez
@galileopy
Feb 17 2017 14:38
oh, so is a list of end options for the test then. :), I don't have something in mind, just wanted to know if there was a way to avoid the combinatoric problem, I always avoid combinatoric problems, in this case there is no way around that, since you want to generate all possible options
perhaps, start simple, like a function that takes a list of possible values and return its combination, and go up from there
Mick Dekkers
@mickdekkers
Feb 17 2017 14:39
Right, that's what I was thinking
Problem is I'm not sure how to even start as I've not done anything with combinatorics before, especially in a functional programming style
Galileo Sanchez
@galileopy
Feb 17 2017 14:40
so for one required field you will have something that will produce [{actual: true}, {actual: false}]
for optional you will return [{station: "Foo" }, {}], [{unplanned: true}, {unplanned: false}, {}]
notice that optional returns an empty object always
then you just need some sort of cross product there
and mergeAll
perhaps using the obscure ap(Array) that gives you a cross product
Mick Dekkers
@mickdekkers
Feb 17 2017 14:45
interesting
Galileo Sanchez
@galileopy
Feb 17 2017 14:49
yep, i think this could be a step in the right direction
@ram-bot
const options = [{station: "Foo" }, {}, {unplanned: true}, {unplanned: false}, {}]
const required = [{actual: true}, {actual: false}]
const merger = lift(merge);
merger(options, required)
ram-bot
@ram-bot
Feb 17 2017 14:50
[ { station: 'Foo', actual: true },
  { station: 'Foo', actual: false },
  { actual: true },
  { actual: false },
  { unplanned: true, actual: true },
  { unplanned: true, actual: false },
  { unplanned: false, actual: true },
  { unplanned: false, actual: false },
  { actual: true },
  { actual: false } ]
Galileo Sanchez
@galileopy
Feb 17 2017 14:51
it needs some work so to process a list of lists for options,
I also omitted how to get the options and requirements array, since that seems pretty trivial
Drew
@dtipson
Feb 17 2017 14:53
there may be a more specific lens pattern for that sort of complex update operation, though it might require some things ramda doesn't support yet: https://github.com/calmm-js/partial.lenses#tutorial
Mick Dekkers
@mickdekkers
Feb 17 2017 15:05
Thanks @galileopy, that should get me started
@dtipson are you talking about the issue I was talking about?
As a side note, does anyone know if the ramda repl has an auto-tidy option for the output? I'd like it to do that when the output changes
Markus Pfundstein
@MarkusPfundstein
Feb 17 2017 15:12
you can erase the console .. forgot how though
Keith Alexander
@kwijibo
Feb 17 2017 16:01
Is there a less bespoke way to do this in ramda? ie, sort the keys in obj using an array of keys ks
const ks = ['a', 'b','c']
const obj = {c: 3, a: 2, b: 8}
const x = reduce((r, i)=>assoc(i, obj[i], r),{}, ks)
toPairs(x)
I find I tend to just use reducefor everything instead of looking for more specific functions
Stefano Vozza
@svozza
Feb 17 2017 16:04
the universality and expressiveness of fold...
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 16:06
R.pick
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 16:06
That will do it, but it's kind of an implementation detail
@ram-bot
const ks = ['a', 'b','c']
const obj = {c: 3, a: 2, b: 8}

toPairs(pick(ks, obj))
ram-bot
@ram-bot
Feb 17 2017 16:07
[ [ 'a', 2 ], [ 'b', 8 ], [ 'c', 3 ] ]
Keith Alexander
@kwijibo
Feb 17 2017 16:29
:+1:
is there a function that will add default values to an object if the key doesn't already exist?
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 16:30
I generally use merge(defaults)
Keith Alexander
@kwijibo
Feb 17 2017 16:31
cool thanks
i thought evolve might do it, but no ...
Bravi
@Bravilogy
Feb 17 2017 17:45
do you guys use folktale stuff?
I was trying to use .cata method on Validation and it seems to be deprecated
I'm not sure how to extract the data now :/
.get and .getOrElse are not quite what I need
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 17:56
.fold?
I use data.either and data.maybe and I think fold is the method I use
Bravi
@Bravilogy
Feb 17 2017 17:57
hmm I'm not sure how different is Validation from Maybe
confusing stuff :D
ah yes fold seems to be working
thanks!
Rick Medina
@rickmed
Feb 17 2017 18:38
@Bravilogy I think there is an article on folktale's docs explaining the usefulness of several of its types
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 18:47
R.pathOr
Michael Reinig
@MReinigJr
Feb 17 2017 20:44
Hello, I am trying to .fork a series of futures based upon the results in an array. The array looks like ['123', '456', '789'] and this what is returned from listEmails below. I initially had getEmails as getEmail and would only accept a single orderId instead of orderIds, where orderIds is an array. When getEmails was getEmail, I would get back something like [Future { _fork: [Function] }, Future { _fork: [Function] }] after .map(R.map(...)) over the results from listEmails. The only way I found to call each future individually was to .fork again in the onResolve callback accepted by the first .fork. It seemed very clunky this way. This is what lead me to the code below. What this essentially does is takes the array of Ids from listEmails, and then processes each resolve or reject for each ID individually. So a single failure will blow through the SetUpOrderFutures to the onReject callback, and if getEmails resolves for a particular ID, then that result continues through the rest of the function, but again, it goes through individually. Not as an array like listEmails returned. It works great and I have tested it thoroughly, but the R.chain in getEmails really kind of confuses me as to why it works. To further my confusion, exchanging R.chain with R.map still works. Looking at the type signatures of each, this kind of makes sense why they can be used interchangeably under the given circumstances, more specifically because they dispatch to Future's implementation of each. So time for my real question here. Why does R.chain work as I have described in getEmails? The more I think about it, the stranger this seems to me, because I essentially take a single future e a, and then turn it into future e b. Where b is a list of futures that then get completed individually. The strange part is that SetUpOrderFutures waits for each future resulting from each id that is processed to return. A bit mind boggling how this all comes about. Thank you so much in advance.
const listEmails = R.curry((gmailApi, query, oauth2Client) => {
  return Future((reject, resolve) => {
    gmailApi.users.messages.list({
      "auth": oauth2Client,
      "userId": "me",
      "q": query}, (err, response) => {
        err ? reject("Gmail Error: " + err) : resolve(response.messages);
      }
    )
  });
});

const getEmails = R.curry((gmailApi, orderIds, oauth2Client) => {
  return Future((reject, resolve) =>
    R.map(orderId =>
      gmailApi.users.messages.get({
        "auth": oauth2Client,
        "userId": "me",
        "id": orderId,
        "format": "full"
      }, (err, response) => {
        err ? reject("Gmail Error: " + err) : resolve(response);
      }), orderIds
    )
  );
});

const SetUpOrderFutures = listEmails(gmail, "label:test", auth)
  .chain(orderIds => getEmails(gmail, orderIds, auth))
  .map(email => email.payload.body.data)
  .map(emailBody => Buffer.from(emailBody, 'base64'))
  .fork(console.log, console.log);
Stefano Vozza
@svozza
Feb 17 2017 21:42
If you map over the list ids and turn them into futures with getEmails you could use sequence to turn it into a Future Array
Not sure if that makes it easier to work with
Michael Reinig
@MReinigJr
Feb 17 2017 22:01
Hi Stefano! Thank you. That might make it easier to rationalize. Will a single reject cause them all to fail? There is a similar example in the docs using R. traverse, but a single reject causes all to reject. Are you able to explain why the R.chain works as I have described? It is confusing how it is able to execute each Future independently.
Stefano Vozza
@svozza
Feb 17 2017 22:26
actually you're right, it will cause them all to fail
i am surprised that chain works the way it does
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 22:33
It's possibly the result of chain having relatively weird behavior for arrays, and nothing to do with the Futures
Michael Reinig
@MReinigJr
Feb 17 2017 22:33
@svozza you and me both. It kind of reminds of promise.all, but again, each future executes independently. It works out great! Just confusing how/why it does.
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 22:34
@ram-bot
chain(inc, [1, 2, 3])
ram-bot
@ram-bot
Feb 17 2017 22:34
[ 2, 3, 4 ]
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 22:34
@ram-bot
map(inc, [1, 2, 3])
ram-bot
@ram-bot
Feb 17 2017 22:34
[ 2, 3, 4 ]
Michael Reinig
@MReinigJr
Feb 17 2017 22:36
Hi Brad! Yes, this is exactly what I am seeing. The interchangeability of map and chain in this situation. That is a source of my confusion, but based on the type signatures, it does make sense. My biggest hangup is what @svozza was discussing and @kwijibo identified when I first posted this question is the R.chain in the getEmails.
@Bradcomp and @svozza, in the ramda docs, it does mention the implementation is used for the second argument when applicable for map and chain. So this applies to the case when Futures are used. This is so far the best rational I have been able to come up with.
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 22:43
const SetUpOrderFutures = listEmails(gmail, "label:test", auth)
//Future e ([messages])
  .chain(orderIds => getEmails(gmail, orderIds, auth))
//Future e [Future e Email]
  .map(email => email.payload.body.data)  //<= Does this work?
  .map(emailBody => Buffer.from(emailBody, 'base64'))
  .fork(console.log, console.log);
Michael Reinig
@MReinigJr
Feb 17 2017 22:46
I will check...I have a few mocha tests set up for getEmails and listEmails if you would like to see them.
wait, it does not look like you changed my code. If you made no changes, then yes it works.
A single resolve that was processed out of getEmails will hit '.map(email => email.payload.body.data)'. If it rejects, it goes straight to the console.log in the fork.
So each resolve out of getEmails completes the reset of the composed functions independently.
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 22:52
OOOhhhhhhh I just saw what I missed before
You are, in essence, using the Future as a stream
Michael Reinig
@MReinigJr
Feb 17 2017 22:53
And I am not sure if you have this correct Future e [Future e Email] I think it actually may be more like this //Future e (Future e [Future e Email]).
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 22:53
As each request completes, it's getting passed into the same future
Michael Reinig
@MReinigJr
Feb 17 2017 22:53
YES!!!
Then that original Future completes after the others do.
This seems strange to me how it just works that way for async operations. Have you seen this before?
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 22:57
No. I've only used a Future to store a single asynchronous value, not a stream.
It's probably due to the fact that the Future doesn't cache
Michael Reinig
@MReinigJr
Feb 17 2017 22:59
So what would you expect to happen if it did cache?
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 23:00
I would expect you to just get the first email back
Michael Reinig
@MReinigJr
Feb 17 2017 23:01
interesting...if you had to implement something like this, how would you have gone about it. I am trying to get a better handle on how this actually works. Because if this works for the future monad, I can potentially see it working this way for other monads as well.
By the way, thank you so much for the insight. Thinking of it as a stream is very interesting and helpful.
Keith Alexander
@kwijibo
Feb 17 2017 23:12
@MReinigJr not sure if it helps, but instead of getEmails, write a getEmail :: orderID -> Future Error Email function, then you can write
pipe(
listEmails, //=> Future([id])
chain(traverse(Future.of, getEmail)), //=> Future([email])
map(map(
    email =>  Buffer.from(email.payload.body.data, 'base64'), 
)) //=> Future([Buffer(data)])
)().fork(console.error, console.log)
ah sorry, you're deliberately doing it that way to fire off the getEmail requests separately
that's pretty cool
Michael Reinig
@MReinigJr
Feb 17 2017 23:18
Hi @kwijibo thanks for the reply. Exactly. I do not want a single failed email retrieval to cause it to reject them all. It awesome how simple it was to implement what I was looking for, I just wish I could rationalize it better. :)
Keith Alexander
@kwijibo
Feb 17 2017 23:21
yeah it sort of feels like there should be some kind of abstraction to capture that - it's like chainEach or something
Michael Reinig
@MReinigJr
Feb 17 2017 23:21
@Bradcomp I am not so sure if a stream is accurate here now that I am pondering more on this comment of yours. I think that is a fantastic comparison and makes understanding what is happening easier and I do think there are many parallels. However, I do not think it would work out the way I am describing it currently working if it was a stream. How each future resulting from an ID implements the rest of the composed functions is what I am struggling to wrap my head around. I wonder if since both Future.chain and Future.map both return Futures, by returning a response or reject, it it is still comes back as a Future. And that is why it completes the rest of the composed functions. I may be reaching here.
Keith Alexander
@kwijibo
Feb 17 2017 23:24
@MReinigJr a Future is basically just the (reject, resolve)=> function you pass into the constructor, right? calling fork calls this function - you can think of the function as being fork
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 23:24
So each time a request resolves it gets flattened by chain and passed through the rest of the functions. Because Futures are lazy and don't cache, you can treat each resolution as an independent completion of the rest of the functions, and the array just disappears
Michael Reinig
@MReinigJr
Feb 17 2017 23:25
@kwijibo that is a really interesting way of thinking of it. Would the type signature be similar to forEach? If so, I think that would work. I wonder if R.forEach could work in this instance.
Keith Alexander
@kwijibo
Feb 17 2017 23:25
and when you call .map or .chain you're just composing functions into the fork
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 23:25
:point_up: forEach is kind of what I was thinking of too
Keith Alexander
@kwijibo
Feb 17 2017 23:26
so if you do Future.map(f).map(g), you're getting essentially something like (reject, resolve) => resolve(g(f(x)))
in your case, where you're doing map or chain inside the fork, you could actually be using forEach
you're just doing some effectual iteration - the fork function doesn't return the value of the future
so you're just calling this composed callback with each item in your list
Michael Reinig
@MReinigJr
Feb 17 2017 23:29
@kwijibo I do not think that Future.map(f).map(g) is correct. I could be wrong. I have only been working with Futures for a few days now. If the future rejects, the following maps will not run. Only the onReject function provided in the .fork will run.
However, I think you are right about the forEach. I am going to give that a go.
Keith Alexander
@kwijibo
Feb 17 2017 23:30
yes, the resolve composition only runs on success
I'm not sure what you're getting at?
Michael Reinig
@MReinigJr
Feb 17 2017 23:31
you are right that fork does not provide a return value. I can not do const result = futureFunction.map(g).map(f).fork(onReject, onResolve).
Keith Alexander
@kwijibo
Feb 17 2017 23:33
when i say (reject, resolve) => resolve(g(f(x))) - this is a simplification of something like
(reject, resolve) => someAsyncFunc((err, data) => err? reject(err) : resolve(g(f(data))))
but obviously the inner working of each fork function will be slightly different depending on the API of the async thing you are calling
your getEmails is structurally similar to
new Future((rej, res) => {
   res(1)
   res(2)
   rej("bad")
   res(3)
}).fork(console.error, console.log)
// 1 
// 2
// Error: bad
// 3
and calling map(f).map(g) on it is equivalent to
new Future((rej, res) => {
   res(g(f(1)))
   res(g(f(2)))
   rej("bad")
   res(g(f(3)))
}).fork(console.error, console.log)
Michael Reinig
@MReinigJr
Feb 17 2017 23:37
@kwijibo Yes! Can you please explain this a bit more? This is where my confusion is?
Keith Alexander
@kwijibo
Feb 17 2017 23:39
which is equivalent to
console.log(g(f(1)))
console.log(g(f(2)))
console.error("bad")
console.log(g(f(3)))
Michael Reinig
@MReinigJr
Feb 17 2017 23:40
@kwijibo Yes! on your edit and additional response. I am following you now what you meant earlier.
Denis Stoyanov
@xgrommx
Feb 17 2017 23:40
chain(inc)([1,3,4]) -> [2,4,5] what?! this is wrong
Michael Reinig
@MReinigJr
Feb 17 2017 23:40
@kwijibo If I am understanding this correctly, this is an excellent breakdown.
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 23:41
@xgrommx :hand: javascript :hand:
Denis Stoyanov
@xgrommx
Feb 17 2017 23:41
[1,2,3] >>= (+1) <- error!
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 23:41
:stuck_out_tongue:
Denis Stoyanov
@xgrommx
Feb 17 2017 23:41
uglyscript
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 23:41
I know it's wrong, one of those oddities
R.chain
Keith Alexander
@kwijibo
Feb 17 2017 23:42
@MReinigJr I found it really helped me understand Tasks/Futures to try to write a very simple implementation
const Task = fork => ({
   map: f => Task((reject, resolve)=>{ fork(reject, x => { resolve(f(x)) })  }),
   chain: f => Task((reject, resolve)=>{ fork(reject, x => { f(x).fork(reject, resolve) })  }),
   fork
})
Denis Stoyanov
@xgrommx
Feb 17 2017 23:42
@ram-bot
chain(add)(inc)(10)
ram-bot
@ram-bot
Feb 17 2017 23:42
monad.call(...).apply is not a function
Keith Alexander
@kwijibo
Feb 17 2017 23:43
if you try and write it out, you see that Future is just a mechanism for composing with these fork shaped functions
it's just a variation on ordinary function composition, which is pretty cool
Future.map composes the fork with an unary function, Future.chain composes it with another fork-shaped function
Michael Reinig
@MReinigJr
Feb 17 2017 23:45
@kwijibo it really is! I am not as comfortable with recursion as I would like to be. I am studying your "simple" implementation. :)
Denis Stoyanov
@xgrommx
Feb 17 2017 23:45
@ram-bot
var S = x => y => z => x(z)(y(z))
var K = x => y => x
var B = S(K(S))(K)
var B1 = B(B)(B)
var W = S(S)(S(K))
var monadFn = B1(W)(B)

monadFn(add)(inc)(10)
ram-bot
@ram-bot
Feb 17 2017 23:45
21
Bravi
@Bravilogy
Feb 17 2017 23:46
how about having ramda katas?
like on codewars.com
but specifically for ramda / fp
Bravi
@Bravilogy
Feb 17 2017 23:48
:D
so I need to learn Haskell now then
okay.. here we go
Brad Compton (he/him)
@Bradcomp
Feb 17 2017 23:49
Or just do the problems in JS
;)
Bravi
@Bravilogy
Feb 17 2017 23:49
but then I wouldn't know the better solution in js
Drew
@dtipson
Feb 17 2017 23:49
fork can provide a return value, but only a synchronous one. Usually it's built such that the return value is a nullary function that controls the original effect, such as cancelation
It isn't a value
Michael Reinig
@MReinigJr
Feb 17 2017 23:54
@kwijibo can you describe your map implementation some? I am getting lost a bit on how your map is being applied to something.
@kwijibo maybe just the type signature of map would be all that is needed to help clear somethings up for me.
@kwijibo is it the same map signature we are used to seeing?