These are chat archives for ramda/ramda

22nd
Apr 2015
Ludwig Magnusson
@TheLudd
Apr 22 2015 06:29
@joneshf Interesting point. Come to think of it, most of ramdas functions probably mutate state of local private variables.
However, no Maybe, Either I have seen implemented in JavaScript give you a pure interface since attributes assigned to this are publicly available.
Viktor Fröberg
@vikfroberg
Apr 22 2015 11:26
I’m trying to understand how to organize data in a functional manner. Would love some help: http://bit.ly/1G6duvX
Raine Virta
@raine
Apr 22 2015 11:35
@vikfroberg to me it makes sense to divide the problem into two parts:
  1. reducing the input into a list where each item is a separate event with a date
  2. applying a formatting function into each item in that list with map
after 1. you can also easily sort the list by date with R.sortBy
Viktor Fröberg
@vikfroberg
Apr 22 2015 11:40
@raine Thanks, I'm going to give it a shot. What I'm struggling with is the part where I convert one event to number of days. What would I use to accomplish that? R.times?
Raine Virta
@raine
Apr 22 2015 11:45
> R.range(event.startDate, event.endDate + 1)
[ 21, 22, 23 ]
you can build a list of Dates from that
write a function that takes a day of month and returns a Date object, then map over the list
Viktor Fröberg
@vikfroberg
Apr 22 2015 12:07
@raine Thanks, got it working!
Viktor Fröberg
@vikfroberg
Apr 22 2015 12:27
@raine Now is there anything I can do different/better? I would love to use currying instead of the callback functions, but not sure how to though: http://bit.ly/1OeaSWx
Raine Virta
@raine
Apr 22 2015 12:33
you could call the map function formatEventStr or something
Raine Virta
@raine
Apr 22 2015 12:40
var eventsPerDay = R.reduce(function(acc, event) {
  var days = R.range(event.startDate, event.endDate + 1);
  return R.concat(acc, R.map(R.assoc('date', R.__, event), days));
}, [], events);
try to place more functions in variables instead of data. then compose those functions together to avoid intermediary values
Hardy Jones
@joneshf
Apr 22 2015 12:42
i think you should be able to replace reduce with chain so you dont' have to worry about initial values and concat and whatnot
var eventsPerDay = R.chain(function(event) {
  return R.range(event.startDate, event.endDate + 1).map(function (date) {
    return {title: event.title, date: date};
  });
}, events);
then continue with what @raine just posted :)
so you get something like:
var eventsPerDay = R.chain(function(event) {
  var days = R.range(event.startDate, event.endDate + 1);
  return R.map(R.assoc('date', R.__, event), days);
}, events);
Viktor Fröberg
@vikfroberg
Apr 22 2015 12:48
@raine Thanks! Didn't know about R.assoc!
@joneshf Awesome! I was looking for a "flatMap" function but couldn't find it, chain does the trick!
Hardy Jones
@joneshf
Apr 22 2015 12:51
you can then extract the range out
function dateRange(event) {
  return R.range(event.startDate, event.endDate + 1);
};

