These are chat archives for ramda/ramda

6th
Apr 2017
Robert Mennell
@skatcat31
Apr 06 2017 00:00
@ram-bot reduce(call, add)([1,2,3])
ram-bot
@ram-bot
Apr 06 2017 00:00
fn.apply is not a function
Robert Mennell
@skatcat31
Apr 06 2017 00:00
I'd avoid reduce( call ) TBH
Kurt Milam
@kurtmilam
Apr 06 2017 00:00
bad news?
Robert Mennell
@skatcat31
Apr 06 2017 00:01
@ram-bot const ad = x => y => x + y;apply(uncurryN(2, ad), [1, 2, 3]);
ram-bot
@ram-bot
Apr 06 2017 00:01
3
Kurt Milam
@kurtmilam
Apr 06 2017 00:02
@ram-bot
addled = x => y => x + y
reduce( call, addled )( [ 1, 2 ] )
ram-bot
@ram-bot
Apr 06 2017 00:02
3
Robert Mennell
@skatcat31
Apr 06 2017 00:04
@ram-bot uncurryN(2, x => y => x + y)(1,2,3)
ram-bot
@ram-bot
Apr 06 2017 00:04
3
Robert Mennell
@skatcat31
Apr 06 2017 00:04
so depends on how you want to pass things?
Kurt Milam
@kurtmilam
Apr 06 2017 00:04
I have a situation where I need to pass the same 2 arguments to three different functions.
so either apply( uncurryN ) or reduce( call ) would make it possible for me to do something like:
const applyTo = reduce( call )
const theArgs = [ 1, 2 ]
const doWithTheArgs = applyTo( theArgs )
doWithTheArgs( add )
doWithTheArgs( multiply )
doWithTheArgs( subtract )
Kurt Milam
@kurtmilam
Apr 06 2017 00:10
Which certainly look nice, at least.
Robert Mennell
@skatcat31
Apr 06 2017 00:10
This message was deleted
@ram-bot var theargs = [1,2];add(...theargs);
ram-bot
@ram-bot
Apr 06 2017 00:11
3
Robert Mennell
@skatcat31
Apr 06 2017 00:11
you can use a spread opperator to do that
Kurt Milam
@kurtmilam
Apr 06 2017 00:11
remember, my add is x => y => x + y
@ram-bot
addled = x => y => x + y
ram-bot
@ram-bot
Apr 06 2017 00:12
[Function: addled]
Robert Mennell
@skatcat31
Apr 06 2017 00:12
@ram-bot uncurryN(2)(x=>y=>x+y)(...[1,2])
ram-bot
@ram-bot
Apr 06 2017 00:12
3
Kurt Milam
@kurtmilam
Apr 06 2017 00:13
ok, I can live with that. Any insight into why reduce( call ) is a bad idea?
Robert Mennell
@skatcat31
Apr 06 2017 00:14
Kurt Milam
@kurtmilam
Apr 06 2017 00:14
gotcha.
thanks
Robert Mennell
@skatcat31
Apr 06 2017 00:16
so a good way to do this:
const uncurry2 = uncurryN(2);
const unaddledadd = uncurry2(x=>y=>x+z);
unaddledadd(...[1,2]);
Kurt Milam
@kurtmilam
Apr 06 2017 00:17
That'll do the trick, thanks!
Robert Mennell
@skatcat31
Apr 06 2017 00:18
of course this is helpful if you always recieve them as an array... If not you might want to put in a check branch
Kurt Milam
@kurtmilam
Apr 06 2017 00:27
that's actually still not as nice as reduce( call ), since I'd need to uncurry2 each of the three functions I need to call with my args. Hmm.
Robert Mennell
@skatcat31
Apr 06 2017 00:28
const uncurry2 = uncurryN(2);
uncurry2(x=>y=>x+y, ...[1,2]);
thankfully these functions are curried in Ramda so it'll just kind of work
or are you looking to specifically call them in teh manner you outlined before?
Robert Mennell
@skatcat31
Apr 06 2017 00:36
@kurtmilam
Kurt Milam
@kurtmilam
Apr 06 2017 00:42
sorry, was shortly afk
I think reduce( call ) is more flexible and the code looks nicer. I have a sneaky feeling this is similar to the Reader monad, where my array of arguments would be the context.
Robert Mennell
@skatcat31
Apr 06 2017 00:50
in this use case it very well may be and since you know the dangers of how it can break, then you're good
Kurt Milam
@kurtmilam
Apr 06 2017 00:50
const applyUnary = R.reduce( R.call )
const applyUnaryTo = R.flip( applyUnary )
const withFocus = applyUnaryTo( [ x, lens ] )
// lens operations
withFocus( viewOn )
withFocus( overOn )( fn )
withFocus( setOn )( value )
I'll read up on the Reader monad again and see whether I can finally grok it with this context in mind.
Thanks again for your help and input!
Note: I won't be calling R.* with withFocus. Would be a waste since they're already curried. I will be calling similar lens functions that are not curried like Ramda's.
Rodrigo Álvarez
@Papipo
Apr 06 2017 07:04
Does always() memoize the value?
Rodrigo Álvarez
@Papipo
Apr 06 2017 07:22
I have an array and a function. I want to create an object where the keys are the array values and the values the function applied to those keys
Is there anything for that?
Jonah
@jonahx
Apr 06 2017 07:29
@Papipo
const f = x => x*x
pipe(map(x => [x, f(x)]), fromPairs)([2, 3])
// => {"2": 4, "3": 9}
Rodrigo Álvarez
@Papipo
Apr 06 2017 07:33
I have used this although I don't know if it works yet
R.zipObj(lenses, R.map(this.props.atom.view, lenses))
Eugene Lerman
@airbugg
Apr 06 2017 08:14

