These are chat archives for ramda/ramda

4th
Mar 2017
Gabe Johnson
@gabejohnson
Mar 04 2017 00:32
@skatcat31 :thumbsup:
Johnny Hauser
@m59peacemaker
Mar 04 2017 03:47
how welcome are occasional off-topic questions?
If I'm wrong to ask, my apologies and I'll keep that in mind. But, does anyone understand this minimist test?
    t.deepEqual( parse([ '--no-moo' ]), { moo : false, _ : [] } ); // why is the result not {'no-moo': true}
I've never seen a cli arg that worked that way
Johnny Hauser
@m59peacemaker
Mar 04 2017 03:55
I'm wondering if this is something substack just liked or if this is common place for clis
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 03:59
I've never seen it before...
Looks like yargs does it too
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:00
I googled around and found a few examples of clis with --no-${x} options and {noX: true}makes sense
so, they just like to make it more convenient
{noX: true} === {x: false}
minimist is assuming too much. I'm making a version with far fewer concerns.
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 04:02
Are you writing an arguments parser?
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:03
Yeah. Just minimist without all the opinions.
unless you know of one already
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 04:04
I've never used minimist. I usually use yargs in node
But it isn't minimalistic at all
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:05
I'm doing something where I need to know whether the arg was actually passed in. minimist populates the result object with false values if they aren't given.
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 04:05
While it's not JS, clap is probably the best rust module I've had the pleasure to use.
Oh that's problematic
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:06
I actually started with yargs. It's too hard to be as precise as I need to be. minimist with stopEarly is fantastic, save for these undesired extra operations.
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 04:08
Most of the CLIs I build are just for internal use, so precision isn't really necessary
Most All
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:09
Kinda true in my case, but... it's quite complicated
It's my take on docker-compose, but... I prefer it greatly
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 04:10
That's cool
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:11
I made it just good enough to launch my product at work with it, but docker is still a pain.
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 04:11
It's frustrating when existing tools are just different enough from what you need that you can't use them
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:11
heheh yeah, I wouldn't say docker-compose is even close though
docker-compose + a ton of crazy make tasks
James Forbes
@JAForbes
Mar 04 2017 04:12
I usually use commander
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 04:13
@JAForbes That looks nice! I've never used it but I might try it next time
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:14
yargs and commander are ok for simple things, imo
James Forbes
@JAForbes
Mar 04 2017 04:14
Yeah I've tried others but for 1 reason or another I always go back to it.
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:14
too much magic, not enough functionalness!
dude, automatically searching ./bin for executables with a certain name - super not cool
James Forbes
@JAForbes
Mar 04 2017 04:16
I had this idea that I never used for the inverse situation (calling cli tools): https://www.npmjs.com/package/conff

@m59peacemaker yeah I never used sub commands, but technically its pure because you explicitly call parse(process.argv)

And it's searching its own directory for the sub commands.

So if you say npm install a cli, its not searching your working directory for matching names, its searching its own. Which is reasonable I think.

its not so different to say elm's cli
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:21
Yeah, it sounds good until you run into that edge case and there's no way to get control over what you need.
James Forbes
@JAForbes
Mar 04 2017 04:21
what's the edge case?
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:22
I recall having all the foo-bar foo-bazfoo-qux executables in bin, and then I needed a command that just could not be done that way, and I had to redo the whole setup to accomodate it
It was something very atypical, but I can't remember now
James Forbes
@JAForbes
Mar 04 2017 04:24

I'm finding it hard to imagine that, worst case scenario you just write a foo-qux that proxies to the actual scripts you want to call.

Its a lot like fantasy land's prefix dispatching if you tilt your head :D

