These are chat archives for ramda/ramda

4th
Apr 2017
Johnny Hauser
@m59peacemaker
Apr 04 2017 01:11

Ramda's lens is a bit involved. https://github.com/ramda/ramda/blob/master/src/lens.js#L29-L38

What's the extra complexity for, compared to something like:

const lens = (get, set) => ({get, set})
const view = (lens, data) => lens.get(data)
const set = (lens, v, data) => lens.set(v, data)
const over = (lens, fn, data) => lens.set(fn(lens.get(data)), data)
James Forbes
@JAForbes
Apr 04 2017 01:20
composition
Johnny Hauser
@m59peacemaker
Apr 04 2017 01:21
can you explain it in a way nubs can understand? :)
James Forbes
@JAForbes
Apr 04 2017 01:22
but as cool as it is than lenses compose using normal function composition, I've thought just having a special lens compose operator could be helpful in making the implementation less indirect
Johnny Hauser
@m59peacemaker
Apr 04 2017 01:22
Ohhhh
You can compose them together
James Forbes
@JAForbes
Apr 04 2017 01:22
So if you have a lens that points at a property a and you have a lens that points to a property b you can compose them compose(lensA, lensB)
Johnny Hauser
@m59peacemaker
Apr 04 2017 01:22
neato
hmph
James Forbes
@JAForbes
Apr 04 2017 01:23
and its like lensPath(['a','b'])
which seems like a party trick until you start using lenses for things other than paths
but yeah I'm sort of partial to abandoning lens' function composition, it'd probably a lot simpler to implement, and more performant, less overhead
Johnny Hauser
@m59peacemaker
Apr 04 2017 01:24
I wonder if it can be done simpler.
It's not many lines of code, but I can't wrap my head around what Ramda does.
James Forbes
@JAForbes
Apr 04 2017 01:24
you could still define set in terms of over, but viewcould be a lot less indirect
Johnny Hauser
@m59peacemaker
Apr 04 2017 01:25
It's especially surprising that you need to map over an object like { value: theValue } in that process
Brian McKenna
@puffnfresh
Apr 04 2017 01:26
James Forbes
@JAForbes
Apr 04 2017 01:26
Thanks @puffnfresh!
Brian McKenna
@puffnfresh
Apr 04 2017 01:27
type Iso s a = forall p. (Profunctor p) => p a a -> p s s
a common Profunctor is functions
in this formulation, you can instantiate p to a function and get back:
type Iso' s a = (a -> a) -> (s -> s)
so an iso allows you convert a function between "a" to a function between "s"
same with lens, prism, etc.
matfournier
@matfournier
Apr 04 2017 02:18
Hola. Trying to work with promises (through node-fetch). Should I be using Task (from fantasyland) or pipeP/composeP? I have it in Task right now but that doesn't seem.... right.
Ryan Stegmann
@rstegg
Apr 04 2017 02:20
@matfournier are you looking for the laziness that Task provides? (also a Future is more of the go-to at this point for async)
matfournier
@matfournier
Apr 04 2017 02:25
It's more this is my first time hitting promises in my functional JS learning and looking for the best way to handle them, esp since I am wrapping an exisiting lib that returns a promise.
Johnny Hauser
@m59peacemaker
Apr 04 2017 02:46
@JAForbes How about this? https://goo.gl/cyBdQz Please don't mind the stupid names. I was avoiding global naming conflict and hens was a tickling starting place.
matfournier
@matfournier
Apr 04 2017 03:32
annnd solved! fluture was the way to go. Easy drop in and even handled my downstream json parsing (which is also returning a promise), so the fromPromise method was the way to go...
James Forbes
@JAForbes
Apr 04 2017 04:38

Hey @m59peacemaker I'll give it a closer look soon, but ftr you can take advantage of block scoping

see: https://goo.gl/Yq5bvW

@matfournier fluture is great, also check out creed, its not lazy, but its a fantasy land and static land compliant promise lib. So it has map and chain, its also made by the same person who wrote most.js, so it's probably pretty fast too.
But I'm a big fan of future, just wanted to mention creed
@m59peacemaker hey that's pretty cool, why do you check if its a lens (isLens) out of interest?
matfournier
@matfournier
Apr 04 2017 04:48
Yeah. Beating my head against the wall with fluture. I need to use fetch to get something (a promise), then convert to json (another promise), which I am using fromPromise to do. But then can't seem to just get out the resolved value (which I want to shove into an Either for handling whether or not the fields I need exist on the json...)
James Forbes
@JAForbes
Apr 04 2017 04:49
@matfournier can you elaborate why you want to put it into an Either
That's totally possible, but it'll will still stay within the Future, so something like Future.bimap( Left, Right ) will give you a Future Either a
matfournier
@matfournier
Apr 04 2017 05:00
@JAForbes my issue with bimap is how do I wrap something that isn't a future inside of it, that may fail?
E.g.... .bimap(e => e, s => s.users_url) but accessing the users_url may fail as it may not exist (hence wanting to shove it into an Either)
matfournier
@matfournier
Apr 04 2017 05:13
if I instead wrap s.users_url in an Either.Left or Right depending on whether it's undefined, I'm stuck on the next step as the object I'm in is still a future, not a future resolved to an Either... so I can't map/continue on my way on the inner Either, which makes me suspect I'm doing something wrong.
Though I haven't cracked this, I've certainly learned a lot. Thanks for the help.
James Forbes
@JAForbes
Apr 04 2017 05:42
@matfournier you can't make something async, sync right. So your not going to lose the Future, you can flatten Futures, but you'll always be left at the very least with 1 outer Future
I mean technically you could turn the outer Future into some other async structure, but the point is, its going to be async
You could use an Either, but you could also just use the fact Future's have a rejection path already. Its definitely ok to nested Either within a Future though, it has semantic value.
Casey Link
@Ramblurr
Apr 04 2017 06:08
Anyone know of an elegant way to perform a R.minBy on an array of objects but considering two properties on the object? That is "choose the object from the array that has the smaller X, and the smaller Y" (in that order)
matfournier
@matfournier
Apr 04 2017 06:22
@JAForbes I get what you're saying. Back to playing! Thanks.
James Forbes
@JAForbes
Apr 04 2017 06:49
@Ramblurr is it like the smallest x, then the smallest y, or is it something like add x and y together and find the smallest?
Casey Link
@Ramblurr
Apr 04 2017 06:50
the former, the smallest x, and then the smallest y
James Forbes
@JAForbes
Apr 04 2017 07:08
How about
var sortByKeys =
  pipe(
    map( pipe(prop, ascend) )
    ,sortWith
  )