Hi guys - I have a somewhat abstract question regarding building a functional (stateful, though) mock node.js. I'm trying to rebuild our current in-memory express node server (used for mocking a db and simple CRUD api calls) in a functional manner. I've been doing functional front-end work for quite some time (ramda was indispensible for me in that regard), however I find myself struggling with rewriting our simple (yet convoluted) server in a functional manner. To give a somewhat more concrete example, let's consider the following piece of code:

router.get('/app-name/users', (req, res) => {
  const pageSize = parseInt(req.query.pageSize) || 30;
  const query = req.query.query;
  const cursor = req.query.cursor;
  const {users, nextCursor} = processItems({query, currCursor: cursor, pageSize});
  setTimeout(() => {
    res.send({items: users, nextCursor, total: numOfUsers});
  }, 0);
});

In the following piece of code, processItems is a function that has access to the global mock-db, so it's not pure.
How would one go about rebuilding this in a more functionally-idiomatic way, without constantly rebuilding the mock-db state?

Denis Stoyanov
@xgrommx
Apr 06 2017 08:19
@ram-bot
const W = x => y => x(y)(y)

compose(map(W(multiply)), W(zipObj))([2, 3])
ram-bot
@ram-bot
Apr 06 2017 08:19
{ '2': 4, '3': 9 }
Denis Stoyanov
@xgrommx
Apr 06 2017 08:20
W or join for function (monadic join) will be helpful :smile:
const joinM = chain(identity)
Bravi
@Bravilogy
Apr 06 2017 09:10
which front end framework do you guys use? I have developed this urge to get rid of this everywhere and I'm always looking for some good FP frameworks out there. I really like Cycle.js for that reason. At work it's React for me and I always try to stick with functional components rather than classes
Kurt Milam
@kurtmilam
Apr 06 2017 09:12
mithril is interesting.
small and supposedly fast.
Rodrigo Álvarez
@Papipo
Apr 06 2017 09:14
I love mithril
Kurt Milam
@kurtmilam
Apr 06 2017 09:14
:thumbsup:
Eugene Lerman
@airbugg
Apr 06 2017 09:19

Hi guys - I have a somewhat abstract question regarding building a functional (stateful, though) mock node.js. I'm trying to rebuild our current in-memory express node server (used for mocking a db and simple CRUD api calls) in a functional manner. I've been doing functional front-end work for quite some time (ramda was indispensible for me in that regard), however I find myself struggling with rewriting our simple (yet convoluted) server in a functional manner. To give a somewhat more concrete example, let's consider the following piece of code:

router.get('/app-name/users', (req, res) => {
  const pageSize = parseInt(req.query.pageSize) || 30;
  const query = req.query.query;
  const cursor = req.query.cursor;
  const {users, nextCursor} = processItems({query, currCursor: cursor, pageSize});
  setTimeout(() => {
    res.send({items: users, nextCursor, total: numOfUsers});
  }, 0);
});