Johnny Hauser
@m59peacemaker
Mar 04 2017 04:25
Right! but for real - that wasn't a possibility.
James Forbes
@JAForbes
Mar 04 2017 04:25
haha ok, but does that make commander bad, for not catering to something that is atypical :D?
@Bradcomp clap looks incredible
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:26
Nah, not necessarily. I just wish all of this stuff was built on top of better abstractions so you can just go down a layer when you want
Here's something I've mine I am proud of in that regard https://www.npmjs.com/package/babel-plugin-root-require
If you want something that is almost that, you can so easily build it.
James Forbes
@JAForbes
Mar 04 2017 04:30
that's fair
James Forbes
@JAForbes
Mar 04 2017 04:35
@m59peacemaker I like your file naming conventions, it kind of reminds me of https://www.youtube.com/watch?v=yD2ekOEP9sU
Johnny Hauser
@m59peacemaker
Mar 04 2017 04:36
neat. What naming conventions, though?
I didn't understand the compliment :)
James Forbes
@JAForbes
Mar 04 2017 05:11
long descriptive filenames
Johnny Hauser
@m59peacemaker
Mar 04 2017 05:12
ah. I wouldn't know any other way!
James Forbes
@JAForbes
Mar 04 2017 05:12
:D
Johnny Hauser
@m59peacemaker
Mar 04 2017 05:13
I have a friend that is quite good about descriptive names https://github.com/TehShrike/abstract-state-router/blob/master/index.js#L171
Brekk
@brekk
Mar 04 2017 12:22
Hey all, I've learned a bunch by lurking on this channel; recently published a primer http://codepen.io/brekk/post/functional-workaholism ; comments / critique / chastisements welcome.
James Forbes
@JAForbes
Mar 04 2017 13:09
@brekk brilliant!
The writing style is a lot like composition itself, gradually adding new layers building upon previous paragraphs, but in a structured way.
Rick Medina
@rickmed
Mar 04 2017 14:16
@brekk I like the focused approach! I didn't read it in detail but one thing you might check is idempotency. It is probably an overloaded concept but the main idea is that subsequent calls return the same value of the first one. In fp terms it would be something like f(f(a)) = f(a). It is also used to describe, eg, the HEAD http verb. I would use determinism to describe same inputs, same outputs. It is good that you group determinism and non side effects since both allow referential transparency which I guess is the most important benefit in fp. Hopefully, someone else can correct me if I'm wrong.
Brekk
@brekk
Mar 04 2017 14:20
👍🏽 awesome. My understanding was that there was a difference between mathematic-idempotency (as you are describing) and programmatic-idempotency; but that is a good call-out
Rick Medina
@rickmed
Mar 04 2017 14:46
@brekk :+1: It sucks to be that "well actually" guy (specially since I consider myself kind of a newbie really) but I've been learning lately about the importance of semantics (specifically, denotational semantics) in context of fp and I think it's important.
Brekk
@brekk
Mar 04 2017 14:47
@rickmed not at all, it’s important to get the correct terms for things. I’ve amended my post; thank you for the correction!
Rick Medina
@rickmed
Mar 04 2017 14:47
:)
Gabe Johnson
@gabejohnson
Mar 04 2017 15:12
@brekk great article! Higher-order functions take functions as inputs and/or return a function as a result.
Brekk
@brekk
Mar 04 2017 15:18
@gabejohnson thank you! good catch.
Gabe Johnson
@gabejohnson
Mar 04 2017 15:29
:thumbsup:
do you think the fp community is toxic?
Stefano Vozza
@svozza
Mar 04 2017 18:04
Can't speak for other languages but the Javascript FP community is amazing. So many genuinely nice people, all willing to help.
Brekk
@brekk
Mar 04 2017 18:09
:point_up: I’ve found that to be the case as well.
(The JS FP community is awesome.)
Travis LaDuke
@laduke
Mar 04 2017 18:21
news to me
Keith Alexander
@kwijibo
Mar 04 2017 19:06
@brekk very nice article.
Gabe Johnson
@gabejohnson
Mar 04 2017 19:22
I would say that there are FP oriented language communities that are somewhat insular. Some very opinionated members even seem to have distain for other methods/languages/communities
Drew
@dtipson
Mar 04 2017 19:28
speaking of terminology, where do people draw the line between just calling something a curried function and a function that has been run through something like curry? Various places call the latter "autocurrying" since the result can be called with any grouping of the arguments. So when an article like the above says "When we curry a function" there are two meanings in my mind "re-writing the function so that it takes its arguments one by one" vs "running a function through curry or curryN"
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 19:31
I'll call both versions a curried function, but sometimes I'll use the term 'manually curried function' for an a => b => c => {} type construct.
Drew
@dtipson
Mar 04 2017 19:39
Makes sense. I wonder if it's confusing to people that you can uncurryN a manually curried function, and then run curryN on it, and end up with a slightly different beast than what you started with
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 19:43
Yeah, that is a little odd... I don't think I've ever uncurried a function though...
Drew
@dtipson
Mar 04 2017 19:44
It's frustrating to have enough knowledge of these things that I want to teach/excite coworkers and others about it all to 1) know I still don't know enough to be 100% sure of anything I say and yet 2) have somewhat forgotten what pain points/concepts I really needed to understand when first learning it
Rick Medina
@rickmed
Mar 04 2017 19:44
I've wondered what is the use of uncurry as well
Drew
@dtipson
Mar 04 2017 19:46
in CT, curry and uncurry are isomorphisms, right?
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 19:49