sortByKeys(['x','y'])(xs)
its not a binary function like minBy, but if you just pass it a list instead it should be ok
you could wrap this up and make it a binary if you want, but seems pointless
Casey Link
@Ramblurr
Apr 04 2017 07:09
hm interesting!
James Forbes
@JAForbes
Apr 04 2017 07:10
if you want it to be a binary op you could do
pipe(
    map( pipe(prop, ascend) )
    ,(fs) => (a,b) => sortWith(fs, [a,b])
)
just incase you're using this inside reduce or something
Johnny Hauser
@m59peacemaker
Apr 04 2017 10:34
@JAForbes no reason, actually
I was just being super careful. I was thinking maybe throwing there if isn't a lens
James Forbes
@JAForbes
Apr 04 2017 10:57
ah cool, was wondering if I was missing something to do with the compose implementation
Johnny Hauser
@m59peacemaker
Apr 04 2017 11:09
good idea with the block. I'm such a scrub haha.
I'm so conditioned to (() => { and I didn't feel like it
man, I'm thankful for the reminder about those.
James Forbes
@JAForbes
Apr 04 2017 11:37
yeah it only occurred to me recently
I actually wish we still didn't have block scoping, but at least I've found a use for it
Johnny Hauser
@m59peacemaker
Apr 04 2017 11:44
oh yeah? What's the drawback?
Just more language bulk?
James Forbes
@JAForbes
Apr 04 2017 11:46
Its not even just bulk, its more complexity, bulk can be good.
Johnny Hauser
@m59peacemaker
Apr 04 2017 11:49
heh, I consider "bulk" to be useless crap
James Forbes
@JAForbes
Apr 04 2017 11:49
ah yeah I added that qualifier because in js, in our context size is considered a bad thing
Johnny Hauser
@m59peacemaker
Apr 04 2017 11:49
That's not the proper definition, I see.
James Forbes
@JAForbes
Apr 04 2017 11:49
meanwhile clojurescript (as an example) has a completely different philosophy
Johnny Hauser
@m59peacemaker
Apr 04 2017 11:50
Scrapping prototypes would be a huge win :)
except for... practicality in the real world
James Forbes
@JAForbes
Apr 04 2017 11:56

const should have just done 1 thing, made a variable immutable, but they throw in block scoping, which var doesn't do, so there's now this inconsistency, where previously it was just "no block scoping use functions". It also didn't really get immutability right, so...

And "no block scoping: use functions" isn't a bad thing at all, its just unusual. But JS is unusual, and if we just accept it for what it is we could avoid these additions that have complex exceptional behaviour.

  • Const isn't really const, and block scoping happens sometimes
  • classes aren't really classes
  • template literals are multi line strings but they have annoying indentation behaviour, so they don't even get that right, and then they also have this weird tagged template literal feature (livescript did this so much better, by spitting out the respective parts as data if requested)
  • arrow functions aren't just sugar, they also have this special behaviour for this, again exceptional behaviour that is of 0 use in FP

Everything they add, its like they get the thing its meant to do wrong, and they add some special addendum. Reading the spec is like "its like ... kind of sort of, except when.... oh and watch out for"

They design the language for sub optimal use cases which only validates those sub optimal paths as optimal paths.

So I wish they just stopped adding stuff. And if they are going to add stuff, be consistent, respect the existing language behaviour and extend it instead of pretending its a different language.

I mean web assembly is here, there's really no reason to pretend JS isn't JS anymore.

Johnny Hauser
@m59peacemaker
Apr 04 2017 11:57
oy
You mean because const x = {}; x.newProp = 'huehuehue'
Yeah, totally agree with all of that.
James Forbes
@JAForbes
Apr 04 2017 11:59

also pet peeve with block scoping:

try {
  const a = ...
  const b = ...
  const c = ...
  throw ...
} catch (e) {
  // I want to access a,b,c (e.g. for logging) here but I can't
}

The above seems easy to fix, but the solution is to just localize try catches as much as possible, adding more and more layers of nesting.

This problem doesn't really affect me because I don't use try catch, but it still annoys me in principal.
But I imagine it would come up a lot when people write imperative code with async await.

Johnny Hauser
@m59peacemaker
Apr 04 2017 12:00
Is this a good name? (for my lightweight functional lib)
const mapNth = (n, fn, data) => over(lensIndex(n), fn, data)
then I have to figure out a name for regular over. mapLens doesn't make sense :)
James Forbes
@JAForbes
Apr 04 2017 12:02
seems like equivalent functionality, is it to make it more approachable for people?
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:03
It's mostly Ramda, minus support of things like placeholders / fantasy-land, whatever else I can drop
but moreover, super modular
So you can justify using it in a 10 line npm package, just installing the functions you need.
Ramda can never be ideal for that, even if it were published like @ramda/reduce @ramda/map etc
because each thing would be a bit heavy
As to the over topic, I think having map in the name makes it more understandable.
Kurt Milam
@kurtmilam
Apr 04 2017 12:06
'over' is called 'modify' in partial.lenses.
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:06
I was just thinking, I bet there are common practices on this topic I should follow.
Kurt Milam
@kurtmilam
Apr 04 2017 12:09
I think 'map' is fine, as well, since it just means 'apply a provided function to the contents of the thing to be mapped'.
Guess the only issue with 'map' is that there are plenty of people who only know of its use in the context of iterating over arrays or collections.
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:10
Haskell calls it over, so that settles it imo :)
Kurt Milam
@kurtmilam
Apr 04 2017 12:10
Indeed, that's good to know.
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:11
though that stuff is straight up Sci-Fi to me, so that's my best guess at what the docs say.
"I saw the word 'over' in there".
James Forbes
@JAForbes
Apr 04 2017 12:12

You both might have different experience to me, but so far I've found trying to write utils that hide the lens from the caller ends up being less powerful/useful.

I mean, there's exceptions, but instead of mapNth, I'd just make writing lensIndex less verbose, like L.at

and let the user supply it themselves to over
Stephan Meijer
@smeijer
Apr 04 2017 12:13
How can I make this filterByState point free?, optionally by using lenses:
const list = {
  tasks: [
    { id: 2, state: 'done' },
    { id: 3, state: 'todo' },
    { id: 4, state: 'done' },
  ],
};

const filterByState = state => R.pipe(
  R.prop('tasks'),
  R.defaultTo([]),
  R.filter(R.propEq('state', state)),
);

const todos = filterByState('todo');
const result = todos(list);
James Forbes
@JAForbes
Apr 04 2017 12:14

const mapNth = (n, fn, data) => over(lensIndex(n), fn, data)

vs over( L.at(0), fn, data )

Not so bad inline