In the following piece of code, processItems is a function that has access to the global mock-db, so it's not pure.
How would one go about rebuilding this in a more functionally-idiomatic way, without constantly rebuilding the mock-db state?
anyone? :)

Raine Virta
@raine
Apr 06 2017 09:34
why not pass mock db as argument?
Eugene Lerman
@airbugg
Apr 06 2017 09:37
to processItems? I thought of it, but wouldn't it just mean that the router.get call would have to be aware of a global mock db object?
Raine Virta
@raine
Apr 06 2017 09:38
not sure what you mean by being aware
you can require it in the same file or pass it as an argument to the module
Rodrigo Álvarez
@Papipo
Apr 06 2017 09:39
if you are passing it it's not global
Raine Virta
@raine
Apr 06 2017 09:39
passing is preferable to global, mostly
Kurt Milam
@kurtmilam
Apr 06 2017 10:17
@ram-bot
const applyUnary =
  R.reduce( R.ifElse( R.is( Function )
                    , R.call
                    , R.reduced
                    )
          )
const applyUnaryTo = R.flip( applyUnary )
const doWithTooManyArgs = applyUnaryTo( [ 1, 2, 3, 4, 5 ] )
doWithTooManyArgs( x => y => x + y)
ram-bot
@ram-bot
Apr 06 2017 10:17
3
Kurt Milam
@kurtmilam
Apr 06 2017 10:17
@skatcat31 Thought you might like that ^^
Now I'd just like to know whether that's reinventing the wheel / whether there's already a name for that.
James Forbes
@JAForbes
Apr 06 2017 10:36
Another mithril user over here :D
Rodrigo Álvarez
@Papipo
Apr 06 2017 10:44
Can I somehow provide a default value for a lens that returns undefined?
Kurt Milam
@kurtmilam
Apr 06 2017 10:45
which lenses are you using?
James Forbes
@JAForbes
Apr 06 2017 10:45
A default value in a lens would break the lens laws, you can do it, but it'd be an illegal usage. partial.lenses is built on taking that idea as far as you can
you could do it by just using pathOr or propOr when defining the lens, but again, breaking laws
Brian McKenna
@puffnfresh
Apr 06 2017 10:46
you can write a view function which does it
just not part of the lens
James Forbes
@JAForbes
Apr 06 2017 10:46
yeah good point
Rodrigo Álvarez
@Papipo
Apr 06 2017 10:46
yeah, that sounds better
James Forbes
@JAForbes
Apr 06 2017 10:47
but I mention pathOr, propOr simply so you don't feel you need to use a different library to achieve that kind of behaviour, ramda can do it
Rodrigo Álvarez
@Papipo
Apr 06 2017 10:53
Now that I think of it my use case is more complex. I guess this is why I wanted to use atoms instead of streams. I have this big state object which stores streams, components can access them by providing paths to them, which are then interpreted via lenses. The thing is, if the path does not contain a stream already, I should put it there and then return it.
Kurt Milam
@kurtmilam
Apr 06 2017 10:55
@Papipo I ask for the stream and if it doesn't exist, create it.
If it does exist, I return it.
Rodrigo Álvarez
@Papipo
Apr 06 2017 10:57
yeah, nothing fancy
Kurt Milam
@kurtmilam
Apr 06 2017 10:57
I also register my streams and keep them separate from the actual state object. Looks like: { state:{}, streams:[] }
That way I'm not mixing my data with my accessors. I also use atoms (or at least constructs that I'm calling atoms).
Rodrigo Álvarez
@Papipo
Apr 06 2017 11:02
I am experimenting and learning at the same time, so...
:D
Kurt Milam
@kurtmilam
Apr 06 2017 11:03
Me too :D
And having plenty of fun while doing it
Rodrigo Álvarez
@Papipo
Apr 06 2017 11:04
Same here
Bravi
@Bravilogy
Apr 06 2017 11:15
@kurtmilam @Papipo yeah I've tried mithril too and I like it very much. started with the old version
Kurt Milam
@kurtmilam
Apr 06 2017 11:58

I'm stumped as to why this works:

return a => R.ifElse( isUndefined
                      , _ => withAtom( viewOn )
                      , compose( L.get( lens ) )( withAtom( setOn ) )
                      )( a )

