These are chat archives for ramda/ramda

6th
Sep 2015
Danielle McLean
@00dani
Sep 06 2015 00:04 UTC
I figure it's basically just injecting a dependency, which is Good Designâ„˘ as much as it is specifically accommodating testing.
hemanth.hm
@hemanth
Sep 06 2015 07:45 UTC
If you were to pick and imperative code and convert it to functional using ramda to explain the features of FP, which code would that be?
Simon Friis Vindum
@paldepind
Sep 06 2015 08:15 UTC

@hemanth Depends on the size of the example

const max = R.reduce(R.max, 0, [3, 4, 1, 9 ,3]);

Imperative code would be something like this:

let max = 0;
for (let i of [3, 4, 1, 9 ,3]) { max = R.max(max, i); }
hemanth.hm
@hemanth
Sep 06 2015 08:17 UTC
@paldepind looking for some example that would involve the basics, so it would be nice to explain it to a huge group who are new to FP, thanks!
Explaining the hows are easy, whys are though...
Simon Friis Vindum
@paldepind
Sep 06 2015 08:20 UTC
The important difference in the example above is that in the FP code I can let max be const. In the other I have to make it a let. IMO this highlights the key difference between imperative and functional code. The first is a definition of what max is. The second is a sequence of steps that ends of with max having the correct value.
hemanth.hm
@hemanth
Sep 06 2015 08:20 UTC
@paldepind Why not Math.max.apply(null,[3,4,1,9,3]) ?
Simon Friis Vindum
@paldepind
Sep 06 2015 08:22 UTC
@hemanth Well. Let's say we don't have that function and that we want to define it.
hemanth.hm
@hemanth
Sep 06 2015 08:23 UTC
hmm
Simon Friis Vindum
@paldepind
Sep 06 2015 08:24 UTC
Of course that example in itself does not really show how FP is better.
hemanth.hm
@hemanth
Sep 06 2015 09:12 UTC
Agree, will have set of examples
BTW, is this functional enough?
let map = function(f, args) {
let [x, ...xs] = args;
if (x === void 0) {
return [];
} else {
return [f(x)].concat([].slice.call(map(f, xs)));
}
};
if let [x,...xs] = args is avoided it would be nice...
Simon Friis Vindum
@paldepind
Sep 06 2015 09:46 UTC
@hemanth Well. I don't like it. You get the head and the tail of the list before you check if it is empty. I'd much rather do the destructuring after you've checked if the list is empty.
hemanth.hm
@hemanth
Sep 06 2015 09:47 UTC
@paldepind Makes sense, trying to implement reduce in a similar manner
Simon Friis Vindum
@paldepind
Sep 06 2015 09:48 UTC
Something like this:
const map = (f, list) =>
list.length > 0 ? [f(list[0])].concat(map(f, list.slice(1)) : []
hemanth.hm
@hemanth
Sep 06 2015 09:52 UTC
^ I'm getting an error on that. Like to use the spread operator rather thanslice
Simon Friis Vindum
@paldepind
Sep 06 2015 09:54 UTC
I didn't test it :S I think spread would only complicate things in this case.
I'm missing a parentheses after slice.
hemanth.hm
@hemanth
Sep 06 2015 10:01 UTC
@paldepind Nice, I would rather have list first and fn next in the param order
Jack Firth
@jackfirth
Sep 06 2015 10:24 UTC
@raine @CrossEye Re contracts and Eiffel - that implementation is significantly different from Eiffel's in that it allows contracts for higher order functions - you can specify "this function accepts a function of two arguments that returns a boolean" in the contract. Makes it a huge win for FP, and drastically improves error messages. Eiffel contracts are more "before calling this, state is X, after calling, state is Y"
hemanth.hm
@hemanth
Sep 06 2015 10:26 UTC
@paldepind slice doesn't mutate the array, nice.
Dave Keen
@ccapndave
Sep 06 2015 11:16 UTC
Hey everyone - is there a way to write R.pipe(R.prop('rows'), R.pluck('doc')) with a single function?
Scott Sauyet
@CrossEye
Sep 06 2015 12:58 UTC
@jackfirth: I only glanced at the docs, and didn't dig very deep. While I've never bought the notion that the only good FP is strongly typed FP, I certainly have some sympathy for attempts to add stronger typing to JS. After all Ramda's avoidance of overloading and of optional parameters certainly works along those lines. But I'm usually not that intetested in attempts to add real strong typing to JS. I'll have to take another look at this one though.
Hardy Jones
@joneshf
Sep 06 2015 13:14 UTC

@raine another options is to add another function, one that performs the algorithm, like @00Davo said:

baseAlgorithm = (read, payload) =>
.then(transporter.sendMailAsync.bind(transporter));

module.exports = {baseAlgorithm, ioAlgorithm};

Then you can test the algorithm with a unit test, and the io version with an integration test.

But you still have the shortcut function that you care about
If yu don't want to expand the public api, then shove the base algorithm in some internal file somewhere, or move it to an entirely separate module and depend on it.
Tamas
@bling5630
Sep 06 2015 14:09 UTC
hey guys, do you have any ideas how can I solve this much more elegant? thanks,Tamas

input is

['apple', 'the', 'one', 'two', 'three', 'elephant', 'whatever']

var filterByLength = R.filter(function(str) {

return str.length > 3 && str.length < 6;
});

desired output is

['apple', 'three']
Hardy Jones
@joneshf
Sep 06 2015 14:15 UTC
@bling5630 what do you view is inelegant about it?
Tamas
@bling5630
Sep 06 2015 15:07 UTC
I just like a shorter one, what do you think about it?
Matthew Steedman
@knubie
Sep 06 2015 15:07 UTC
@bling5630 if you're looking for a point-free solve something like this would work:
var filterByLength = R.filter(
R.converge(
R.and,
R.compose(
R.lt(3),
R.prop('length')
),
R.compose(
R.gt(6),
R.prop('length')
)
)
);
that's obviously longer, though
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:16 UTC
Something like R.filter(R.where({length: R.allPass([R.gt(6), R.lt(3)])})) is shorter.
Danielle McLean
@00dani
Sep 06 2015 15:17 UTC
Pity Ramda doesn't make functions into applicatives. filter \$ ((> 3) <&&> (< 6)) . length
Matthew Steedman
@knubie
Sep 06 2015 15:19 UTC
This would work too R.filter( R.compose( R.contains(R.__, R.range(4,6)), R.prop('length')))
Scott Sauyet
@CrossEye
Sep 06 2015 15:19 UTC
I'd still choose R.lt(R.__, 3) or R.flip(R.lt)(3) for readability.
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:21 UTC
@CrossEye I was just going to ask; What's up with the argument order of gt and lt? It had me confused for a moment. I expect var lt6 = R.lt(6) to make a predicate function that returns true when the argument is Less Than 6.
Danielle McLean
@00dani
Sep 06 2015 15:22 UTC
It's because R.lt(x, y) means x < y and curried arguments apply left to right. https://github.com/algesten/fnuc exists mainly because that's Kind of Weird.
Scott Sauyet
@CrossEye
Sep 06 2015 15:22 UTC
@00Davo: the language doesn't really offer us the opportunity. We did try something once, but it wreaked havoc with one's sense of what currying would do.
Danielle McLean
@00dani
Sep 06 2015 15:24 UTC
Yeah, fnuc's approach to currying is also pretty confusing.
Scott Sauyet
@CrossEye
Sep 06 2015 15:24 UTC
@Avaq: but what would you expect R.lt(7, 12) to return?
Danielle McLean
@00dani
Sep 06 2015 15:25 UTC
Could "reverse" currying like fnuc be added, but only applied in Ramda itself to lt and similar functions? Or would that be too inconsistent?
Since those functions are all binary it shouldn't be too difficult to figure out how the reverse-curry should behave, I figure.
Scott Sauyet
@CrossEye
Sep 06 2015 15:25 UTC
that's what we did try. The resulting confusion is what got is to add the placeholder.
Danielle McLean
@00dani
Sep 06 2015 15:26 UTC
Dang.
Oh hey, speaking of: Why's the placeholder an object with a specially-named field on it? Just making it an empty object and then comparing against that in the functions would work too, no?
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:27 UTC
@CrossEye Well, I would have to think about it for a brief moment, taking into consideration that Ramda tends to "swap" arguments, and conclude that it would probably return false because 12 is not less than 7.
Scott Sauyet
@CrossEye
Sep 06 2015 15:30 UTC
It's pretty easy to make a universal claim that fn(a, b) === fn(a)(b) for Ramda functions. To say its the same except for certain non-commutative operators was problematic. And we probably couldn't just say binary functions, either, or would you expect R.map(fn, list) but R.map(list)(fn) ? Madness.
@Avaq where does Ramda swap arguments?
Or are you suggesting that we should?
Ludwig Magnusson
@TheLudd
Sep 06 2015 15:31 UTC
I seem to remember something about compose being broken in the latest release. Is that correct?
Matthew Steedman
@knubie
Sep 06 2015 15:31 UTC
@CrossEye I think he's referring to the data being the last argument, as opposed to a library like underscore where it's the first
Scott Sauyet
@CrossEye
Sep 06 2015 15:31 UTC
Yes, today or tomorrow I'm going to raise an issue to fix it.
Ludwig Magnusson
@TheLudd
Sep 06 2015 15:32 UTC
@CrossEye It does not seem to be completely broken. Is there a good defenition of when it fails?
Scott Sauyet
@CrossEye
Sep 06 2015 15:32 UTC
well, #1318 is there for discussion already. I'm going to create a PR.
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:36 UTC
@CrossEye Yeah, I mean reversed, as opposed to say, underscore. The data comes last. When I'm unsure about which of the arguments goes for "data", I use the "assign partially applied function to variable and ask myself what I would expect it to do"-method. Though that method seems to fail for Ramda's gt and lt, like I explained. It's not the end of the world, but Ramda's argument order has started to make a lot of sense to me, until I met gt and lt just now. :P
Scott Sauyet
@CrossEye
Sep 06 2015 15:36 UTC
Compare 0.15: http://bit.ly/1PUEiW6 to current:http://bit.ly/1PUEiWa on
R.pipe(R.add, R.map)(10)([1, 2, 3])
Yes, for a while we had flipped versions of some of these functions. We lost them when we thought it was all figured out with the operators idea. The whole thing was started by my looking for good names for flipped versions of lt, gte, etc. We had subtractN, divideBy, and such, but no good names for these.
I'd love a better solution, but R.lt(7, 12) just seems to me it should return true and divide(20, 5) should return 4 not 0.25. fnuc was built around trying to solve this issue, but I think it ends up somewhat incoherent.
Ludwig Magnusson
@TheLudd
Sep 06 2015 15:42 UTC
I'll stay on 15 for now then :)
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:42 UTC
But wouldn't you expect R.divide to be able to let divideBy20 = R.divide(20)?
Danielle McLean
@00dani
Sep 06 2015 15:42 UTC
Non-serious suggestion: just make lt take only one parameter. lt(x, y) is just a long way to spell x < y, which you don't need, whereas lt(x) is useful.
Obviously it'd still return a function taking the second parameter, but the point is it's only allowed to take one argument at a time. Like a Real Haskell Function.
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:47 UTC
Maybe I'm wrong.
It's an odd one to think about, but my intuition went with R.lt(6) to test if the following value is less than 6. I think I've even used it like that to explain currying to colleagues. :P
Danielle McLean
@00dani
Sep 06 2015 15:50 UTC
â€¦ hmm, actually, here's a thought. What if R itself were made callable, and then you wrote something like R(6).lt to express the section (6 <)? It's a hack, but maybe it's a good one?
Scott Sauyet
@CrossEye
Sep 06 2015 15:52 UTC
@00Davo That would be an odd hack for Ramda. Seems almost... stateful.
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:53 UTC
@00Davo Yeah! And then we can implement a .chain() method so we can keep on chaining: R(6).chain().lt(3).equals(true).value()!
Danielle McLean
@00dani
Sep 06 2015 15:53 UTC
It's not actually stateful. R() would just return an object full of partially-applied functions.
Aldwin Vlasblom
@Avaq
Sep 06 2015 15:54 UTC
(I'm just kidding, of course) ;)
Haha, so when you call R(x), all of Ramda's functions are partially applied with x, and the results are returned in an object. That's an interesting way of doing things. :P
Scott Sauyet
@CrossEye
Sep 06 2015 15:58 UTC
@Avaq, @00Davo: One thing that bothers me about the chaining syntaxes is that they usually only apply to the libraries built-in functions. In R.compose/R.pipe, there's nothing special about Ramda functions; you can pass anything you want. So, while you could do something like R(3).someRfunc, the only way I know to make it more generic so that you could apply any other function you like is with a stateful registration process.
So yes, I've thought about this idea a bit! :smile: Although I never considered using R itself as the function.
@TheLudd: We'll see what happens. It's not at all clear that people will want to roll back the compose changes.
Tamas
@bling5630
Sep 06 2015 16:00 UTC
@knubie yes, basically I looking for the point-free version, thank you, longer but better for me :)
Scott Sauyet
@CrossEye
Sep 06 2015 16:00 UTC
@TheLudd: I find it broken now, but it's also simplified in some nice ways.
@bling5630: and look what you brought on! :smile:
Danielle McLean
@00dani
Sep 06 2015 16:02 UTC
Yeah, R(x).whatever would be a lot less interoperable. Considering R.flip is available, though, maybe that's okay? R(x).f would really just be sugar specifically for the binary operator funcs.
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:02 UTC
@bling5630 This solution is much shorter: R.filter(R.where({length: R.both(R.lt(3), R.gt(6))}))! :P
Tamas
@bling5630
Sep 06 2015 16:03 UTC
folks, thank you all of the ideas!
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:05 UTC
@CrossEye Doesn't that read like "filter where length is both less than three and greater than six"? :P
Ok I stop now.
Matthew Steedman
@knubie
Sep 06 2015 16:09 UTC
@Avaq certainly has a point there
Scott Sauyet
@CrossEye
Sep 06 2015 16:14 UTC

Right, which is why I use:

R.filter(R.where({length: R.both(R.gt(R.__, 3), R.lt(R.__, 6))}))

or

R.filter(R.where({length: R.both(R.flip(R.gt)(3), R.flip(R.lt)(6))}))
I don't love these solutions. I just haven't found anything I really like better.
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:16 UTC
But isn't the point of Ramda to provide "parameters [that] are arranged to make it convenient for currying"? If you have to R.flip them yourself all the time for legibility, that's inconvenient in my opinion.
Danielle McLean
@00dani
Sep 06 2015 16:18 UTC
I'd const _ = R.__, which is a tiny bit more convenient to use when you have to. Still less convenient than having a function that's the right way around to begin with though.
Martin Algesten
@algesten
Sep 06 2015 16:20 UTC
I'm obviously partial, but what is the argument for ramda's argument order. Doesn't all this flipping sort of imply that the whole API could benefit from reversing?
R.map(arr, fn);
Danielle McLean
@00dani
Sep 06 2015 16:24 UTC
If flipping were more broadly necessary than in a few very specific cases, which basically are just "the non-commutative binary operators", then maybe. But for the most part the order it's in works out pretty well.
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:24 UTC
@algesten The argument is that arguments are passed in an order convient for currying. So R.map takes it's data last, because it's a much more common case to want to create a partially applied map with a mapper, than it is to create a partially applied map with it's data, waiting for different mappers to be passed-in
Martin Algesten
@algesten
Sep 06 2015 16:25 UTC
ok. makes sense. it doesn't take long to get used to reverse currying though ;)
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:25 UTC
@algesten The argument we are having now is specific to gt and lt, where that idea seems to have failed to make it. :P
Scott Sauyet
@CrossEye
Sep 06 2015 16:26 UTC
It also affects subtract, divide, modulo. But it's still a very small minority of Ramda functions.
@algesten: I have been fascinated by fnuc since I first saw it. But I've never been quite convinced. map(square) seems an obvious abstraction, and currying is familiar to most functional programmers in that format.
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:33 UTC
@CrossEye Ha, just ran into you SO question (via fnuc) - seems this is not a new problem what-so-ever. :)
Scott Sauyet
@CrossEye
Sep 06 2015 16:34 UTC
Was just about to post the history of this issue: See #175, #368, and then where we reverted it in #388.
Martin Algesten
@algesten
Sep 06 2015 16:34 UTC
@CrossEye i don't expect fnuc to go anywhere. but i'm happy you find it interesting. i'm all pro ramda and it's role in FP for javascript.
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:40 UTC
Ah I see. I agree with the conclusion of #388. No way should lt(x, y) !== lt(x)(y)! But I just feel the settled-upon order of arguments is wrong. I disagree with your statement that lt(10, 12) is the only comfortable way to write "is 10 less than 12", because under that reasoning map(arr, func) would also be the preferrable version.
Danielle McLean
@00dani
Sep 06 2015 16:41 UTC
I'm not sure I follow that argument. map(func, arr) reads "map func over arr", which is totally reasonable.
Martin Algesten
@algesten
Sep 06 2015 16:43 UTC