And then if they want to abstract they can as const mapNth = n => over(L.at(n))
if over is curried
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:15
I suppose my need for that shortcut depends on how I end up solving the mapping object/map problem where you have [ k, v ] all the time. At the moment, my lib is like:
map(over(lensIndex(1), inc), { foo: 1 }) // { foo: 2 }
and that gets annoying!
map(overNth(1), inc
much better
and heck, now that I say that, over is elegant there.
I don't think I can approach this the ramda way because:
pipe(map(inc), filter(isEven)) // actually using transducers but looks the same as usual
Gabe Johnson
@gabejohnson
Apr 04 2017 12:18
@JAForbes I think block scope is fine (especially for newcomers to JS). My gripe is w/ the new semantics of the basic for loop wrt let
Kurt Milam
@kurtmilam
Apr 04 2017 12:19
@JAForbes re: 'hiding the lenses from the caller' - not sure I follow. Could be because I hadn't really paid close attention to the previous conversation between you and Johnny.
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:20
I think he meant by calling it map
You need to realize you're working with a lens
Otherwise, it's probably a leaky abstraction.
Gabe Johnson
@gabejohnson
Apr 04 2017 12:21

for used to be sugar

for (var i = 0; i < 4; i++) {...};

// same as
var i = 0;
while (i < 4) {...; i++;}

but w/ let i is weirdly scoped to the block

Johnny Hauser
@m59peacemaker
Apr 04 2017 12:21
I'm not quite smart enough to say without messing around with it, but I think you could especially get into trouble if you mutated during over, thinking that it is like normal map where that's ok because you're creating a new thing.
Gabe Johnson
@gabejohnson
Apr 04 2017 12:22
you can't desugar it
James Forbes
@JAForbes
Apr 04 2017 12:23
I meant, having the utility (e.g. mapNth) call lensIndex internally, instead of the lens just being an arg (@kurtmilam , @m59peacemaker )
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:24
Do you still think it's bad even if you know it's lens based?
James Forbes
@JAForbes
Apr 04 2017 12:24
@m59peacemaker its not so much that I think its bad, its just when I do it I seem to undo it soon after
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:24
hmm
Kurt Milam
@kurtmilam
Apr 04 2017 12:24
ah, gotcha. I hadn't taken a close look at the implementation.
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:24
I just really like to call only one function when dealing with [ k, v ] all the time
James Forbes
@JAForbes
Apr 04 2017 12:24
because I might need a very similar function but for a different lens, and parameterizing is so simple, why not do that and avoid the hassle
yeah fair enough
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:25
I even considered just making it overLast just for that purpose.
but, then you need overFirst to map the keys
so, meh
Gabe Johnson
@gabejohnson
Apr 04 2017 12:26
well I guess you can do
for (let i = 0; i < 4; i++) {...};

// same as
{
  let i = 0;
  while (i < 4) {...; i++;}
}
James Forbes
@JAForbes
Apr 04 2017 12:26

I was working on a ui abstraction over lenses, and it quickly became apparent hiding lenses from the user, would make things more complex than that getting them comfortable with lenses.

So instead of hiding them, I changed the api around, basically binding lenses to a particular state stream, which you'd think would defeat the point of lenses, but on reflection, reading/writing from arbitrary state is less useful than composing lenses (for me at least)

Johnny Hauser
@m59peacemaker
Apr 04 2017 12:27
@smeijer It would take me quite a while to solve that, but I can tell you that every time I have had that problem so far, converge or useWith have been the ticket to making it point free.
Kurt Milam
@kurtmilam
Apr 04 2017 12:27
I tend to eschew using indexes, preferring first, last, head and tail.
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:28
I don't see how they can be comparable.
James Forbes
@JAForbes
Apr 04 2017 12:28
@smeijer I think you want this:

useWith(
  filter, [propEq('state'), propOr([], 'tasks')]
)
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:30
([ k , v ]) => [ k, inc(v) ]
overNth(1, inc)
// how does first, last, head, or tail help?
@JAForbes like a boss :)
At least I was 50/50 on the correct function! Improvement, woop woop.
James Forbes
@JAForbes
Apr 04 2017 12:32

@smeijer the first argument will go to the first function of the list, so 'todo' would go into propEq('state', 'todo')

propEq('state', 'todo')

And the list, argument will go to the second funciton

propOr([], 'tasks', list)

Then when they're expanded, they both get applied to filter.

