These are chat archives for ramda/ramda

14th
Jun 2016
James Forbes
@JAForbes
Jun 14 2016 02:45

Is there a function already in ramda that allows you to define a pipe and a map at the same time?

e.g. instead of

R.map(
  R.pipe(
     ...
  )
)

It's just:

pipeMap(
   ...
)

Usage example

var pipeMap = R.converge(R.map, [R.pipe])

var daysFromToday = pipeMap(
   R.prop('unixtime')
   ,R.add( -now )
   ,R.multiply( 1 / 86400000 )
   ,Math.round
)

daysFromToday([{unixtime: 123123423844}, {unixtime: 123143247554}, ... etc ]) //=> [3,4,2,-1]

I know its easy to define but I find myself doing this a lot and wondered if there is something like this by some other name already defined.

Wait... is that what pipeK is? WOAH!

My head just exploded, that makes so much sense, I can't believe that never occurred to me.
Well I'm not sure, I get equivalent output using pipeK, but perhaps its less efficient under the hood.
Brad Compton (he/him)
@Bradcomp
Jun 14 2016 03:13
@JAForbes pipeK is similar, but for chain instead of map, so you could call it pipeChain in your parlance ;)
Each function should take a value and returned a wrapped value, like a -> [b] if you're looking at arrays
James Forbes
@JAForbes
Jun 14 2016 03:19
@Bradcomp thanks! So why does pipeK seems to give me the desired output with arrays when array's don't have a chain method. Is this ramda just filling in the gap?
Brad Compton (he/him)
@Bradcomp
Jun 14 2016 03:20
Can you post your pipeK code?
James Forbes
@JAForbes
Jun 14 2016 03:23
This message was deleted
Now that is an url
Brad Compton (he/him)
@Bradcomp
Jun 14 2016 03:24
lol, truth
I'm getting nothing though
James Forbes
@JAForbes
Jun 14 2016 03:25
Here it is inline, that link never loads for me:
const pipeMap = R.converge(R.map, [R.pipe])
const now = new Date().getTime()

const daysFromTodayMap = pipeMap(
   R.prop('unixtime')
   ,R.add( -now )
   ,R.multiply( 1 / 86400000 )
   ,Math.round
)
const daysFromTodayK = pipeK(
   R.prop('unixtime')
   ,R.add( -now )
   ,R.multiply( 1 / 86400000 )
   ,Math.round
)
const input = [
  {unixtime: 1465960886275}
  ,{unixtime: 1466047294366} 
]
R.equals(
  daysFromTodayK(input) //=> [1,2]
  ,daysFromTodayMap(input) //=> [1,2]
) //=> true
I just realised I can use compose instead of converge in that definition, I always forget that.
James Forbes
@JAForbes
Jun 14 2016 03:30
Here it is evaluated in a tonic repl: https://tonicdev.com/jaforbes/575f7a2131546413003455e3
Brad Compton (he/him)
@Bradcomp
Jun 14 2016 03:30
interesting... tbh, I'm having trouble understanding why it's working, so my mind-s blown a bit too
James Forbes
@JAForbes
Jun 14 2016 03:30
yeah that is my current status
Brad Compton (he/him)
@Bradcomp
Jun 14 2016 03:33
The implementation isn't super helpful either https://github.com/ramda/ramda/blob/v0.21.0/src/composeK.js
James Forbes
@JAForbes
Jun 14 2016 03:33
If this isn't a bug I'd like to know because it stops me writing R.compose(R.pipe, R.map) everywhere
Brad Compton (he/him)
@Bradcomp
Jun 14 2016 03:33
I suspect there's some monadic reason for it...
Perhaps it's _makeFlat in the second branch of chain
because after that invokes, it's just a map which is the behaviour I'm experiencing
Seems to unnest if there is a nesting, otherwise it just clones the list
why it does that, I have no idea
Brad Compton (he/him)
@Bradcomp
Jun 14 2016 03:38
Interesting... You can see the behavior I expected here: http://goo.gl/i3FmR7
I don't know if the fact it works without wrapping the function output is a happy accident or intended
James Forbes
@JAForbes
Jun 14 2016 03:40
Same
Tushar Mathur
@tusharmath
Jun 14 2016 07:42
@LeonineKing1199 For starters, I want to make the function point free.
Tushar Mathur
@tusharmath
Jun 14 2016 11:47

In ramda —

const test = (a) => {
    return R.compose(P(a), Q)(a)
  }

How can I make this point free?