For a coffeescripter this makes so much sense.

map arr, (a) -> blaha

Granted could also be solved.

map(arr) (a) -> blaha
especially when the function needs a couple of lines
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:45 UTC
But isn't the reason libraries that came before chose a function-last interface because it reads and writes nicer? If I wouldn't consider currying, I would completely agree with their decisions. I feel ramda just does one thing: Giving up comfort for curry. :D
Which, in the end, turns out to be pretty comfortable in FP, because you're usually just passing one argument per call.
Matthew Steedman
@knubie
Sep 06 2015 16:49 UTC
For Ramda, I think it's more important that a function read better partially applied, than it is for it to read better fully applied.
So I agree with @Avaq, most of the time I'm using functions like lt, gt, subtract, they're partially applied
and I either end up having to supply a placeholder or flip them
In fact, the first time I used subtract, I had just assumed the arguments were in the reverse order that they're currently in.
Scott Sauyet
@CrossEye
Sep 06 2015 16:53 UTC
I think the reason that libraries that came before chose a function-first interface is that they were converting pseudo FP method-based libraries such as Array.prototype.map, and there it just felt more natural to convert arr.map(fn) to map(arr, fn). If they had been considering currying, or spending a lot of time looking at other languages that do this, they might well have chosen a different order.
@knubie, I think you''re in the minority here, but I have no evidence for that feeling.
... not for using these more often partially applied, but for assuming that the fully applied version of subtract is R.subtract(a, b); //=> b - a;.
I would welcome a PR reversing these functions to get discussion going with everyone who might be interested. Certainly no guarantees it would be accepted; I'm not even sure I would support it, but I do think it's worth discussing.
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:56 UTC
@CrossEye I found myself(don't laugh, this was years ago) rewriting setTimeout to take its function last, because it reads and writes nicer that way.
I just assumed other libraries had the same reason for similar kinds of interfaces.
Matthew Steedman
@knubie
Sep 06 2015 16:57 UTC
@CrossEye I'm pretty sure in haskell subtract 3 5 returns 2, so I don't think it's that strange
I read it as subtract 3 from 5
Aldwin Vlasblom
@Avaq
Sep 06 2015 16:58 UTC
Oh whoops. I'm also linking to the wrong thing. I believe I had a version before Promises which took the callback last.
Scott Sauyet
@CrossEye
Sep 06 2015 17:02 UTC
@knubie, that wouldn't be a surprise at all. That's logically (subtract 3)(5), in a language where all functions are unary. But here we deal with polyadic functions. And our curry is more complex, allowing fn(a, b, c) === fn(a, b)(c) === fn(a)(b, c) === fn(a)(b)(c)
Aldwin Vlasblom
@Avaq
Sep 06 2015 17:06 UTC
@CrossEye But isn't it a strong argument that we (being Ramda) should favour the ligibility of partially applied functions over their fully applied variants?
Scott Sauyet
@CrossEye
Sep 06 2015 17:20 UTC
My take is that if we had this the other way we'd be arguing about how horrible it is that R.divide(20, 5); //=> 0.25, and "why should we favour the legibility of partially applied functions when the main definition is so clearly wrong?" That's why we have the placeholder. But, as I said, I would not mind a PR brought up to get this issues into everyone's view.
Aldwin Vlasblom
@Avaq
Sep 06 2015 17:34 UTC
@CrossEye Alright. I think I'll do that PR. The more we've discussed, the more I'm convinced of my own argument (order). It'd be interesting to see what others say. :)
Hardy Jones
@joneshf
Sep 06 2015 18:51 UTC
A big issue that most people forget is that when you support function last, you support bad designs/less maintainable code.
If your lambda is more than a line, why is it a lambda?
Hardy Jones
@joneshf
Sep 06 2015 18:57 UTC
for instance, I'd argue that @bling5630 's example would benefit from being split out to a named function.
Think about what that function does: it tests whether a number is greater than some value and less than some value.
This behavior is complex enough to warrant a named function in my book. It's just enough that it's not unreasonable to expect a flipped operator or using || instead of && depending on how tired you are or how quick you're writing it, or something else.
The example being:
var filterByLength = R.filter(function(str) {
return str.length > 3 && str.length < 6;
});
Jack Firth
@jackfirth
Sep 06 2015 20:03 UTC
Splitting it into a named function also means you can ignore the length property part and use propSatisfies for that
var isBetween = R.curry((low, high, x) => x > low && x < high);
var filterByLength = R.filter(R.propSatisfies(isBetween(3, 6), 'length'));
Scott Sauyet
@CrossEye
Sep 06 2015 21:02 UTC
@jackfirth: That was my very first thought, and then I got lost in the simplest way to abstract the various combinations of a < x < b, a < x <= b, a <= x < b and a <= x <= b. Obviously you can do it, but I didn't see the pretty, obvious abstraction, anything cleaner than inRange :: Bool -> Bool -> Num -> Num -> Num -> Bool, where the first two are includesLeft and includesRight, and your isBetween is inRange(false, false). Those boolean flags are code smells to me, but I'm not sure what a better abstraction would be.
Hardy Jones
@joneshf
Sep 06 2015 21:56 UTC
@CrossEye use specific types:
data Clusivity = In | Ex
inRange :: Clusivity -> Clusivity -> Num -> Num -> Num -> Bool
It's isomorphic to Bool, so you gain readability, without losing power.
Scott Sauyet
@CrossEye
Sep 06 2015 22:01 UTC
@joneshf: That's a great solution in Haskell, but perhaps only a decent one in JS. JS makes you do the work of defining the type, and doing the type-checking yourself in the function. A boolean you can choose a default and then simply use ! or !! to coerce anything as you choose.
Of course above @raine points to a library that would help enforce stronger type contracts.
Hardy Jones
@joneshf
Sep 06 2015 22:19 UTC
i don't follow
If the point is to ensure you've written the right thing, then that's what tests are for in js.
If the point is to ensure users can't do the wrong thing, then js isn't the language you should be writing in.
Anothe option is to take the functions, since that's what you actually care about.
inRange :: (Num -> Num -> Bool) -> (Num -> Num -> Bool) -> Num -> Num -> Num -> Bool
Or make it easier and partially apply them:
inRange :: (Num -> Bool) -> (Num -> Bool) -> Num -> Bool
Scott Sauyet
@CrossEye
Sep 06 2015 22:44 UTC