So we end up with something like filter( propEq('state', 'todo'), propOr([], 'tasks', list)

useWith is helpful when you have separate transform paths for separate args, converge is useful when you have multiple transform paths for the same arg
Kurt Milam
@kurtmilam
Apr 04 2017 12:32
@m59peacemaker I rarely have a need to do anything like that.
James Forbes
@JAForbes
Apr 04 2017 12:33
@m59peacemaker haha no, not at all, I still can't remember how to use chain and ap instead of converge/useWith
I feel sorry for @xgrommx, he's demoed it so many times
and @scott-christopher explained it to me once as well
for some reason my neurons reject it
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:34
man that's got me curious!
James Forbes
@JAForbes
Apr 04 2017 12:34
if you figure it out you can teach it to me :D
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:34
haha probably not going to happen
James Forbes
@JAForbes
Apr 04 2017 12:35
probably for the best, won't stay in my head anyway :D
@gabejohnson I don't know if I buy the "for newcomers to js" argument anymore. newcomers would benefit from a consistent language
I feel very sorry for current newcomers to js in the age of es7
If I were teaching JS today from scratch, I'd teach es3
And I'd avoid most of the language
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:38
hahaha, for real. Except arrow functions
James Forbes
@JAForbes
Apr 04 2017 12:38
Arrow functions too! :D
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:39
:'(
I get the problem (I think), but it's still better than regular ones.
James Forbes
@JAForbes
Apr 04 2017 12:39
most arrow functions I see, could easily be a call to prop or pluck etc
I still use them, but I don't they'd be necessary for teaching at all
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:40
Oh yeah, if you're going to give them the necessary abstractions and enforce point free
James Forbes
@JAForbes
Apr 04 2017 12:40
well not necessarily enforce, but encourage
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:40
if you give someone function, you know they will put this in it.
They just do.
James Forbes
@JAForbes
Apr 04 2017 12:40
not if I never teach them this
:D
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:40
They'll find a way.
James Forbes
@JAForbes
Apr 04 2017 12:41
I've taught some coworkers js, and they to this day don't know what this is
but they're using ramda, and streams happily
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:41
You know what's a real kick in the teeth?
James Forbes
@JAForbes
Apr 04 2017 12:41
what?
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:42
Have you ever looked at the code in any of the transducer libs (besides Ramda) ?
James Forbes
@JAForbes
Apr 04 2017 12:42
no, I'm still a complete transducer novice
they use this?
Gabe Johnson
@gabejohnson
Apr 04 2017 12:42
come on! es5 had a lot of good stuff added to the standard library
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:42
Well, I wrote one, so I think I can help ya there :)
Dude, they are WRETCHEDLY OOP
James Forbes
@JAForbes
Apr 04 2017 12:42
@gabejohnson yeah it did, but easy to implement
e.g. no syntax
@m59peacemaker :(
Gabe Johnson
@gabejohnson
Apr 04 2017 12:43
@m59peacemaker they're also pretty stateful. right?
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:43
What really wrecks me is that one of them was written by a popular Clojure developer... that works for Rich Hickey, I think.
how do you go from Clojure to JS writing OOP!?
for a functional lib!
James Forbes
@JAForbes
Apr 04 2017 12:44
I worked in es3, in windows scripting host once, for a nightmare project, and was suprised how nice the language still was
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:44
@gabejohnson mmm, I don't suppose so. They're just composed awfully.
James Forbes
@JAForbes
Apr 04 2017 12:44
kind of changed my whole world view on js
The one thing I missed more than anything else, was a module system, there was nothing in that environment
So I implemented a lightweight common js
so glad that's over
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:45
I suspect this is the best OOP code and it is therefore the worst of them https://github.com/Gozala/transducers/blob/master/src/transducers.js
I can't understand that code at all.
Gabe Johnson
@gabejohnson
Apr 04 2017 12:46
Honestly I think w/ this and first-class functions you don't need a module system implementation. I know it's heresy. But you do need a specification
James Forbes
@JAForbes
Apr 04 2017 12:47
@gabejohnson interesting, I still needed to load files at the very least
but that's interesting
Gabe Johnson
@gabejohnson
Apr 04 2017 12:47
But I do prefer the new import based module system over the anarchy of the past
James Forbes
@JAForbes
Apr 04 2017 12:48
I'm glad there's a standard, I like commonjs though, but I'm just glad there's a standard
Gabe Johnson
@gabejohnson
Apr 04 2017 12:48
:+1:
James Forbes
@JAForbes
Apr 04 2017 12:48
it'll be nice when node supports it
Gabe Johnson
@gabejohnson
Apr 04 2017 12:49
Yeah...lack of node support still trips me up
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:49
babel-preset-env for the super winniest win everest
James Forbes
@JAForbes
Apr 04 2017 12:50
do you use babel with node @m59peacemaker ?
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:51
always
James Forbes
@JAForbes
Apr 04 2017 12:51
intriguing
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:51
I used to put things under node_modules/app to accomplish something like that
James Forbes
@JAForbes
Apr 04 2017 12:54
yeah I fell down that rabbit hole
symlinks on windows :shipit:
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:55
rm -r node_modules WTF DANGIT git reset --hard
James Forbes
@JAForbes
Apr 04 2017 12:56
I never did the node_modules/app thing because I knew that would happen! :D
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:56
and then yarn came around
James Forbes
@JAForbes
Apr 04 2017 12:56
I used symlinks, but ran into some weird permissions issues
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:56
You can't put things under node_modules with yarn. Yarn owns that space.
James Forbes
@JAForbes
Apr 04 2017 12:57
Oh your on yarn? I stayed with npm
Johnny Hauser
@m59peacemaker
Apr 04 2017 12:57
It installs much faster and is more deterministic.
It was buggy as heck at first, but quite nice now.
James Forbes
@JAForbes
Apr 04 2017 12:57
Not for any reason other than touching the build proces is a luxury I don't deserve right now
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:00
It's also on my todo list to make a non-babel version of that
which could just be a few lines. I just have so many libs I'm working on.
It would just hijack node's module resolution
Stephan Meijer
@smeijer
Apr 04 2017 13:01
It only sucks that yarn doesn't run the postinstall hook :(
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:01
You would require and initialize it at the start of your entry point(s) and then require('~/path/like/this') would work
James Forbes
@JAForbes
Apr 04 2017 13:04
@smeijer it doesn't?
is that for security?
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:09
oh hah! I already made it, it turns out
Stephan Meijer
@smeijer
Apr 04 2017 13:10
:laughing:
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:10
Before I publish (unless I already did that, too....) is this good?
require('project-relative-require').setRoot(__dirname)
const things = require('~/things/relatively')
Should I use cwd() as a default if no path given?
Stephan Meijer
@smeijer
Apr 04 2017 13:13
@JAForbes , I opened the old issue to find the reason. Turned out this issue has been fixed :sunglasses: So ignore my comment. :hushed:
James Forbes
@JAForbes
Apr 04 2017 13:13
I think you should traverse up until you find a package.json @m59peacemaker
@smeijer that's good! :D
@m59peacemaker a project might have multiple entry points where you'd call that hook and they might be in different directories (e.g. testing, ssr)
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:15
ugg
James Forbes
@JAForbes
Apr 04 2017 13:15
package.json is canonical
Stephan Meijer
@smeijer
Apr 04 2017 13:15
hard to keep up with all updates, changelogs, new libraries and other awesomess in the world of javascript
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:15
do you know of a good lookup module for that?
James Forbes
@JAForbes
Apr 04 2017 13:15
I wouldn't be surprised if there's a built in
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:15
I recall looking for something like that before and be dissatisfied with existing packages
James Forbes
@JAForbes
Apr 04 2017 13:15
because that's how require works already
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:15
It's a good point, yeah
James Forbes
@JAForbes
Apr 04 2017 13:16
oh just thought of a great hack though ...
assuming your module is installed in node_modules
you could use require.resolve with the name of your module, then backtrack 1 dir
but there's probably a built in, worth researching
path.resolve( require.resolve('yourpackage'), '..', '..' )
that should give you the root of the project
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:19
it's shady
James Forbes
@JAForbes
Apr 04 2017 13:19
well depending where you main is, but that's in your control
well it's spec'd behaviour
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:19
This particular module should never end up in this situation, but it could be installed nested, right?
James Forbes
@JAForbes
Apr 04 2017 13:20
if you don't want to rely on main require.resolve('yourpackage/package.json')
ahhh true, thought that didn't happen anymore
Johnny Hauser
@m59peacemaker
Apr 04 2017 13:22
I think there's an option for it
Stephan Meijer
@smeijer
Apr 04 2017 13:22
why hack, use process.env instead
James Forbes
@JAForbes
Apr 04 2017 13:23
there's require.parent turns out
@smeijer in what way?
var p;
while( p = require.parent ){
}
root = p.id
Stephan Meijer
@smeijer
Apr 04 2017 13:24
Take the entry file as default, but also read process.env.ROOT_DIR ? Run script with
ROOT_DIR=/my/root node ./main.js
var p = process.env.ROOT_DIR || path.resolve( require.resolve('yourpackage'), '..', '..' )
James Forbes
@JAForbes
Apr 04 2017 13:24
@m59peacemaker I think that while loop will do it
@m59peacemaker actually I'm wrong, that's the same as just going require.main.id
I think you'll just need to traverse using the OS
I think its ok to use sync methods in this case, seeing as its require
const fs = require('fs')

var p = __dirname
var root;
while( !root ){
  fs.readdirSync('.')
    .find( s => s.includes('package.json')
    p = path.resolve( p, '..' )
}
Stephan Meijer
@smeijer
Apr 04 2017 13:29
const fs = require('fs')

var p = __dirname
var root = process.env.ROOT_DIR;

while( !root ){
  fs.readdirSync('.')
    .find( s => s.includes('package.json')
    p = path.resolve( p, '..' )
}
that way you support multiple entry points:
ROOT_DIR=/usr/local/myproject node ./testfile.js
Gabe Johnson
@gabejohnson
Apr 04 2017 13:31
heh @JAForbes I just found this gem https://tc39.github.io/proposal-async-iteration
James Forbes
@JAForbes
Apr 04 2017 13:32
cries
Gabe Johnson
@gabejohnson
Apr 04 2017 13:32
for await (let v of p);
Why they're doubling down on imperative syntactic extensions is beyond me
James Forbes
@JAForbes
Apr 04 2017 13:34
@gabejohnson there was a great scala talk a while back on their for syntax
it supported all kinds of monads, and a few other interfaces did you see it?
I'm not a scala person ftr, but it was pretty cool
Gabe Johnson
@gabejohnson
Apr 04 2017 13:34
no. do you have a link?
is it like do in loop form?
Stephan Meijer
@smeijer
Apr 04 2017 13:35
async generators? :worried: async function* OMG() { yield await response() }
@gabejohnson a do while :P ?
Stephan Meijer
@smeijer
Apr 04 2017 13:35
I do like them in the for though
Gabe Johnson
@gabejohnson
Apr 04 2017 13:36
hahaha
It JS wants side-effectful async iteration can't they just implement forEach for async iterators?
@JAForbes thx. I'll check it out
James Forbes
@JAForbes
Apr 04 2017 13:37
@gabejohnson yeah exactly
@gabejohnson yeah let me know what you think
Kurt Milam
@kurtmilam
Apr 04 2017 13:52
I have two objects, a and b. I want to test whether a[ k ] === b[ k ]. I'm drawing a blank.
Brain-fry, probably.
Kurt Milam
@kurtmilam
Apr 04 2017 13:54
:D awesome. I don't think I was aware of that function. Thanks!
James Forbes
@JAForbes
Apr 04 2017 13:54
:)
Casey Link
@Ramblurr
Apr 04 2017 13:56
Some great discussion in here!
What do you guys think of efforts to add stronger type systems to JS, ala Flow or Typescript?
(ignoring the class based OOP they usually encourage)
Stephan Meijer
@smeijer
Apr 04 2017 13:57
I think it will be almost impossible to do while maintaining backwards compatibility
James Forbes
@JAForbes
Apr 04 2017 13:58
could also do one for lists like: pipe(pluck,apply(equals))
Kurt Milam
@kurtmilam
Apr 04 2017 13:59
@JAForbes trying to get my state/stream/atom stuff ready to publish somewhere. I think it's getting pretty powerful, but it would be nice to have someone take a look and let me know if I'm overcomplicating things and/or going off on any unwise tangents.
James Forbes
@JAForbes
Apr 04 2017 14:01

@Ramblurr I think its a great idea, but my experience to date (typescript) is essentially useless for point free style. It makes composition painful. (I really, really wanted it to work, and I tried so many times :( )

But you could have a type system that worked really well for FP JS, I just don't think the likes of Facebook or Microsoft would jump on that project.

While trying to make it work I realised how much I value composition above static type guarantees. And that the bugs typescript catches aren't bugs you'd encounter if you wrote FP style code anyway.

I'd really like to be proven wrong here though!

And I want to stress, I want static typing, I really want it. I just won't sacrifice concise composition for it
@kurtmilam I'd love to take a look, have you got something live, like a jsbin?
Gabe Johnson
@gabejohnson
Apr 04 2017 14:04
@JAForbes once we get declarative macros back in sweet.js someone could write a type system in it
James Forbes
@JAForbes
Apr 04 2017 14:04
@Ramblurr I also think typescript is an amazing project, its really incredible. VSCode's standard out of the box js experience is pretty amazing because of what TS's compilation server does behind the scenes
@gabejohnson es3 with types :D
Gabe Johnson
@gabejohnson
Apr 04 2017 14:04
hahaha
James Forbes
@JAForbes
Apr 04 2017 14:05
@Ramblurr sanctuary-def is also an amazing project
and all the related libraries
I used to think sanctuary-def was a low level thing, and you should primarily interface with sanctuary, but I've found sanctuary-def is a great tool for application level code, I'm still trying to figure out the best way to integrate it into a project, but its caught a lot more bugs for me than typescript ever did
Right now I'm completely overhauling a permissions system in an api. And there's various entry points to obtain an auth token. So I've made this module:
/* globals localStorage */

//eslint-disable-next-line no-undef
const checkTypes = !!process.env.CHECK_TYPES

const $ = require('sanctuary-def')
const Type = require('sum-type')($, { checkTypes, env: $.env })

const Stream = require('flyd')
const remember = Stream.stream(true)

import $AuthPermission from '../../../types/auth_permissions'

const $Auth = Type.Named('Auth', {
    LoggedOut: {}
    ,LoggedIn:
        { auth_token: String
        , user_id: String
        , auth_permissions: $.Array($AuthPermission)
        }

})

const initial = JSON.parse(localStorage.getItem('auth') || 'null')

const auth = Stream.stream(
    initial
    ? $Auth.LoggedInOf(initial)
    : $Auth.LoggedOutOf({})
)

// notifications if an invalid auth object is created
auth.map(
    $Auth.case({
        LoggedOut: () => localStorage.setItem('auth', 'null')
        ,LoggedIn: () =>
            remember()
            && localStorage.setItem('auth', JSON.stringify(auth()) )

    })
)

export default {
    stream: auth
    , type: $Auth
    , remember
}
Whenever an endpoint sends something of the wrong type into the auth stream, sanctuary tells me
And with a readable error message
Kurt Milam
@kurtmilam
Apr 04 2017 14:09
@JAForbes no, and the primary reason for that is that I suck at client-side modules.
Currently, I have to run webpack and babel to build an app.js bundle that will run on the client.
James Forbes
@JAForbes
Apr 04 2017 14:10
can you commit your bundle to git, and use rawgit?
Kurt Milam
@kurtmilam
Apr 04 2017 14:11
Perhaps 'have to' is the wrong way to say it, but it's the only way I've been able to get the thing to run in a browser for use and node for testing.
I could, but it's ugly as sin, and huge. I haven't applied tree-shaking or anything like that to it.
James Forbes
@JAForbes
Apr 04 2017 14:11
@kurtmilam that's ok for this early stage
Kurt Milam
@kurtmilam
Apr 04 2017 14:12
Let me see if I can figure out how to make it work using rawgit.
James Forbes
@JAForbes
Apr 04 2017 14:12
I might have to sleep pretty soon, so no rush, I'll take a look tomorrow if its ready then
Kurt Milam
@kurtmilam
Apr 04 2017 14:14
cool, I'll see what I can figure out in the meantime.
James Forbes
@JAForbes
Apr 04 2017 14:14
:+1:
Matthew Willhite
@miwillhite
Apr 04 2017 14:15
I’m just starting to get some interest into Lenses…wondering about use cases for lenses vs something like State. At my level they look to serve somewhat the same purpose, perhaps the latter is a bit more robust?
Kurt Milam
@kurtmilam
Apr 04 2017 14:17
@miwillhite I'm playing with lenses, streams and state atm.
Seems to be a powerful combination.
Matthew Willhite
@miwillhite
Apr 04 2017 14:18
Streams are a great one to have the mix ;)

@kurtmilam Anything you have to share (thoughts, intuitions, etc) would be appreciated.

I’ve done some experimenting with State and streams in isolation…not really lenses though

Kurt Milam
@kurtmilam
Apr 04 2017 14:19
Agreed. I'm playing with a solution that gives me a single, global, immutable state container (currently for use with mithril)
One thing I've done is to 'register' in my state container the substreams I create so they can be reused.
Matthew Willhite
@miwillhite
Apr 04 2017 14:21
I build up a lot of data on the client to send back to the server. Working with a legacy, very declarative app and I’m always looking for ways to move it towards pure, immutability.
What are you using for the streams?
Gabe Johnson
@gabejohnson
Apr 04 2017 14:22
@JAForbes I was looking at sum-type the other day. Really cool. Any reason you're not using sanctuary-def as an internal dependency?
Kurt Milam
@kurtmilam
Apr 04 2017 14:23
And I've built some functions that act like over, set and get but they take streams rather than objects, and operate on the objects contained in the streams.
I'm currently using flyd.stream
And partial.lenses
Matthew Willhite
@miwillhite
Apr 04 2017 14:23
ok…just curious because I know something like flyd.stream or most would take an entirely different approach than RxJs
Kurt Milam
@kurtmilam
Apr 04 2017 14:25
For the most part, so far, I'm using streams like:
const s = flyd.stream( {} )
// later on
s() // to get the value in the stream
s( {a:1} ) // to set the value in the stream
I've looked at most, but it's not clear to me from the documentation whether that syntax is supported.
Casey Link
@Ramblurr
Apr 04 2017 14:25
@JAForbes Do you know of any projects on github that are using JS in a very functional style like you're advocating? Point free, heavy use of composition etc? I'd like to ingest a large project and see how it works in practice
Kurt Milam
@kurtmilam
Apr 04 2017 14:26
@miwillhite that syntax makes flyd streams nice for composition with lenses.
Matthew Willhite
@miwillhite
Apr 04 2017 14:26
@kurtmilam from what I understand the idea of setting values in the stream is frowned upon.
In my experiments (looking at a global app store and needing to be able to set values imperatively) I needed to use the most-subject library which gives the ability set in a stream.
I think the way they want you to do it is to generate a new stream with the values you want, but…I didn’t grok how that works for what I needed…I think it nods more towards the Cycle.js application architecture…
but I’m in an old version of angular…so :sweat_smile:
so it looks like with flyd you can keep a reference to the same stream and push values into it
Kurt Milam
@kurtmilam
Apr 04 2017 14:28
Right. That's how I'm using them, mostly, at the moment.
Matthew Willhite
@miwillhite
Apr 04 2017 14:29
so you are then using a “lens” interface to operate on the stream values?
where does State fit in to what you are doing?
Kurt Milam
@kurtmilam
Apr 04 2017 14:30
Well, I have one big state stream container and can create smaller slice or lensed streams that focus on a specific part of the state object in the state stream container.
For instance:
const over = stream => optic => fn =>
  R.compose( R.tap( stream )
           , Object.freeze
           , L.modify( optic, fn )
          )( stream() )
I can either pass the main state stream container into this or one of the sub streams. Either way, the value in the main state stream ends up being replaced with a modified clone of its original contents.
So there is no mutation of the actual state.
Kurt Milam
@kurtmilam
Apr 04 2017 14:36
I'm going to try to get it working online somewhere so I can let people take a look at the whole shebang and also take a small working app for a spin.
Matthew Willhite
@miwillhite
Apr 04 2017 14:36
cool
I had mentioned State thinking of the State monad, trying to get a sense of how the two are comparable (if they are) and pros/cons. BUT the work you are doing here is very interesting to me as well…1. because I’ve been looking at something similar with managing global state with streams, 2. because of your usage of lenses ;)
Johnny Hauser
@m59peacemaker
Apr 04 2017 14:42
Not tested in the wild, but the tests pass, so it's probably good. https://www.npmjs.com/package/root_require
Kurt Milam
@kurtmilam
Apr 04 2017 14:44
I've been trying to understand the State monad, as well, and whether/how it could be applicable. Also been trying to understand how I'd use the streams to manage state without pushing updated values into them.
It seems you're supposed to always generate a new State which is similar to your remark about not changing the contents of streams. Like you, I haven't exactly figured out how that would actually work in practice.
Guess I need to learn me some Haskell, because most of the State monad examples I've seen written in javascript are too simple for me to figure out how they'd be applicable to managing an entire app's state.
Raine Virta
@raine
Apr 04 2017 15:10
I haven't found any compelling examples of State making sense in JS
Rick Medina
@rickmed
Apr 04 2017 15:43
@raine or elsewhere, would love to see one though
Matthew Willhite
@miwillhite
Apr 04 2017 16:04

@kurtmilam yeah…I mean the Monad A Day - State example is the most “complete” example that I’ve found.

Second I guess would be the ramda-fantasy state example.

But they are really just handling one process…and maybe that is enough to start with.

@raine I find myself having to build up objects using complicated forms, so I’m thinking that may be an appropriate usage. Really just trying to keep the application logic immutable and pure in an angular 1.5 context (controller).
I find Futures are a great way to separate the async server lookups managed and forking in one point (my main or IO) before the values go to the view (which can ONLY be managed via side effects)
So the other half of the puzzle for me is how to handle the user input so that I can build up the values and send them back to the serer
Kurt Milam
@kurtmilam
Apr 04 2017 16:11
:thumbsup:
Rick Medina
@rickmed
Apr 04 2017 16:12
maybe there is a use case for node middlewares
btw, does anyone know an immutable abstraction for node's req/res?
Robert Mennell
@skatcat31
Apr 04 2017 16:55
@rickmed nodejs server callback recieves a ClientRequest object as the first argument and a ServerResponse object as teh second. They are Writable stream event emitters with internal state management. Since Immutability is setup at time of Object creation, and these are created by the HTTP server I'm not certain there is a way to make all of them immutable... What are you attempting to do that requires the mto be immutable?
Matthew Willhite
@miwillhite
Apr 04 2017 16:58
Lenses seem like a great alternative to evolve, where the transformations could become rather complex
But I wonder at what point we start to see benefits over using things like prop/assoc which already give us a sense of immutability and compositionality
Going through this “series” which has some references to more in-depth resources: https://medium.com/javascript-inside/an-introduction-into-lenses-in-javascript-e494948d1ea5
Robert Mennell
@skatcat31
Apr 04 2017 17:04
I like that you use sense of immutability...
Rick Medina
@rickmed
Apr 04 2017 17:15
@skatcat31 correct. I don't mean strictly versions of immutable req/res, maybe inject a dummy req/res to composable pure functions which describes the middleware and then merge it into the original created object something like that? just thinking out loud an alternative to mutate and next
Robert Mennell
@skatcat31
Apr 04 2017 17:16
@rickmed since you mentioned next I'm assuming you're using expressJS?
Rick Medina
@rickmed
Apr 04 2017 17:16
have used, yes
Rick Medina
@rickmed
Apr 04 2017 17:21
but all popular frameqorks work similarly
Robert Mennell
@skatcat31
Apr 04 2017 17:24
Interesting thing about req: it has a reference to res and next nested inside of it. Really your function can take just req, and you can access req.app, req.res, req.res.locals, req.next
@rickmed
you should also be probably mutating res.locals for anything in the transaction that you need to mutate
Rick Medina
@rickmed
Apr 04 2017 17:57
@skatcat31 along those lines...anyways, just wondering if there was something interesting like that out there more thought out than a 1 minute brain fart
Robert Mennell
@skatcat31
Apr 04 2017 18:00
@rickmed due to the nature of the actual data structure underneath I'm not certain thered be a way to do it...
Stefano Vozza
@svozza
Apr 04 2017 18:14
Are you thinking of something like momi?
Johnny Hauser
@m59peacemaker
Apr 04 2017 19:01
ahhhh, this is what most people do!
adjust(add(10), 1, data)
over(lensIndex(1), add(10), data)
I've always done the lens!
doh
Rick Medina
@rickmed
Apr 04 2017 19:02
@svozza heyy look at the state monad in action! thanks for that I forgot about momi...but not sure...
Johnny Hauser
@m59peacemaker
Apr 04 2017 19:06
and with evolve and assocPath, now I can't see a use for lenses :/
Aaron Rice
@adrice727
Apr 04 2017 19:24

Is there a way to delay evaluation of the default expression in pathOr. For example, in my redux thunk action creator:

const setEvent: ThunkActionCreator = (eventId: EventId): Thunk =>
  async (dispatch: Dispatch, getState: GetState): AsyncVoid => {
    const event = R.pathOr(await getEvent(eventId), ['events', 'map', eventId], getState());
    console.log(event);
  };

I want to see if the event exists in state.events.map, and if not, I'll call my api method to fetch it. It works, but getEvent is called even if the event exists in the map. I want it to be lazily evaluated. Can I make that happen?

Gabe Johnson
@gabejohnson
Apr 04 2017 19:36
@adrice727 you could wrap await getEvent(eventId) in a thunk and check to see if event is a function. If so, call it
const setEvent: ThunkActionCreator = (eventId: EventId): Thunk =>
  async (dispatch: Dispatch, getState: GetState): AsyncVoid => {
    const event = R.pathOr(async () => await getEvent(eventId), ['events', 'map', eventId], getState());
    console.log(typeof event === 'function' ? event() : event);
  };
Though I haven't used async functions and didn't even know they had async arrow functions yet.
Gabe Johnson
@gabejohnson
Apr 04 2017 19:41
That might mess w/ your type checking though. idk
Aaron Rice
@adrice727
Apr 04 2017 19:45

@gabejohnson Thanks, that works. I would just have to change event() to await event(), but I think I'm just going to stick with:

const event = R.path(['events', 'map', eventId], getState()) || await getEvent(eventId);

It's a little cleaner.

Gabe Johnson
@gabejohnson
Apr 04 2017 19:48
glad to help!
Rick Medina
@rickmed
Apr 04 2017 21:02

@JAForbes isn't pipeK + yield/value (a way to fold) essentially a for expression?

it's not at all. nevermind

James Forbes
@JAForbes
Apr 04 2017 22:13

@gabejohnson thanks, yeah it used to be a direct dependency, but sanctuary-def changes versions so rapidly that I couldn't keep up. I decided I'd set the version as a peer dep that I know works, but if I'm a few versions behind someone can live dangerously and inject their own. Once the api's for both libraries settle I'll go back to a direct dependency most likely.

There's going to be a lot of breaking changes coming up in sum-type, eventually, one thing I'd really like to fix is the initialization experience for users that have no idea what sanctuary-def is, and don't want to pass in env etc. But I think that will depend on upstream discussions, which would probably make it a sum-type 2.0 change.

The breaking changes I'm driving towards right now, are really just removing stuff, moving in a more static direction, removing prototype support, making case's behaviour more powerful but also predictable, uncurried constructors (the most common footgun I run into), support object literal style only, easier serialization for sending types over the wire, a lot of small changes that should make for a simpler library.

@Ramblurr I don't sorry :(

I've seen lots of back end code in that style, for a library, but large systems code, and especially front end code is exceedingly rare in open source in that style.

James Forbes
@JAForbes
Apr 04 2017 22:42

@miwillhite I've ranted a lot in the past about why I think Rx's whole "subject's are bad" is harmful.

Clearly subjects aren't bad per se, because they use them internally for things like fromEvent etc.

I think the reason they say pushing values into streams is bad, is because they are worried people will do it all the time instead of writing reactive code. And for their demographic (OO programmers) that is perfectly understandable.

But if you have a source of data (e.g. a virtual dom node) and you want to have a source stream for that event. Then naturally there's nothing wrong with { onclick: clicks }. Particularly when event listeners are already destroyed in element removal, which in the context of virtual dom isn't something you need to think about. And old streams will be gc'd automatically. In fact { onclick: clicks } is a lot simpler and declarative than obtaining the DOM node via a hook to pass to Rx so it can set up event listeners on its own separate to the framework. That's pretty ridiculous in my opinion. Particularly in frameworks like mithril, where event binding and redraw logic is handled by the framework automatically.

I do think we should as much as possible define streams in terms of mapping over a source stream, and we shouldn't push values into dependent streams (except when we know what we're doing in some rare, but powerful cases)