In this case — the function P requires one of the params
Aldwin Vlasblom
@Avaq
Jun 14 2016 12:03
@tusharmath I think: R.useWith(P, [R.identity, Q]).
Tushar Mathur
@tusharmath
Jun 14 2016 12:06
@Avaq Great! thank you :+1:
Aldwin Vlasblom
@Avaq
Jun 14 2016 12:08
Actually, useWith should've been converge. I mix them up. This answer also assumes that P can be given both arguments at once.
Tushar Mathur
@tusharmath
Jun 14 2016 12:11
what about this — const takeArg2 = func => R.compose(func, R.nthArg(1)) point free ?
@Avaq converge looks more suitable
Aldwin Vlasblom
@Avaq
Jun 14 2016 12:33
@tusharmath The first example is even more concise if you have Sanctuary, or some other access to the S combinator: S.S(P, Q), also it works with curried P like you want.
Rafe
@rjmk
Jun 14 2016 12:35
@tusharmath Also, test = ap(P, Q), though S is nicer
Aldwin Vlasblom
@Avaq
Jun 14 2016 12:36
You second question is difficult to answer, because R.nthArg is horribly variadic and I have no idea how R.compose deals with that.
Rafe
@rjmk
Jun 14 2016 12:36
Actually, edited above. Don't think you need to flip P
Tushar Mathur
@tusharmath
Jun 14 2016 12:37
Ok
Aldwin Vlasblom
@Avaq
Jun 14 2016 12:37
You're right @rjmk, that's interesting! ap can work like S. o.o
Tushar Mathur
@tusharmath
Jun 14 2016 12:38
Is there a function such that it takes in an object of factory functions,
magic({
   a: factoryA,
  b: factoryB
})(1, 2, 3)
Rafe
@rjmk
Jun 14 2016 12:38
@Avaq Yeah, function has an applicative instance (and a monad instance) which is often useful though sometimes scary
Tushar Mathur
@tusharmath
Jun 14 2016 12:46
In the above case, the factory functions area called with factory(1, 2, 3) and the output is set to a
and similarly for b
Aldwin Vlasblom
@Avaq
Jun 14 2016 12:54
R.applySpec
Aldwin Vlasblom
@Avaq
Jun 14 2016 12:54
@tusharmath ^
Tushar Mathur
@tusharmath
Jun 14 2016 12:56
@Avaq Perfect :+1:
Is it true that any function can be made point free ? Should point free be the goal of a following functional paradigm
?
May be not the goal, but should I keep refactoring until I get a point free function?
Aldwin Vlasblom
@Avaq
Jun 14 2016 13:05

@tusharmath It's true that all functions can be made point-free*, but I don't think you should strive to define your program in a point-free manner. Only use it when it makes sense to do so, like in the cases where you have a => f(b)(c)(a). Making your program point-free is a form of encoding it manually. It might become more concise, but it also becomes more difficult to understand just from looking at it. The more you encode your program, the more your colleagues (and probably future-self) are going to hate you.

* Look into combinatory logic. The SKI system builds a complete point-free programming language on top of two small functions. So really I should've said nearly all functions can be made point-free - you have to start somewhere. ;)

Personally I think that if you need to "figure out" how to make something point-free, you probably shouldn't be doing it. Keep it apparent. (...is an advice based on my experiences and opinions)
Though it is great exercise. If you're just writing some hobby code.. you know, go for it. :)
Tim Navrotskyy
@dypsilon
Jun 14 2016 13:11
In my opinion it's much more important to create a habbit of writing small functions which compose well with each other instead of writing imperative spaghetti.
point-free forces you to work that way
Tushar Mathur
@tusharmath
Jun 14 2016 13:13
@Avaq what is >SKI system
Tushar Mathur
@tusharmath
Jun 14 2016 13:14
@dypsilon I agree, this exercise of converting to point free functions improves the way one thinks about composibility.
Aldwin Vlasblom
@Avaq
Jun 14 2016 13:20

Here's an implementation of point-free recursive factorial: Y(B(ifElse(isZero)(K(1)))(B(S(mult))(C(B)(dec)))). I think you wouldn't have figured that out from looking at the code. The point I'm trying to make favours code understandibility, because often in cooperative projects that's important. Though the point @dypsilon is making isn't invalid.

I find it difficult to find the balance between improving the code with point-free, and encoding the code with point-free. I think it's good to be aware of that.

Tushar Mathur
@tusharmath
Jun 14 2016 13:26
@Avaq So one should know when to stop :)
That makes sense.

@Avaq

This answer also assumes that P can be given both arguments at once.

Well, in my case, I can't pass both the params in one go.

Aldwin Vlasblom
@Avaq
Jun 14 2016 13:28

I can't pass both the params in one go

So use the other solution, S.S(P, Q) or R.ap(P, Q)

Tushar Mathur
@tusharmath
Jun 14 2016 13:29
what is S.S ?
Often Sanctuary and Ramda are used together. Just use Ramda's ap if you don't have Sanctuary.
Tushar Mathur
@tusharmath
Jun 14 2016 13:39
@Avaq The code doesn't seem to be working, Here is the real function —
(x) => {
      return R.compose(AttachIndex(x), Request, Params)(x)
    }
Aldwin Vlasblom
@Avaq
Jun 14 2016 13:39
There's a third parameter in compose I wasn't aware of.
Tushar Mathur
@tusharmath
Jun 14 2016 13:40
@Avaq In the earlier example, I assumed that it would work in the similar fashion
Aldwin Vlasblom
@Avaq
Jun 14 2016 13:40
Just compose those two together.
R.ap(AttachIndex, R.compose(Request, Params))(x)?
Tushar Mathur
@tusharmath
Jun 14 2016 13:42
as per the docs here — http://ramdajs.com/docs/#ap
the function accepts an array
but you are not using an array
What difference did that make/
?
Aldwin Vlasblom
@Avaq
Jun 14 2016 13:46