whereas this doesn't:

return R.ifElse( isUndefined
                      , _ => withAtom( viewOn )
                      , compose( L.get( lens ) )( withAtom( setOn ) )
                      )
What completely obvious thing am I overlooking?
Kurt Milam
@kurtmilam
Apr 06 2017 12:14
Replace compose( L.get( lens ) )( withAtom( setOn ) ) with R.compose( L.get( lens ), withAtom( setOn ) ) and the behavior persists.
Ok, think I figured it out. Nothing executes in the a => ... ( a ) version until a is supplied, whereas I believe the L.get( lens ) is executing immediately in the other version.
Jonah
@jonahx
Apr 06 2017 12:24
@xgrommx are you currently using hyperapp over mithril?
Rick Medina
@rickmed
Apr 06 2017 13:21
@airbugg are you comfortable with monads? maybe Reader can help
otherwise, pass it down somehow
Raine Virta
@raine
Apr 06 2017 13:35
would be quite interested in seeing usage of Reader in real world, in a way that makes code easier to reason about
Rick Medina
@rickmed
Apr 06 2017 13:44
I think @MarkusPfundstein showed a good example in the fl room needing several db setups
Matthew Willhite
@miwillhite
Apr 06 2017 14:10

@kurtmilam The other thing you could do is wrap the branches in thunks:

return R.ifElse( isUndefined
                      , _ => withAtom( viewOn )
                      , a => compose( L.get( lens ) )( withAtom( setOn ) )(a)
                      )

Not tested, but I’ve taken that approach when I want something to be “lazy"

Rick Medina
@rickmed
Apr 06 2017 14:22
@raine or do you think you would prefer partially apply them globally something like that?
Raine Virta
@raine
Apr 06 2017 14:27
@rickmed well, probably
Rick Medina
@rickmed
Apr 06 2017 15:05
@raine ok, yeah, I guess as always, it depends on the context
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 15:54
@raine here I use Reader.T(Task) to control dependency injection https://gist.github.com/MarkusPfundstein/246446e428ab10dbd2383cf3204a19d0
and here a server side example using express https://gist.github.com/MarkusPfundstein/dece230b540a3cf4053c0af0ee31f709
:point_up: April 6, 2017 5:54 PM @airbugg
Robert Mennell
@skatcat31
Apr 06 2017 16:11
@kurtmilam :point_up: April 6, 2017 3:17 AM Great way of handling it for a data first method with static data
Matthew Willhite
@miwillhite
Apr 06 2017 16:12
Great examples @MarkusPfundstein, thanks for sharing.
Vesa Karvonen
@polytypic
Apr 06 2017 16:13
@kurtmilam I'm also curious to see what you've come up with.
Vesa Karvonen
@polytypic
Apr 06 2017 16:43

@JAForbes:

A default value in a lens would break the lens laws, you can do it, but it'd be an illegal usage.

FWIW, I think calling it illegal can be misleading. :)

It is common practise to define optics that do not necessarily satisfy some laws that have been formulated for optics. Such optics can still be useful and may satisfy different, and still reasonable and useful, sets of laws.