The reason its not simply misguided but harmful in my opinion to advocate for "subjects are bad": If you aren't allowed to compose subjects directly you end up requiring a massive list of predefined operators (e.g. Rx's standard lib) to do anything useful. And even worse, you need to name them, and store those names in your head! And even worse a lot of the names are proprietary and specific to Rx, or they reuse names from FP but incorrectly.

Flyd can do everything Rx can do. A lot of operators in Rx can be reproduced effortlessly in flyd simply because subjects are ok. Really with flyd all you ever need is map. The ability to push values into a stream, and retrieve the most recent value, is invaluable for client side development. If you can't do that you end up jumping through all these hoops on the quest of avoiding subjects that you have to invent entirely new UI programming paradigms. But flyd is also fantasy land compatible, so it works with ramda/sanctuary out of the box.

Most is a great library, but I think streams shouldn't be monadic, they should just be applicative functors. We shouldn't be using observables for everything, its like using div's for everything. There's no reason to have a nested stream in my experience. And there's no reason for a stream to have any notion of errors, we've got Either, Maybe, Future etc for that already.

There's so much wrong with the way we talk about streams/observables in my opinion. E.g. the constant comparison to promises, when they are useful for completely different things. Or thinking of streams as "observable" is a very imperative framing. Instead of thinking about "values that change over time" we should be thinking about "permanent relationships that never change". And focusing on time at all defeats the point of abstracting over it. I wish we called them Fact's instead of Streams, or Observables. But I think Streams is arguably closer to the truth than observables.