I think so.Things aren't as ideal in JS because of the difference with Haskell in how functions are applied.

f a b c can be represented in JS as f(a, b, c) OR f(a)(b)(c) depending on if the function is 'curried', whereas in Haskell there's just the one form.

Drew
@dtipson
Mar 04 2017 19:49
right
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 19:50
The , for lack of a better term, Ramda-style curry tries to bridge the gap by making the two forms equivalent
@rickmed I think the main use case is to take a manually curried function and modify it so it can be used in a context that expects a function with a higher arity. For instance, reduce expects a function with the signature (a, b) -> a and would choke on a function with the signature a -> b -> a
Rick Medina
@rickmed
Mar 04 2017 19:55
got it. Also, I read in haskell is a way to transform the "multiple function call" to a one tuple call. Probably useful somewhere
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 20:10

:point_up_2: I think the FP JS community (at least the community around Ramda, Sanctuary, FL, etc. which is what I've experienced) is pretty great, and has been less toxic than other JS communities I've experienced. I think in general it can be hard to get started in FP, but all of the people here seem to me to be friendly and welcoming to newcomers.

While I haven't directly experienced toxicity in other FP communities, I've watched some drama unfold that has made me pretty sad, and I can see why FP has gotten a reputation as being unwelcoming.

Markus Pfundstein
@MarkusPfundstein
Mar 04 2017 20:37
@Bradcomp haskels f a b c is not equivalent to javascript f(a, b, c). the latter takes a triple (a, b, c) where a is member of set A, b of set B, c of set C. The former takes - as we all know - each argument one at a time.
f(a, b, c) is essentially f :: A x B x C while f(a)(b)(c) is f :: A -> B -> C
I use uppercase here to denote that we are taking about sets, and the x means cartesian product (hence a tuple or triple or whatever)
ramda mixes both up, which is mathematically not correct but much more practical of course
Rick Medina
@rickmed
Mar 04 2017 20:41
also f(a)(b) is unbearably unperformant
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 20:44
@MarkusPfundstein You're right. They aren't equivalent. My point was that in Haskell there is no special syntax for calling a curried function because all functions take one argument at a time.
@rickmed I guess it depends on what your performance constraints are. Have you run into situations where currying was a bottleneck in your programs?
Markus Pfundstein
@MarkusPfundstein
Mar 04 2017 20:46
jepp. I know. But if you say, f a b c can be represented as f(a)(b)(c) or f(a, b, c), I am not sure if that isn’t a bit misleading.
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 20:46
Good point :)
Rick Medina
@rickmed
Mar 04 2017 20:47
@Bradcomp no bc I don't use it like that. remember every time you f(a)(b) you are creating a new function. Is almost 2 orders of magnitude slower than a normal function call https://jsperf.com/cost-of-partial-application-and-currying
does not happen in ramda either bc of the syntactic currying.
Brekk
@brekk
Mar 04 2017 21:26
@kwijibo thanks! @rickmed that’s a good perf test, but I also think that the one of the primary trade-offs for FP is clarity / maintainability over speed? (I could be wrong on this, but at least in JS that seems to be the case?)
Mick Dekkers
@mickdekkers
Mar 04 2017 21:27
I wrote some code that normalizes an array of coordinates between 0 and 1. It works, but I'm still pretty new to Ramda/fp so it doesn't look very good.
Does anyone have any tips on how I can clean it up? http://preview.tinyurl.com/jpnqt8l (the goo.gl url shortener wouldn't work)
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 21:29
@SoullessWaffle Having trouble getting it to load. Maybe put it in a gist?
I'm not sure why the url shortening didn't work
Rick Medina
@rickmed
Mar 04 2017 21:37
@brekk absolutely -to a point. There is stuff that a language (eg: js) is not particularly designed to do so you have to be careful. Anyway, that should be a library's problem, not yours (eg lodash/ramda's reduce, map, etc is written in terms of a while loop and not the native method)
Brekk
@brekk
Mar 04 2017 21:38
:thumbsup: agreed!
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 21:41
@brekk @rickmed Agreed on all points! I generally don't create functions in the hot path, curried or no! There are a number of areas where FP in JS has some negative perf implications. Main one I've run into is with immutability / allocations if you do things in a naive manner.
@SoullessWaffle Your code isn't bad at all! If you're interested, I was trying to spin up something by treating the Bounding Box as a monoid.
Mick Dekkers
@mickdekkers
Mar 04 2017 21:43
@Bradcomp That sounds interesting! :D
I mainly dislike that the structure of the bounds object is essentially written twice, but maybe there's no getting around that
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 21:59
I don't know how much I am gaining for you, but it's fun ;)
const BoundingBox = (bounds) => ({
  get() { return bounds; },
  concat(other) {
    const {x, y, z} = other.get();
    const evolver = (bound) => ({
      min: min(bound.min),
      max: max(bound.max)
    });
    return BoundingBox(evolve({
      x: evolver(x),
      y: evolver(y),
      z: evolver(z)
    }, bounds))
  }
});
BoundingBox.empty = BoundingBox({
  x: {
      min: Number.POSITIVE_INFINITY,
      max: Number.NEGATIVE_INFINITY
    },
    y: {
      min: Number.POSITIVE_INFINITY,
      max: Number.NEGATIVE_INFINITY
    },
    z: {
      min: Number.POSITIVE_INFINITY,
      max: Number.NEGATIVE_INFINITY
    }
})
BoundingBox.fromPoint = ({x, y, z}) => BoundingBox({
  x: {
    min: x,
    max: x
  },
  y: {
    min: y,
    max: y
  },
  z: {
    min: z,
    max: z
  }
});
The empty isn't really necessary, but it lets us do this to construct the bounding box from the set of points:
const bounds = compose(reduce(concat, BoundingBox.empty), map(BoundingBox.fromPoint))(points)
Mick Dekkers
@mickdekkers
Mar 04 2017 22:06
oooh I like it!
Yeah, functional programming is a lot of fun :D
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 22:10
const BoundingBox = (bounds) => ({
  get() { return bounds; },
  concat(other) {
    const {x, y, z} = other.get();
    const evolver = (bound) => ({
      min: min(bound.min),
      max: max(bound.max)
    });
    return BoundingBox(evolve({
      x: evolver(x),
      y: evolver(y),
      z: evolver(z)
    }, bounds))
  },
  normalize(point) {
    const toRange = (bound) => bound.max - bound.min;
    const norm = (bound) =>(coord) => (coord - bound.min) / toRange(bound);
    return evolve({
      x: norm(bounds.x),
      y: norm(bounds.y),
      z: norm(bounds.z)
    }, point)
  }
});