the function accepts an array

It states in the documentation of ap:

treats functions as applicatives

That's the property of ap we're using.

What difference did that make

What? Using R.ap? It made the function point-free. There's no more mention of x in the middle, only at the end where you can provide it whenever you want.

Tushar Mathur
@tusharmath
Jun 14 2016 13:49
I meant what difference did passing it as an Array make. It made it point free which is awesome :)
Aldwin Vlasblom
@Avaq
Jun 14 2016 13:51
Right. We're not passing it as an Array, nor is it treating it as an array. R.ap has a special case for functions so that they are treated as Applicatives.
Coincidentally, it gives us the behavior we're looking for. Though the essence of what we want is much more clearly described by the implementation of S.S (the S combinator).
Tushar Mathur
@tusharmath
Jun 14 2016 14:00
Got it
Rafe
@rjmk
Jun 14 2016 14:09

It's useful to know that the general form of ap is Applicative a => a (b -> c) -> a b -> a c (the type signature specialised to lists should probably look a bit more like [(a -> b)] -> [a] -> [b], rather than what it currently does). Functions from some type are applicatives (applicatives have to have exactly one free type variable, which in this case is the output type).

So if we fix the input type as r, we have ap :: (r -> b -> c) -> (r -> b) -> r -> c. So we've got (1) a function that takes an r and a b and then gives a c; (2) a function that takes an r and returns a b and (3) an r, and we have to work out how to put them together to get a c. If you think about it, the way to do that is ap = f => g => x => f(x, g(x)).

Aldwin Vlasblom
@Avaq
Jun 14 2016 14:14
, -> )(
Aldwin Vlasblom
@Avaq
Jun 14 2016 14:20
I think it's really interesting how that works @rjmk! :D
This is the first time the meaning of "functions are applicatives" clicks in my head.
ghci> :i (->)
instance Applicative ((->) a) -- Defined in ‘GHC.Base’
That's awesome.
Denis Stoyanov
@xgrommx
Jun 14 2016 14:53
applicative for function is just a S combinator => S = x => y => z => x(z)(y(z))
functor for function is a operator composition or B combinator => B = x => y => z => x(y(z))
Aldwin Vlasblom
@Avaq
Jun 14 2016 15:00
@xgrommx So in theory I could compose functions using fmap? o.o
Denis Stoyanov
@xgrommx
Jun 14 2016 15:01
@Avaq sure, (\x -> x * 10) <$> (\x -> x * 20) $ 20 I got 4000
Aldwin Vlasblom
@Avaq
Jun 14 2016 15:01
Prelude> fmap (\x -> x + 1) (\x -> x + 1) $ 1
3
That's mindblowing! :D
Denis Stoyanov
@xgrommx
Jun 14 2016 15:02
for applicative in function of is just () => x and ap is just S combinator
Aldwin Vlasblom
@Avaq
Jun 14 2016 15:03
Yeah
Denis Stoyanov
@xgrommx
Jun 14 2016 15:03
Function.of = function(x) {
  return () => x;
};

Function.prototype.map = function(other) {
  return (...args) => {
    return this(other(...args));
  };
};

Function.prototype.ap = function(other) {
  let self = this;
  return (...args) => {
    return self(...args)(other(...args));
  };
};

Function.prototype.liftA2 = function(other, fn) {
  return Function.of(fn).ap(this).ap(other);
};
Aldwin Vlasblom
@Avaq
Jun 14 2016 15:04
It all makes a lot of sense, I just hadn't looked at it like that before. Just when you think you get something... ;)
Denis Stoyanov
@xgrommx
Jun 14 2016 15:06
@Avaq all have started from combinators =)
Aldwin Vlasblom
@Avaq
Jun 14 2016 15:07
So would it be better in Haskell if . were equal to fmap? Because now it's like a handicapped fmap is occupying that nice . character. :P
Namely an fmap that only works on Functors of the Function type.
Denis Stoyanov
@xgrommx
Jun 14 2016 15:09
@Avaq
import Prelude hiding ((.))

infixr 5 .

(.) = const(<*>) <*> const
as in combinators B = S(KS)K
Aldwin Vlasblom
@Avaq
Jun 14 2016 15:13
How do I become a Haskell developer?
Denis Stoyanov
@xgrommx
Jun 14 2016 15:15
@Avaq I don't know) for me it's just a fun
LeonineKing1199
@LeonineKing1199
Jun 14 2016 15:32

@LeonineKing1199 For starters, I want to make the function point free.

Point free is a goal, not a requirement or a standard. It should also never be done at the expense of readability or refactorability. Don't chase unicorns you can't catch.

Rafe
@rjmk
Jun 14 2016 16:51
@Avaq @xgrommx Also, there's a monad instance! The chain is something like >>= :: (b -> r -> a) -> (r -> b) -> r -> a. This is used as the Reader monad in Haskell for joining together computations that might all need access to some environment (e.g. DB connections, secret keys, ...). It's like composition where you provide every function with an extra argument beforehand