Think of a line plot. Rx thinks of streams as a series of dots on the graph. We should think of streams as a line or curve on that plot where the individual dots are not of any interest, but the relationship, or the equation is.

So y = x ^ 2not [{ x:0, y:0}, { x:2, y: 4}, { x: 3, y: 9 }, ...]

The fact there are discrete points on a line isn't important at all.

Anyway that's my rant on Rx/observables :D

Johnny Hauser
@m59peacemaker
Apr 04 2017 23:22
You're a boss, dude.
@JAForbes I realized just a bit ago that I've been needlessly using lenses. I caught onto doing this:
adjust(add(10), 1, data)
// instead of
over(lensIndex(1), add(10), data)
So, why have lenses?
Brian McKenna
@puffnfresh
Apr 04 2017 23:25
optics compose
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:26
Right, I suppose the other way doesn't, then. What's a situation where you'd need to compose them?
Brian McKenna
@puffnfresh
Apr 04 2017 23:26
I have a list of people and I want to change the street number of their address
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:26
Oh, neato.
Brian McKenna
@puffnfresh
Apr 04 2017 23:27
[{name: "Brian", address: {street: {no: 32, name: "George", suffix: "St"}}}]
James Forbes
@JAForbes
Apr 04 2017 23:27
And parent components can give their lenses to child components, and the child component can compose them and pass them to their child components without needing to know anything about the shape of the surrounding state
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:27
yeah, super neat.
James Forbes
@JAForbes
Apr 04 2017 23:27
Its a great standard, composable api for interacting with state
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:28
Should I just have lenses in my lib or do I need the adjust stuff as well?
Brian McKenna
@puffnfresh
Apr 04 2017 23:28
it's a great API for interacting with data
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:28
I don't see a reason to have both.
James Forbes
@JAForbes
Apr 04 2017 23:28
yeah just lenses I think
Brian McKenna
@puffnfresh
Apr 04 2017 23:28
optics tend to be more general
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:28
sweet
Brian McKenna
@puffnfresh
Apr 04 2017 23:28
but most forms of optics have laws
if you satisfy the laws, definitely an optic
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:30
I'm not quite there yet in understanding things.
My lens implementation does the same things as Ramda's. That's about as intelligent as I am about it. Laws and such... no idea.
Brian McKenna
@puffnfresh
Apr 04 2017 23:31
  • You get back what you put in
  • Putting back what you got doesn't change anything
  • Setting twice is the same as setting once