var eventsPerDay = R.chain(function(event) {
  return R.map(R.assoc('date', R.__, event), dateRange(event));
}, events);
from there it's pretty hard to go pointfree without getting a bit convoluted
since event needs to be passed into both dateRange and the function in map
you could do it, but it's probably more readable this way
Viktor Fröberg
@vikfroberg
Apr 22 2015 12:57
For the sake of learning, how would I do that?
Hardy Jones
@joneshf
Apr 22 2015 13:00
Hmm, this will get long, so I apologize in advance.
also, there might be an easier way than this if there's some function that is basically ap for functions in ramda.
also that's the punchline ^
so the idea is that if you can make R.map(R.assoc('date', R.__, event) and dateRange(event)) take their event argument, then you can say use pipe to string them together.
dateRange(event) is trivial to make this happen, just use dateRange
Hardy Jones
@joneshf
Apr 22 2015 13:05
the other is also pretty easy, just pipe with the inner function, to map: R.pipe(R.assoc('date', R.__), R.map)
although, i'm not actually sure how R.__ will handle being within that pipe like that
it might actually be better to get rid of it
Scott Sauyet
@CrossEye
Apr 22 2015 13:08
R.__ doesn't do anything useful in the last position.
Hardy Jones
@joneshf
Apr 22 2015 13:08
so it'll have the same behavior as above the non-piped version then?
oh
i see what you mean
Scott Sauyet
@CrossEye
Apr 22 2015 13:09
Nothing to do with pipe.
Hardy Jones
@joneshf
Apr 22 2015 13:10
okay, then assoc :: String -> a -> {k: v} -> {k: v} from the docs, so we want String -> {k: v} -> a -> {k: v}
then that's R.flip(R.assoc('date'))
Scott Sauyet
@CrossEye
Apr 22 2015 13:11
Part of the reason for avoiding a points-free solution here is that it simply adds nothing. Here is a version of dateRange:
Hardy Jones
@joneshf
Apr 22 2015 13:11
so what we actually wanted was R.pipe(R.flip(R.assoc('date')), R.map)
so now both parts accept the event argument
and we just need some way to thread it through each part
Scott Sauyet
@CrossEye
Apr 22 2015 13:13
var dateRange = R.converge(R.range, R.prop('startDate'), R.compose(R.inc, R.prop('endDate'))
That is not nearly as readable as the original version.
Hardy Jones
@joneshf
Apr 22 2015 13:14
it turns out that what we want is the Applicative instance for functions
it goes by many names, so don't be taken aback by Applicative
Reader, S combinator, Starling, etc
and it actually seems like converge as well
Scott Sauyet
@CrossEye
Apr 22 2015 13:19
Not sure if it's the same thing or not.
Recently discussed correct sig for it in #1035
(x1->x2->… -> z) -> ((a->b->… -> x1), (a->b->… -> x2), …) -> (a->b->… -> z)
Hardy Jones
@joneshf
Apr 22 2015 13:25
yeah, it's a variadic version
which works for this
so we can use converge to supply event to both functions
and then we just need a converging function that applies one to the other
so let's look at some types real quick
Hardy Jones
@joneshf
Apr 22 2015 13:31
dateRange :: {startDate :: Int, endDate :: Int, ...} -> [Int]
R.pipe(R.flip(R.assoc('date')), R.map) :: {...} -> [a] -> [{date :: a, ...}]
for some approximation of types. here i'm using ... to indicate some other fields
then we see that in both functions, they take an object, so we want converge to end up taking an object as our argument
The event object is exactly the one we want to pass to converge
So, given the signature from @CrossEye above, we want converge to have the type:
This message was deleted
Hardy Jones
@joneshf
Apr 22 2015 13:37
([Int] -> ([Int] -> [{date :: a, startDate :: Int, endDate :: Int, ...}]) -> [{date :: a, startDate :: Int, endDate :: Int, ...}]) -> 
  ( ({startDate :: Int, endDate :: Int, ...} -> [Int])
  , ({startDate :: Int, endDate :: Int, ...} -> [Int] -> [{date :: a, startDate :: Int, endDate :: Int, ...}])
  ) -> [{date :: a, startDate :: Int, endDate :: Int, ...}]
I dunno, it's pretty bad any way
Scott Sauyet
@CrossEye
Apr 22 2015 13:40
When they get this bad, sometimes we resort to R.nthArg. Our more likely, give up on points-free.
Hardy Jones
@joneshf
Apr 22 2015 13:41

you could use some synonyms

let A = [Int]
let B = [{date :: a, startDate :: Int, endDate :: Int, ...}]
let C = {startDate :: Int, endDate :: Int, ...}

Then you get

(A -> (A -> B) -> B) -> ((C -> A), (C -> A -> B)) -> B
@CrossEye yeah, I agree, it's way convoluted. but @vikfroberg wanted to know for the sake of learning :)
Viktor Fröberg
@vikfroberg
Apr 22 2015 13:43
This is amazing!
Hardy Jones
@joneshf
Apr 22 2015 13:43
in any case, we've already got C -> A, which is dateRange, and C -> A -> B is R.pipe(R.flip(R.assoc('date')), R.map). so we just need A -> (A -> B) -> B.
and that's R.flip(R.call)!
so we can slap it all together as...
R.converge(R.flip(R.call), dateRange, R.pipe(R.flip(R.assoc('date')), R.map))
then it's point free, and also pointless :P
So you can now say:
var eventsPerDay = R.chain(R.converge(R.flip(R.call), dateRange, R.pipe(R.flip(R.assoc('date')), R.map)), events);
Scott Sauyet
@CrossEye
Apr 22 2015 13:47
Very nicely done.... even if pointless. :smile:
Viktor Fröberg
@vikfroberg
Apr 22 2015 13:48
This is going to take some time to digest, but you have no idea how valuable this is! :D
Hardy Jones
@joneshf
Apr 22 2015 13:50
@vikfroberg I'm glad you didn't walk away half way through.
I think it was saved by having converge exist
otherwise would've had to greenspun up a reader.
but
@CrossEye i think that means for ramda-fantasy, reader can be made simpler
or maybe not simpler
but it could be implemented in terms of converge
Scott Sauyet
@CrossEye
Apr 22 2015 14:21
Perhaps. I've never really tried to understand Reader.
Raine Virta
@raine
Apr 22 2015 14:25
what do Applicative and Reader have in common? i thought Reader is basically like an environment for function
Scott Sauyet
@CrossEye
Apr 22 2015 14:26
There's one other somewhat interesting function that's related. useWith has this signature:
(x1 -> x2 -> ... -> z) -> ((a -> x1), (b -> x2), ...) -> (a -> b -> ... -> z)
Hardy Jones
@joneshf
Apr 22 2015 15:19
@raine Reader has an Applicative instance. remember Applicative isn't a data type, it's a specification/interface/typeclass/trait/module/whatever.
also Reader is exactly Function or at least it should be.
so in that sense, it doesn't really make sense to have it as an abstraction, since it should have the same behavior for map,ap,chain, etc that functions do. What you really want is the transformer version, ReaderT, so that you can compose it with arbitrary Monads.
and if you happen to compose ReaderT with identity you get Reader === Function.