These are chat archives for ramda/ramda

14th
Feb 2016
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:00
Imo fp is mostly about referential transparency
declarative rather than imperative code
immutability, etc
callbacks return values, just like how fs.readFile returns either an err or a value
the category theory friendly signature of fs might look like: fs.readFile(Either a) => Left error or Right value
Scott Sauyet
@CrossEye
Feb 14 2016 00:04
Don't understand that sig, but a number of people have been talking about cycle. I'll have to check it out.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:09
Sorry, forgot to add path
The readFile signature in NodeJS is fs.readFile(file[, options], callback), where callback takes (err, data) as params
Since it will only return either err or data but not both, you could describe this using the Either type, so instead of (err, data), it becomes Either a, which would be pattern matched to Left err or Right data.
Scott Sauyet
@CrossEye
Feb 14 2016 00:16
Right. I was noting that readFile itself doesn't have a meaningful return. That is what seems odd to me from an FP perspective.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:23
Why?
Scott Sauyet
@CrossEye
Feb 14 2016 00:28
As I said, FP is about values. A function that doesn't return a value is an odd beast.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:42
Maybe from a traditional point of view from how functional languages evolved
But JS is event driven, and turning it into anything else is an odd beast. I think cyclejs is the right approach to fp in js
The main selling point to me for fp is that it's referentially transparent. If you say x = somevalue, then when you go to fetch x, it better be that value
I suppose it's a bit unusual because traditionally (ie lisp) you would manipulate a list recursively with a function to return a new one, etc
Can't really do that with event driven functions
Casey Watson
@watsoncj
Feb 14 2016 00:50
Is there a way to filter by index?
Scott Sauyet
@CrossEye
Feb 14 2016 00:50
I've still only looked at cycle's front page, and it definitely looks worthy of a deeper investigation, but I don't share your enthusiasm for thinking of callbacks as Referentially transparent...
R.addIndex
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:52
In all honesty I've only been playing with the idea in my head for a little while, and I still haven't fully explored cyclejs either
Scott Sauyet
@CrossEye
Feb 14 2016 00:52
Nice, the bot is working. So addIndex(filter)((val, idx) => idx % 2 == 0, list) should give you the even-indexed elements of the list.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:53
Oh that's cool
my gitter isn't picking up on the 🔍 symbol though
Scott Sauyet
@CrossEye
Feb 14 2016 00:56
Mine isn't either (in the bot output, you mean?) That's just a link to the source code.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:57
yeah, but i'm sure it's supposed to come up as some kind of symbol
it's not λ
Scott Sauyet
@CrossEye
Feb 14 2016 00:57
Yeah, think it's a magnifying glass. Saw it earlier. Don't know why it broke.
Ah, works on my mobile, but not my desktop.
Mobile is Android Chome(ish?), Desktop is Ubuntu Firefox.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:58
should be :mag: or :mag_right:
Scott Sauyet
@CrossEye
Feb 14 2016 00:58
The former.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 00:58
:mag: or :mag_right:
yeah firefox here also
haven't tried chromium
Scott Sauyet
@CrossEye
Feb 14 2016 01:00
yeah, not working in Chromium either.
So @watsoncj, before we went off track on the :mag:, that was for you.
const list = ['a', 'b', 'c', 'd', 'e'];
addIndex(filter)((val, idx) => idx % 2 == 0, list); //=> ['a', 'c', 'e']
Casey Watson
@watsoncj
Feb 14 2016 01:04
Thanks @CrossEye !
David Chambers
@davidchambers
Feb 14 2016 03:07
R.head
David Chambers
@davidchambers
Feb 14 2016 03:07
S.head
@raine, any chance we could have ram-bot work for Sanctuary functions as well?
@ram-bot S.head([1, 2, 3])
ram-bot
@ram-bot
Feb 14 2016 03:09
Just(1)
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 03:27
@ram-bot S.tail([1,2,3])
R.tail
Hardy Jones
@joneshf
Feb 14 2016 03:29
whoa
what is @ram-bot ?
David Chambers
@davidchambers
Feb 14 2016 03:30
It's @raine's latest creation: raine/ram-bot.
Hardy Jones
@joneshf
Feb 14 2016 03:31
That's awesome
hmm
Does it work for everyone?
David Chambers
@davidchambers
Feb 14 2016 03:33
Seems to have crashed. :\
James Forbes
@JAForbes
Feb 14 2016 03:38
@Risto-Stevcev are you saying that the arguments the readFile callback receives, are essentially the return value of the readFile function, despite the indirection?
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 03:42
@JAForbes Yeah, basically
A part of functional programming is the declarative nature, or writing it out like you would a mathematical function
There's nothing inherently mutable about that style of programming
It's basically unheard of to have a library give you a result from a callback and have it change halfway through you doing something with it, without you having changed it yourself
James Forbes
@JAForbes
Feb 14 2016 04:58