these are the lens laws
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:32
Sweet, I think I've got that.
Brian McKenna
@puffnfresh
Apr 04 2017 23:32
a prism is for optional or partial data
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:32
Does groupBy make any sense as a transducer?
I wrote it as one, but I don't think it's a thing that can be very generic.
  return (acc, value) => {
    const key = getKey(value)
    const group = getGroup(key, acc)
    const newGroup = putIntoGroup(value, group)
    return nextStep(acc, [ key, newGroup ])
  }
Brad Compton (he/him)
@Bradcomp
Apr 04 2017 23:35
It would only work in the last step, so I don't think so. Once you run it you get an object, which is not a reducible type in Ramda
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:36
Should I have a super generic thing like that and then you make array of objects-to-object-with-key, array-pairs-groupby out of that?
Brad Compton (he/him)
@Bradcomp
Apr 04 2017 23:38
:point_up: Is that your groupBy? I thought you were referring to R.groupBy
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:38
I made a transducer lib and a groupby like that, yeah
It's like this at the moment
const getKey = v => v.name
const getGroup = (key, coll) => coll[key] || []
const putIntoGroup = (value, group) => {
  group.push(value) // not really supposed to mutate, but it's ok here
  return group
}

const groupBy = getKey => nextStep => {
  return (acc, value) => {
    const key = getKey(value)
    const group = getGroup(key, acc)
    const newGroup = putIntoGroup(value, group)
    return nextStep(acc, [ key, newGroup ])
  }
}
I'm trying to figure out if it's worth abstracting that way. You could pass in your getGroup, putIntoGroup and derive a groupBy for something other than array to object.
Johnny Hauser
@m59peacemaker
Apr 04 2017 23:57
oh snap tho!
that won't compose!
pipe(groupBy(prop('name')), filter(v => v[1].length > 2))
I think a transducer would be filtering out items before they can collect :/