Robert Mennell
@skatcat31
Apr 06 2017 16:44
@polytypic I think what he's trying to say is at that point it looks like a lens, talks like a lens, but it's really a duck because when you poke the lens expecting it to do nothing, it does something you don't expect.
which is bad
Vesa Karvonen
@polytypic
Apr 06 2017 16:49
@skatcat31 Can you give an example where you'd be surprised? Why would you poke a lens expecting it to do nothing?
Ryan Stegmann
@rstegg
Apr 06 2017 16:50
@xgrommx sweet hyperapp reference!
Vesa Karvonen
@polytypic
Apr 06 2017 16:51
@skatcat31 Also, note that you are not responding to what I said. What I said is that it is not at all uncommon to define optics that do not satisfy certain laws for optics and that can be useful.
Ryan Stegmann
@rstegg
Apr 06 2017 16:52
@polytypic i know, right?
Vesa Karvonen
@polytypic
Apr 06 2017 16:53
@rstegg ?
Ryan Stegmann
@rstegg
Apr 06 2017 16:54
i thought you were forming an argument, my bad
Robert Mennell
@skatcat31
Apr 06 2017 17:03
@polytypic How is that not responding? I'm pointing out it passes all the tests for a lens except one: it provides a default. Now export that and hand it to someone who understands lenses. Imagine their suprise when their 'lens' provides a value when it shouldn't and now their data model didn't behave. They now have ot dig through code to find out what happened. Documentation is now needed. It'd be better, more predictable practice to keep it private, or instead provide a function to initialize in the specific one use case you need. That way the lens can still apply to every model it should, and it won't provide a default when you wouldn't want it to when inheriting
@polytypic now you're right they have a ton of use cases to use optics that AREN'T lenses by definition of adherence, but most of those use cases, if not all, is also probably made more generic by using a defaulter function for lazy evaluation and keeping fewer lenses around
Vesa Karvonen
@polytypic
Apr 06 2017 17:09
@skatcat31 Interesting. Are you familiar with lens laws? What do you mean by "private" and "inheriting" in this context? I'm curious, are you making extensive use of optics in your programming?
Robert Mennell
@skatcat31
Apr 06 2017 17:21
@polytypic programming scope: private versus public. If you export with this optic that does not obey put (get a) a = a && get (put b a) = b then someone will pick it up, and be frustrated because they expected a lens. Javscript has scope and things can be made private to a scope if this use case is desired. Inehritance: implimenting something that satisfies get a == null ? get a : default is easy enough in places that need it so much so that pipe(get(alens), defaulta) is a common practice, so a lens can stay a lens and return a default
@polytypic javascript kind of has one built in: something['key'] is a lens: o[k] = o[k] will not mutate the data. If it's undefined, it's now REALLY undefined XD
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 17:41
@polytypic if you break the laws, your lens is not a lens anymore but some custom object. its dangerous to then still call it a lens and will only lead to errors and bad surprises down the road
Robert Mennell
@skatcat31
Apr 06 2017 17:41
@polytypic
:point_up: April 6, 2017 10:21 AM Okay that lens 'example' fails miserably(not composable). So instead I'll go to where I use them A LOT: Database interaction. I program our database and state management libraries for real time data aggregation. To keep them working with a common API and predictable behaviour I abstract them into lenses in such ways they are composable and won't mutate the state in use.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 17:43
laws are non negotiable
Robert Mennell
@skatcat31
Apr 06 2017 17:43
:+1:
Vesa Karvonen
@polytypic
Apr 06 2017 17:44
Oh dear. :)
Robert Mennell
@skatcat31
Apr 06 2017 17:44
@MarkusPfundstein however to be fair, he did refer to it as optic, not lens per say
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 17:45
@polytypic seriously, thats why we love fp. because we can trust the laws
Robert Mennell
@skatcat31
Apr 06 2017 17:46
@polytypic realized I missed something you asked:
var a = {};
var alens = lensProp('a');
get(alens, a) == undefined;
Vesa Karvonen
@polytypic
Apr 06 2017 17:48
Yes, I very much like FP too. I've been programming since about 1990 and mostly using FP since about 2001 when we implemented a simple compiler for a simple ML style language in OCaml with a colleague.
I'm sorry that I haven't been writing Monad tutorials for you guys: https://groups.google.com/forum/#!topic/comp.lang.haskell/jjA2YaU8TN0
I think I can fairly say that on the infamous ladder, I can say that I'm at the expert level.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 17:52
did you refer to lenses when talking about optics? or is optics some kind of different concept?
Robert Mennell
@skatcat31
Apr 06 2017 17:53
:point_up: April 6, 2017 9:51 AM @MarkusPfundstein we started a bit before this but I think this got misconstrued somewhere
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 17:54
yeah but does he mean lens?
probably
ok @polytypic , so if you were talking about lenses, then you say its ok to violate laws if it serves a practical use right?
do you think that is also true for monads or functors? Like, if it suits me, can I define a functor that violates one of the laws if I think there is a practical use for it?
Robert Mennell
@skatcat31
Apr 06 2017 17:57
@MarkusPfundstein @polytypic I think this conversation should be ended. We all understand what a lens is, and I think it's clear at this point that we all agree in some cases you may ignore the laws to do something. you probably wouldn't propigate it to other people, but in this single use case as longas you'd done it right, and safely, it should be fine to do it here to save effort in the long run
Vesa Karvonen
@polytypic
Apr 06 2017 17:57
TBH, I think that you lack understanding of how lenses are used in practise.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 17:57
I totally disaggree 10000000 times
Robert Mennell
@skatcat31
Apr 06 2017 17:57
@polytypic if I offended you in some way I did not mean too. Text communications contain one major downfall: tone
Vesa Karvonen
@polytypic
Apr 06 2017 17:58
What you do with optics is that you define accessor for your data model.
Robert Mennell
@skatcat31
Apr 06 2017 17:58
@polytypic optics != lens
Vesa Karvonen
@polytypic
Apr 06 2017 18:00
Yes, I know. Lenses ⊂ Optics. :)
Robert Mennell
@skatcat31
Apr 06 2017 18:00
whoops... sorry @polytypic was trying to @MarkusPfundstein on optic != lens
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 18:01
i didn’t know the word optic. in the link above they say lenses are part of optics, so ok. but that means if he talks about optics, he talks about lenses too.. nevertheless it doesn’t matter Lenses or Glasses or whatever. He advocates in a public forum that its ok to break laws.. and I challenge him on that
Robert Mennell
@skatcat31
Apr 06 2017 18:02
@MarkusPfundstein taht is a much better way of putting it
@MarkusPfundstein however @polytypic did not advocate for breaking laws, I don't think
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 18:03
@skatcat31 Also, note that you are not responding to what I said. What I said is that it is not at all uncommon to define optics that do not satisfy certain laws for optics and that can be useful.
Robert Mennell
@skatcat31
Apr 06 2017 18:04
@MarkusPfundstein lens: laws applied to optics in a specific case I think is what he was refferring too.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 18:04
ok maybe not directly advocating it, but justifying it…
Gabe Johnson
@gabejohnson
Apr 06 2017 18:06
@MarkusPfundstein his definition of partial lens is adequate I think
Vesa Karvonen
@polytypic
Apr 06 2017 18:06
When you have a specific data structure and specific requirements for operations that you need, then it doesn't really matter whether your use of optics breaks laws or not as long as they produce the results that you want.
Gabe Johnson
@gabejohnson
Apr 06 2017 18:08
it's like "semigroup" isn't a "group" (no identity or inverse element) but it's still useful
Vesa Karvonen
@polytypic
Apr 06 2017 18:08
Yes, that is also a part of it.
Gabe Johnson
@gabejohnson
Apr 06 2017 18:11
@polytypic is a "partial lens" the same as a prism?
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 18:11
@gabejohnson sure , but in your function that takes Group, you would not allow someone to put in a semigroup right?
Gabe Johnson
@gabejohnson
Apr 06 2017 18:11
@MarkusPfundstein I wouldn't, but this is JS
unless that function was or used concat
Robert Mennell
@skatcat31
Apr 06 2017 18:12