readFile given the same arguments can pass completely different values to the callback though.

Also hard to see how a callback is referentially transparent. Because you can't write callback based code without a callback.

What do you mean by referentially transparent? Because, my understanding is, it means you can take a function body and stick it right in the call site, and the code will be equivalent.

e.g.

var increment = (a) => a + 1

var b = increment(0)

// can be inlined to the body of increment
var b = 0 + 1

But a callback:

readFile('path', function(err, data){
  //...
})

can not be inlined

Also, not seeing the connection with cyclejs. I'm curious what you mean. It's interesting to think of a callback inputs args as a return value.

The thing with the IO Monad (as far as I know, I'm not expert at all, like at all), is that the side effects are contained. It's really returning a pathway, that will be executed at a later time. So a function that returns an IO Monad can be pure in a way that callback code can't.

Risto Stevcev
@Risto-Stevcev
Feb 14 2016 05:22
My understanding is the IO Monad makes a lazy language pure. If the language is strictly evaluated, then it doesn't necessarily make it any more pure than something like IO returning an Either type: let eitherErrorOrContent = readFile(handle)
The monad abstraction, like you said, lets it return a pathway that can be executed later
From what I can see, that abstraction doesn't necessarily make strict languages more pure
readFile as a haskell monad can also return completely different values. Just because it encapsulates IO in a Monad, doesn't mean it doesn't have side effects. It just means those side effects are contained.
Likewise, with a callback approach, the side effects are contained. The callback function can be isolated from the actual listener itself, such as: someListener(someCallback), with someCallback existing in another place, and completely independent of any global variables, or mutable state
someCallback can effectively exist as a pure function, outside of someListener
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 05:27
A callback can also be inlined
increment (Left error) = throw error
increment (Right a) = a + 1
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 05:32
fs.readByteString(increment)
But not having a return statement doesn't mean you have a computation context. It could just trigger other events with whatever the function computed.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 05:49
It relates to cyclejs in the form of observables
I still haven't fully read through how cyclejs works, but it looks like its in the direction of my thinking
The callback approach is also the observer pattern "don't call us, we'll call you". This is really beneficial from the point of view of keeping code decoupled and modular, since each modular piece of code keeps track of its own internal state, and notifies listeners of any changes.
Keith Alexander
@kwijibo
Feb 14 2016 07:19
nicely decoupled modularity is not the most common experience with js callback code...
kwijibo @kwijibo not following @Risto-Stevcev's argument
Keith Alexander
@kwijibo
Feb 14 2016 07:50
Cycle.js seems much more like ramda-fantasy's IO monad than it does fs.readFile
Observables have a return value
and it looks to me like cycle.js is explicitly separating the declaration of Effects (via Drivers and Observables) from their execution, in Cycle.run
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 07:54
@kwijibo That's because callback functions often update and reference variables outside of the function itself, and are therefore mutable.
@kwijibo I haven't looked at cyclejs entirely, but if thats the case then you're right, and I still haven't fully explored my own concept. It just seems like the event driven approach is not necessarily undesirable for reasons mentioned, and could potentially be written decoupled
I haven't explored purescript's Eff monad really either
Maybe there's a better case to use Monads for that. It just seems awkward because listeners don't follow some execution order, but monads do
Keith Alexander
@kwijibo
Feb 14 2016 08:07
@Risto-Stevcev if you read Staltz's http://staltz.com/why-react-redux-is-an-inferior-paradigm.html, he argues that his Cycle.js is a better abstraction than Redux because it is explicit about keeping I/O architecturally separate
Keith Alexander
@kwijibo
Feb 14 2016 08:14
In javascript land, I/O is all going to be events and callbacks (and local mutation) under the hood, but the consensus seems to be that this is too low level, and higher level abstractions are needed to write decoupled code. AIUI, Observables, IO & Future Monads, etc are trying to provide abstractions to make decoupling easier.
Risto Stevcev
@Risto-Stevcev
Feb 14 2016 09:24
@kwijibo I think I'm just explaining myself badly. I really think what I'm talking about is cyclejs, and that cyclejs just decorates a function that has a return, but under the hood the returned object is used to emit to a listener or set of listeners
I'm also talking about keeping IO separate
I haven't had the time to look at cyclejs or functional reactive programming because I'm working to get something done asap, but I have a feeling that this is what my intuition is
I had a similar discussion about dependent types to someone, and he thought my idea was the dumbest thing he ever heard of. I think I didn't explained myself well back then either, but I left that conversation doubting myself, only to find out about dependent types like a year or more later when I was looking about haskell and functional stuff
Raine Virta
@raine
Feb 14 2016 09:42
@joneshf yeah works for everyone, it just has trouble recognizing new users. gitlab needs to add an endpoint for fetching a single user's data to fix that
David Chambers
@davidchambers
Feb 14 2016 09:48
@ram-bot S.tail([1, 2, 3])
ram-bot
@ram-bot
Feb 14 2016 09:48
Just([2, 3])
Michael Hurley
@buzzdecafe
Feb 14 2016 13:05
@ram-bot Maybe.Just(1)
hmmm
Raine Virta
@raine
Feb 14 2016 13:05
needs to look like code
Michael Hurley
@buzzdecafe
Feb 14 2016 13:05
aaa thx
Raine Virta
@raine
Feb 14 2016 13:05
so backticks or ```
Michael Hurley
@buzzdecafe
Feb 14 2016 13:05
@ram-bot Maybe.Just(1)
ram-bot
@ram-bot
Feb 14 2016 13:05
ReferenceError: Maybe is not defined
Michael Hurley
@buzzdecafe
Feb 14 2016 13:06
so ramda-fantasy not included i guess
Raine Virta
@raine
Feb 14 2016 13:06
should I add ramda-fantasy?
Michael Hurley
@buzzdecafe
Feb 14 2016 13:06
sure!
@ram-bot R.map(R.multiply(200), S.Just(10));
ram-bot
@ram-bot
Feb 14 2016 13:07
Just(2000)
Michael Hurley
@buzzdecafe
Feb 14 2016 13:07
sweet
@ram-bot R.map(R.add(1))
ram-bot
@ram-bot
Feb 14 2016 13:08
[Function: f1]
Raine Virta
@raine
Feb 14 2016 13:11
@ram-bot Maybe.Just(1)
Raine Virta
@raine
Feb 14 2016 13:54
@ram-bot Maybe.Just(1)
ram-bot
@ram-bot
Feb 14 2016 13:54
_Just { value: 1 }
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 14:12
how to do this in a more general, nicer way? const foo = R.map((x) => [x, x.bar])
Raine Virta
@raine
Feb 14 2016 14:14
repeat each twice, map the second with prop('bar')
will not say it's "nicer" though
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 14:18
yeah, js data mangling is a pain
i should switch to clojurescript
Hardy Jones
@joneshf
Feb 14 2016 14:28
@raine very cool. Thanks!
@ashnur what do you want to generalize?
Raine Virta
@raine
Feb 14 2016 14:46
@ram-bot uniqWith(identical, times(Math.random, 100000)); true
ram-bot
@ram-bot
Feb 14 2016 14:47
Error: Script execution timed out.
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:06
@joneshf i have a list of render functions each of them with a list of associated routes and i would like to have an object with a the routes as keys and for each route all associated render functions in a list
x.bar from my question was View.routes
Hardy Jones
@joneshf
Feb 14 2016 15:10
@ashnur sorry, bit early to wrap my head around those words :) can you provide a quick example?
of input and output
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:11
in pseudocode maybe :). but i already worked it out, although it's quite ugly
r.pipe(r.map((v) => [v.routes, v]), (ps) => ps.reduce((p, n) => r.mergeWith(r.concat)(p, r.pipe(r.zip(n[0]), r.fromPairs)(r.repeat([n[1]], n[0].length))), r.map((vs) => (state) => vs.map(v => v(s))))
something like this
Hardy Jones
@joneshf
Feb 14 2016 15:14
// I am imagining you start with something like this:
[{func: foo, routes: [route1, route2]}, {func: bar, routes: [route2, route5]}, ...]
// and want something like this:
{route1: [foo], route2: [foo, bar], route5: [bar], ... }
oh
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:16
the end of it is not good, the first two in the pipe is ok
there is an apply hidden here somewhere
Hardy Jones
@joneshf
Feb 14 2016 15:17
well, what's your input like?
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:17
a list of functions with a property on them, called routes that has a value that is an array of strings
Hardy Jones
@joneshf
Feb 14 2016 15:17
ah
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:18
sorry, i am writing a lot of typos today
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:29
const r = require('ramda')
const mergeFirsts = r.mergeWith( r.concat)
const objectify = (p, n) => mergeFirsts( p, r.pipe(r.zip(n[0]), r.fromPairs)(r.repeat([n[1]], n[0].length)))
const guide = r.pipe( r.map((v) => [v.routes, v]) 
                    , r.reduce(objectify, {})
                    , r.map((vs) => (state) => vs.map(v => v(state))))
@joneshf this works, but i wish it wouldn't be this complicated, not sure i will be able to understand it in 2 months
guide is the function i export
@joneshf but the example you wrote is basically the same as this, just missing the first step where i detach the routes from the View function and create a pair from them. that's in the objectify function here
Hardy Jones
@joneshf
Feb 14 2016 15:37
function foo(x) {}
foo.routes = ['r1', 'r2'];
function bar(x) {}
bar.routes = ['r2', 'r5'];
function baz(x) {}
baz.routes = ['r1', 'r3'];

const funcs = [foo, bar, baz];

const routify = func => R.map(R.objOf(R.__, [func]), func.routes);
const routed = R.chain(routify, funcs);
const mergeAllConcat = R.reduce(R.mergeWith(R.concat), {});
mergeAllConcat(routed);
//=> {"r1": [function foo(x) {}, function baz(x) {}], "r2": [function foo(x) {}, function bar(x) {}], "r3": [function baz(x) {}], "r5": [function bar(x) {}]}
Is that kind of what you're looking for?
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:38
exactly
wow
Hardy Jones
@joneshf
Feb 14 2016 15:39
that routify function doesn't have a good name, and it might be a bit more complex than need be, so maybe rename and split it up?
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:41
i don't know, i still just looking in disbelief
r.map((vs) => (state) => vs.map(v => v(state))) isn't there a shortcut for this?
Hardy Jones
@joneshf
Feb 14 2016 15:42
maybe
r.map((vs) => (state) => vs.map(v => v(state)))
r.map(vs => state => vs.map(r.call(state)))
r.map(vs => vs.map(r.call(r.__)))
r.map(r.map(r.call(r.__)))
I dunno that the first step is correct, nor do I think it's clearer what's happening
maybe it's r.call(r.__, state)?
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:45
i don't know how r.__ works :D
Hardy Jones
@joneshf
Feb 14 2016 15:45
In which case you probably can't simplify more
foo(r.__) == x => foo(x)
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:46
oh
Hardy Jones
@joneshf
Feb 14 2016 15:46
so you can put it wherever you need it
Also, I think the function needs to be curried
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:46
isn't that's the same as just foo?
Hardy Jones
@joneshf
Feb 14 2016 15:47
in that case, yes
foo(r.__, bar, baz)
you usualy use it when you want to leave out an argument that is't the last
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:47
i see
Hardy Jones
@joneshf
Feb 14 2016 15:47
or
foo(bar, r.__, baz)
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:47
and it works with arbitrary functions?
Hardy Jones
@joneshf
Feb 14 2016 15:48
I think the function has to be curried from ramda.
or made from other ramda functions
think
not entirely sure.
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 15:48
will look it up then, thanks : )
David Chambers
@davidchambers
Feb 14 2016 17:07
v => v(state) can be expressed in terms of the T combinator, T :: a -> (a -> b) -> b: T(state). @Avaq, we should have Sanctuary export T as well. :)
David Chambers
@davidchambers
Feb 14 2016 17:12
@ashnur, I don't recommend this, but x => [x, x.bar] can also be written R.lift(R.pair)(R.identity, R.prop('bar')). I'd stick with the lambda, though. ;)
@ram-bot R.map(R.lift(R.pair)(R.identity, R.prop('bar')), [{bar: 'x'}, {bar: 'y'}, {bar: 'z'}])
ram-bot
@ram-bot
Feb 14 2016 17:12
[ [ { bar: 'x' }, 'x' ],
  [ { bar: 'y' }, 'y' ],
  [ { bar: 'z' }, 'z' ] ]
Aldwin Vlasblom
@Avaq
Feb 14 2016 17:14
@davidchambers Oohhh, never came across a purpose for the T combinator. Interesting. :smile:
David Chambers
@davidchambers
Feb 14 2016 17:15
It's not often useful, but it's occasionally exactly what one is looking for.
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 17:17
: D
Hardy Jones
@joneshf
Feb 14 2016 17:18
I feel like A and T are way more useful than they appear.
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 17:19
i have no idea how you even remember these
joneshf @joneshf had to look the names up :)
Hardy Jones
@joneshf
Feb 14 2016 17:19
I know them as ($) and (#) from PureScript respecrtively
or, I guess ($) and (&) from Haskell as well.
David Chambers
@davidchambers
Feb 14 2016 17:20
@joneshf, these may be examples of functions one doesn't think to use until one learns of their existence, then wonders how one got by without.
Hardy Jones
@joneshf
Feb 14 2016 17:20
haha, probably
I will admit though, T is much more useful as an operator than a function.
David Chambers
@davidchambers
Feb 14 2016 17:22
That's true.
I wonder why PureScript uses (#) rather than (&). I'm sure there's a reason.
Hardy Jones
@joneshf
Feb 14 2016 17:23
(&) wasn't in the haskell prelude when ps chose (#)
so no precendent
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 17:24
and # ?
David Chambers
@davidchambers
Feb 14 2016 17:24
Ah, okay. I'd forgotten that (&) was added to Haskell's prelude so recently.
Hardy Jones
@joneshf
Feb 14 2016 17:24
It's right next to ($)
also, it can be backronymed to be called "pound"
so one is "dollar", the other is "pound"
:)
David Chambers
@davidchambers
Feb 14 2016 17:25
Cute!
Dominic Chambers
@dchambers
Feb 14 2016 22:40

Sorry, noob question here. If I have some data like this:

var data = {
    users: {
        Bob: {
            age: 34
            gender: 'Male'
        },
        Alice: {
            age: 41
            gender: 'Female'
        }
    }
};

and I want to remove the gender property from the user data, how do I do that with Ramda?

GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 22:49
@dchambers maybe something like r.pipe(r.prop('users'), r.map(r.dissoc('gender')))(data) ?
Scott Sauyet
@CrossEye
Feb 14 2016 23:01
Or maybe this, to keep the users property intact:
R.evolve({users: R.map(R.dissoc('gender'))})(data);
Scott Christopher
@scott-christopher
Feb 14 2016 23:01
@dchambers If you need to keep the top level object, you can use R.over and R.lensProp here too:
removeGender = R.dissoc('gender');
removeGenderFromUsers = R.over(R.lensProp('users'), R.map(removeGender));
removeGenderFromUsers(data);
David Chambers
@davidchambers
Feb 14 2016 23:06
Welcome, @dchambers!
Scott Sauyet
@CrossEye
Feb 14 2016 23:08
yeah, that's not going to get confusing... :smile:
GÁBOR Áron Zsolt
@ashnur
Feb 14 2016 23:13
: ))