//Empty and fromPoint as before

const bounds = compose(reduce(concat, BoundingBox.empty), map(BoundingBox.fromPoint))(points)
map(bounds.normalize, points);
Definitely a hybrid OO / FP approach here :stuck_out_tongue:
Mick Dekkers
@mickdekkers
Mar 04 2017 22:16
Nice! I like the evolvers, they make the code a lot more succinct
I did something similar here by creating a compareCoord function:
const compareCoord = (coord, point) => ({
  min: R.min(point[coord]),
  max: R.max(point[coord])
})

const getBounds = R.reduce((acc, point) => R.evolve({
    x: compareCoord('x', point),
    y: compareCoord('y', point),
    z: compareCoord('z', point)
  }, acc),
  {
    x: {
      min: Number.POSITIVE_INFINITY,
      max: Number.NEGATIVE_INFINITY
    },
    y: {
      min: Number.POSITIVE_INFINITY,
      max: Number.NEGATIVE_INFINITY
    },
    z: {
      min: Number.POSITIVE_INFINITY,
      max: Number.NEGATIVE_INFINITY
    }
  })
I think the partly OO approach makes sense in this case
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 22:19
Yeah, I took some inspiration from that ;)
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 22:25
If you don't like manually constructing the empty you could define a cube function
BoundingBox.cube = (min, max) => BoundingBox({
    x: {min, max},
    y: {min, max},
   z:  {min, max}
});

BoundingBox.empty = BoundingBox.cube(Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY);
Mick Dekkers
@mickdekkers
Mar 04 2017 22:40
Nice, those are some really good ideas
I'm putting all of this in an es6 class
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 22:45
Here is what I ended up with if it helps... I took out a bunch of the points from the array just to allow me to create the link
Mick Dekkers
@mickdekkers
Mar 04 2017 22:54
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 22:58
Sweet!
Markus Pfundstein
@MarkusPfundstein
Mar 04 2017 23:06
this is going to be interesting :-)
Brad Compton (he/him)
@Bradcomp
Mar 04 2017 23:09
Are you going to work through it?
Markus Pfundstein
@MarkusPfundstein
Mar 04 2017 23:26
i will skim i guess… this stuff takes time (need more 🙏)