@polytypic I'm pretty sure I may ahve started this over something stupid, so I'm going to refine my comment with less words:

just don't call your optic a lens if it isn't a lens, because optics are close enough to lenses that if someone comes along and tries to use it in such a way that it does something they didn't expect it can lead to problems. You should probably keep them scope limited, or make sure they know well enough in advance that it isn't a lens, but something more akin to pipe( get(lens), when, isNil, always('default') ) like behaviour

which is probably more useful for people coming after than for those in, and was based of an assinine assumption I made about what you were referncing, applogies
Vesa Karvonen
@polytypic
Apr 06 2017 18:18
Think of your data structure / data model as a tree. Make a single cut to the tree to take out a branch (including everything to the leaves in that branch). You then use optics to create operations for manipulating those branches in ways that satisfy the invariants of that branch. In such a case it doesn't generally matter whether those optics satisfy all laws for optics. Indeed, you are not composing anything after/to-the-right-of those optics, because you are already dealing with the leaves. OTOH, it does matter that you can compose optics before/to-the-left-of those optics so that you can get to those branches.
@skatcat31 Yes, the partial lenses library documentation clearly states that the optics in that library are partial.
Robert Mennell
@skatcat31
Apr 06 2017 18:21
@polytypic like I said, was based on an assinine assumption I made -.-; It shows how often we just assume and go 'Oh, he passed it ot get, must be a lens. Wait where'd that come from?' in our practices
@polytypic after all duck casing is REALLY common practice in JS
@polytypic can't tell you how many times I've gotten angry at this references with JS... soemtimes I want to go back to C/++... then I remember what a horrible place that was back when I used C/++
Vesa Karvonen
@polytypic
Apr 06 2017 18:27
Yes, I used to use C++ too.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 18:30
ok, so in general you would advocate for lawful optics but when dealing with leaves you say it doesn’t matter anymore right?
Vesa Karvonen
@polytypic
Apr 06 2017 18:33
Note that the leaf example is just an example of a general situation where the laws don't matter as much.
But, yes, I very much like to have suitable laws to help reasoning.
Rodrigo Álvarez
@Papipo
Apr 06 2017 18:35
How would you write this in a ramda-ish way?
  streamFor(prop) {
    let lens = R.lensProp(prop)
    let stream = R.view(lens, this.streams)
    if (stream === undefined) {
      stream = flyd.stream()
      this.streams = R.set(lens, stream, this.streams)
    }
    return stream;
  }