When I write a public API, I like to ensure that if the user does the wrong thing, she's likely to notice something wrong quickly, an undefined or null or empty list when she expects data, or at last resort an error thrown.

My day job is almost all client-side JS; I suppose I could look for another one, but outside that I do have to deal with bad data from users, and have to consider how to handle it. So I would probably need to check for Clusivity.In, Clusivity.Ex, and other inside that function. And that would be for both sides of the range. While this is a nice advance in type-safety, it's also significantly more complex code than checking for a boolean.

That's my dilemma. The boolean is not nearly as readable, and it would probably lose to a nicer function, but adding a data type such as this, when there's no built-in support does not end up coming across as actually simpler.

Hardy Jones
@joneshf
Sep 06 2015 22:47 UTC
I didnt image that function would be user facing. When I said user, I meant user of a library, or another person on a team. I would imagine that there would be some attempt to validate input before passing it straight to that function, yeah?
No amount of compile time safety is going to help you if you are allowing raw input from users at runtime.
Or am i misunderstanding what you're saying?
Also, that function is probably too complex to be in the public api anyway, so it's kind of moot :).
Scott Sauyet
@CrossEye
Sep 06 2015 23:15 UTC

Well, that's why I didn't bring it up when the question first arose. I started down isRange(a, b) but realized that it really was not as useful as it should be as it didn't let a user choose in/exclusive at either end, but, as I said, couldn't come up with a clean way to do that.