Robert Mennell
@skatcat31
Apr 06 2017 18:38
@polytypic knew that last name seemed familiar...
Vesa Karvonen
@polytypic
Apr 06 2017 18:44
@gabejohnson No, prisms are not precisely the same as partial lenses (as defined in my lib). All kinds of optics are of course related.
Robert Mennell
@skatcat31
Apr 06 2017 18:49
@MarkusPfundstein pipe( view(lens), when, isNil, always('default') ) is really annoying to type for EVERY leaf XD! However this "inherited" optic is REALLY useful( the function behaves like a view(lens)) and I'm pretty sure you see a use case for it
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 18:50
sure I do
its just no optic if it violates one of the laws
but nevermind
Robert Mennell
@skatcat31
Apr 06 2017 18:53
which law?
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 18:56
it doesn’t matter which law :-) thats the whole point.. but lets cut it now. we spam the whole thread
The whole chapter actually
Johnny Hauser
@m59peacemaker
Apr 06 2017 19:04
Does anyone recognize this?
fn([ { a: 1, b: 1 }, { a: 2 }, { a: [ 1 ] } ])  // => { a: [ 1, 2, [ 1 ] ], b: 1 }
Vesa Karvonen
@polytypic
Apr 06 2017 19:05
@MarkusPfundstein Note that one use for "defaults" in partial lenses is just to specify required data structure parts on the way to the optional leaf element being accessed. IOW, the "default" is kind of boilerplate data that must exist in the (JSON) data structure and you specify such "defaults" as part of the partial lens. Such defaults don't fundamentally break any laws (GetPut and PutGet can still be fully satisfied).
@MarkusPfundstein Also, it can be useful to be able to compose optics in such a way that while the composition as a whole satisfies laws, a subcomposition might not.
Robert Mennell
@skatcat31
Apr 06 2017 19:08
Johnny Hauser
@m59peacemaker
Apr 06 2017 19:08
Not quite
It's almost mergeWith(concat), but that would bust that array value
oh wait, you can't concat anyway.
Matthew Willhite
@miwillhite
Apr 06 2017 19:11
@m59peacemaker This looks a lot like what you were trying to do a couple weeks ago :suspect:
Johnny Hauser
@m59peacemaker
Apr 06 2017 19:11
It is similar!
The best way to do that turned out to be reduceBy
I just tried that and it didn't work out, but it's possible I have utterly confused myself for the time being.
I did learn that I could do this:
pipe(
  map(map(of)),
  reduce(mergeWith(concat), {}),
  map(v => v.length > 1 ? v : v[0])
)(data)
that's one way I solving it
kinda janky, though
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 19:16
@polytypic also PutGet?
if view returns a default on undefined, and I set undefined first , it would return the default instead of undefined
Vesa Karvonen
@polytypic
Apr 06 2017 19:17
@MarkusPfundstein Also, if you think of so called polymorphic lenses (the s t a b parameters), where the type of the input s may be different from the type of the output t, then such lenses obviously don't satisfy naïve GetPut/PutGet laws.
Johnny Hauser
@m59peacemaker
Apr 06 2017 19:18
In that solution, I'm anticipating the conflict and putting everything into arrays so they can be concatted, and then later unwrapping the one's where there was no conflict. Just writing a manual reduce is more practical.
Vesa Karvonen
@polytypic
Apr 06 2017 19:20
@MarkusPfundstein Consider L.compose(L.define([]), L.find(R.whereEq({id})), L.defaults({id}), L.prop("value")).
Johnny Hauser
@m59peacemaker
Apr 06 2017 19:32
Here's an atrocity that does solve it:
const mergeByGrouping = (arrayOfObjects) => {
  const groupedKeys = {}
  return arrayOfObjects.reduce((acc, v) => {
    Object.entries(v).forEach(([k, v]) => {
      if (has(k, acc)) {
        if (groupedKeys[k]) { 
          acc[k].push(v)
        } else {
          groupedKeys[k] = true
          acc[k] = [ acc[k], v ]
        }
      } else {
        acc[k] = v 
      }
    })
    return acc         
  }, {})
}
Vesa Karvonen
@polytypic
Apr 06 2017 19:36
@MarkusPfundstein That example composition is for manipulating arrays of objects with an id property and where the ids are supposed to be unique, which is an invariant of the data structure. Such data structures, kinds of association lists, are quite common IME. Partiality makes such a composition actually useful (can insert and remove) when compared to total lenses (can only view and update and those operations are partial anyway, so you can't actually use (total) lenses).
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 19:36
yeah I am on it
can you show me for which input L.define and L.defaults is necessary?
ah
if I remove
ok
Vesa Karvonen
@polytypic
Apr 06 2017 19:40
The purpose of L.define([]) is to enforce the invariant that the array always exists. So, if you start with an array and remove the last element, then you still get an array.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 19:40
yeah I see. when I change the 100’s to undefined
const IN = [{id: 6, value: 8}, { id: 10 }];

log(L.set(lens2, L.get(lens2, IN), IN))
yeah that breaks the GetPut law (with your lens composition above)
but I see (!) the utility.
quite cool library actually
Vesa Karvonen
@polytypic
Apr 06 2017 19:44
That is cheating: Your input does not satisfy the data structure invariants.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 19:45
because value is missing?
Vesa Karvonen
@polytypic
Apr 06 2017 19:45
Yes. [{id: int, value: int}] <- The type of the data structure and also with the invariant that ids must be unique.
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 19:46
but the whole purpose of defaults is that data can be missing right
Vesa Karvonen
@polytypic
Apr 06 2017 19:47
In that usage the purpose of the defaults is to allow one to use the partial lens to insert new values into the "assoc list" (or remove - it actually serves both purposes).
(IOW, I mean to say that the defaults part is about writing rather than about reading.)
Markus Pfundstein
@MarkusPfundstein
Apr 06 2017 19:48
ok I see
const IN = [{id: 6, value: 8}];
log(L.set(lens2, 100, IN))
will insert with id : 10
Johnny Hauser
@m59peacemaker
Apr 06 2017 20:09
Is this a good name?
mergeBy(
  (acc, b) => { // accumulated value from conflicts, new conflicting value
    acc.push(b)
    return acc
  },
  (a, b) => [ a, b ], // on first conflict
  data
)

// for example, you could do a regular merge this way:
mergeBy(
  (acc, b) => b,
  (a, b) => b,
  data
)
Jonah
@jonahx
Apr 06 2017 20:24

here’s one of those disheartening examples where i cannot find a ramda solution to a problem that is as succinct and clear as the procedural one. given an array, return an array showing the number of times each element has appeared previously in the list (the first appearance should return 1, so it’s inclusive). eg:

const sample = [1, 2, 1, 1, 2, 3, 4, 3, 4, 5]

const cnts = {}
sample.map(x => {
  cnts[x] = has(x, cnts) ? cnts[x] + 1 : 1  // <— mutation of temp variable
  return cnts[x]
})
// #=> [1, 2, 1, 1, 2, 3, 4, 3, 4, 5]  <—input array
// #=> [1, 1, 2, 3, 2, 1, 1, 2, 2, 1] <— return array: previous appearance counts for each element

even allowing for the functional solution to have worse big-oh performance, i cannot create one in ramda that is as simple as the above. i’d love to know if it’s possible.

Rodrigo Álvarez
@Papipo
Apr 06 2017 23:48
@jonahx I can't believe I did it: https://goo.gl/3SlAER
ram-bot
@ram-bot
Apr 06 2017 23:51
Unexpected token )