And yes, I do mean user of a library. But I work with an ever-shifting, large team that includes some fairly advanced JS people and some... let's say... much less advanced ones. This is not end-users. Some of these library users would probably pass true in place of Clusive.In on their first attempt, regardless of the documentation and test cases. The worst of them would ask for help at that point, before even checking the obvious places.

Actually, rather than asking for help, they'd probably try to explain that this function was broken... :frowning:
Danielle McLean
@00dani
Sep 06 2015 23:20 UTC
What if you took an object to customise the behaviour? Like R.inRange({gt: 3, lt: 6})?
Scott Sauyet
@CrossEye
Sep 06 2015 23:23 UTC
@00Davo: That is just what I would want. Very nice!
Danielle McLean
@00dani
Sep 06 2015 23:24 UTC
:3
Hardy Jones
@joneshf
Sep 06 2015 23:27 UTC
Isn't that worse?
You're approximating sums with products.
Danielle McLean
@00dani
Sep 06 2015 23:28 UTC
As in typewise, you mean?
Scott Sauyet
@CrossEye
Sep 06 2015 23:41 UTC
@joneshf: Bringing it down to specifics, would it be worse because this wouldn't prevent R.inRange({gt: 6, lte: 3}) ?

That wouldn't bother me much. I have no problem responding to incoherent data with the only answer that logically matches:

R.inRange({gt: 6, lte: 3})(n); //=> false for all n

No problem.

Danielle McLean
@00dani
Sep 06 2015 23:45 UTC
Honestly I think it'd be legitimate for it to accept things like {gt: 6, gte: 12} too.
Hardy Jones
@joneshf
Sep 06 2015 23:45 UTC
Nope, I was more thinking inRange({gt: 6, gte: 6}) or inRange({}) or inRange({gthan: 6, lthan: 3}
heh
Danielle McLean
@00dani
Sep 06 2015 23:45 UTC
Could also pretty easily support {'>': 6, '<': 12} I guess.
Hardy Jones
@joneshf
Sep 06 2015 23:46 UTC
the last case is where you have your co-workers that don't read documentation so that's pretty much a wash from the function way
but what semantics do you choose when both gt and gte have the same number?
and, or?
Danielle McLean
@00dani
Sep 06 2015 23:47 UTC
Just 'and' everything you're given. That's what happens when it's lt and gt.
Scott Sauyet
@CrossEye
Sep 06 2015 23:47 UTC
I would choose and just in general.
Only for known properties, lt, lte, gt, gte, or maybe symbolic equivalents. Anything else can be ignored.
Danielle McLean
@00dani
Sep 06 2015 23:49 UTC
@joneshf The coworkers that don't read docs at least can't just pass true when they were supposed to pass Clusivity.Ex` or whatever in this case, though, right?
It won't work correctly-by-coincidence.
Scott Sauyet
@CrossEye
Sep 06 2015 23:50 UTC
You know the worst part is that these same cow-orkers (did I say that?) point to the documentation when anything is wrong, still usually misunderstanding.
I think @00Davo's last point is the important one. This will only work with some modicum of understanding.