These are chat archives for ramda/ramda

1st
Mar 2017
James Forbes
@JAForbes
Mar 01 2017 04:09

Would anyone be opposed to some flipped versions of append and prepend, called appendTo

I've found it comes up often when scanning a stream to accumulate a list: scan(values, flip(append), 0), because in reduce/scan the first argument is the list, not the value to add to the list

Rick Medina
@rickmed
Mar 01 2017 05:16
I think R.reduceRight(R.prepend) works in ramda? probably there's not a scanRight
James Forbes
@JAForbes
Mar 01 2017 05:26
@rickmed in this case though the scan is provided by the stream library
and then there's list.reduce
Rick Medina
@rickmed
Mar 01 2017 05:28
flyd? :)
James Forbes
@JAForbes
Mar 01 2017 05:34
The best stream library https://github.com/paldepind/flyd
Sudarsan Balaji
@artfuldev
Mar 01 2017 05:47
@JAForbes any reasons why you think it's the best? :)
James Forbes
@JAForbes
Mar 01 2017 06:14

Yeah. Rx has a philosophy that directly controlling how events are pushed into a stream, is an anti-pattern.

Because of that philosophy you end up requiring many "operators" that abstract over operations that are otherwise trivial when you control the source.

In flyd, a stream is always what Rx would call a subject. To connect a stream to an event listener, you just pass the stream reference in as a callback.

It exposes a tiny api, and recommends composition to achieve the kind of functionality that would be baked in, in Rx's massive API. It turns out, more often than not, all you need is R.sequence and map to implement 90% of Rx's operators.

It's fast. Not as fast as most, but fast. And it adhere's to fantasy land's applicative functor spec. It has built in support for transducers. And its tiny.

It's also got a beautiful interface for ending. Every stream exposes a stream called end that is itself a stream. This allows you to rig up streams that respond to another stream ending.

I've used it in production on the server and the client, and I just can't say enough about it, it's brilliant.

And I have to say most is fantastic also, for other reasons.
Sudarsan Balaji
@artfuldev
Mar 01 2017 06:16
good to know, thanks :)
James Forbes
@JAForbes
Mar 01 2017 06:16
yeah no problem :+1:

flyd and virtual dom frameworks go together so well

e.g. here's a tiny redux implementation.

const Stream = require('flyd')
const actions = Stream.stream()

const sum = Stream.scan(R.add, 0, actions)

const view = sum.map(function(sum){
  return h('div'
    ,h('p', 'Sum = ', sum )
    ,h('button', onclick: () => actions(+1), '+' )
    ,h('button', onclick: () => actions(-1), '-' )
  )
})

view() // will give you the last emitted view
And if you use something like UnionType or SumType, you can have a stream of actions, and intercept IO actions (network requests, child processes, database requests, etc) in your interpreter. Which works really well on the client and the server.
James Forbes
@JAForbes
Mar 01 2017 06:23

So

Stream.scan( compose( tap(interpreter), update) , initialState, actions)

Where update is your impure code, the majority of your program. And interpreter intercepts IO actions differently based on context. You might have an interpreter for tests, or server side rendering, or client side rendering ... whatever

So in conclusion, its tiny, its fast, but its not a toy, it scales to any problem domain.
Denis Stoyanov
@xgrommx
Mar 01 2017 06:25
Most js more interesting)
Sudarsan Balaji
@artfuldev
Mar 01 2017 06:26
@xgrommx you could've said "most is most interesting" LOL
James Forbes
@JAForbes
Mar 01 2017 06:26
most is great, I prefer flyd
most is monadic, while flyd is just an applicative functor, I don't think streams need to be monadic, it encourages nesting streams which to me is a sign that your trying to use streams in place of other data types that are more specialized
Sudarsan Balaji
@artfuldev
Mar 01 2017 06:29
hey @JAForbes where do I read more about 'applicative functor's?
James Forbes
@JAForbes
Mar 01 2017 06:29
Also most has subjects in another repo, it discourages using subjects directly, which is a fine position, but I don't agree with it
But most is incredible, and you can't go wrong if you choose it, I just think flyd hits the perfect balance of api surface complexity / power
Probably this chapter: https://github.com/MostlyAdequate/mostly-adequate-guide/blob/master/ch8.md of mostly adequate guide @artfuldev
Denis Stoyanov
@xgrommx
Mar 01 2017 06:35
@JAForbes paldepind/flyd#57
Sudarsan Balaji
@artfuldev
Mar 01 2017 06:35
thanks :)
James Forbes
@JAForbes
Mar 01 2017 06:54

@xgrommx I know you know your stuff when it comes to streams, But in my opinion this idea that ajax requests should return streams/observables is misguided, its only required if you're doing IO in your app (e.g. an ajax request that returns a stream needs to be flattened).

Have an actions stream, scan it. When you want to do IO, emit an action to do that, and then have your interpreter intercept it, run the request, and then emit a Response action with the data. It gives you so much more flexibility for testing, and running your app in different contexts.

There's no stream flattening, you just use the actions stream as the promise callback:

// in your interpreter
request.then(function(response){
  actions({ type: 'Response', endpoint: ..., data: response })
})

In Rx's philosophy, everything is an observable, and that means you need to flatten streams, and you need error handling etc. In Flyd, error handling can just be a stream of Eithers, or a stream of Maybe's or a stream of Validation. Its up to you.

But there's never a need to flatten a stream.

Keith Alexander
@kwijibo
Mar 01 2017 10:13
@JAForbes nice summary - have you written these thoughts up anywhere (else) ?
James Forbes
@JAForbes
Mar 01 2017 10:20
Thanks @kwijibo, I've been meaning to, just can never seem to find the time :(
Denis Stoyanov
@xgrommx
Mar 01 2017 11:34
@JAForbes any examples or please correct my version)
Syaiful Bahri
@syaiful6
Mar 01 2017 12:20
@JAForbes nice
Syaiful Bahri
@syaiful6
Mar 01 2017 12:29
i actually use something like @JAForbes described. But, it came from Purescript Pux. My app just consist state stream and action stream. the application function take something like elm architecture. Update function, initial state, and subscription function.

const forwardTaskToStream = (stream, task) => {
  return task.fork(err => {
    const d = new Error(err)
    throw d;
  }, stream)
}

function application(config) {
  const actionStream = stream()
  const input = map(toArray, actionStream)
  const effModelSignal = scan(foldActions, noEffects(config.init), input)
  const stateSignal = dropRepeats(map(getEffState, effModelSignal))

  map(mapAffects, effModelSignal)

  function mapAffects(eff) {
    const effects = eff.effects.concat(config.subscriptions(eff.state)).map(t => later(0, t))
    effects.forEach(eff => {
      forwardTaskToStream(actionStream, eff)
    })
  }
  function foldActions(effModel, actions) {
    return actions.reduce(invokeAppUpdate, noEffects(effModel.state))
  }
  function invokeAppUpdate(eff) {
    return config.update(action, eff.state)
  }
  return {
    state: stateSignal,
    action: actionStream
  }
}
Syaiful Bahri
@syaiful6
Mar 01 2017 12:42
initially i want to use cycle js, but i think i just fight state management with their models, and i feel use stream for everythings just don't make sense.
Sudarsan Balaji
@artfuldev
Mar 01 2017 12:44
@syaiful6 interesting. I find not using streams for everything that changes doesn't make sense.
to me it feels like if it changes over time, and if there's a way to represent such stuff, why not use it always? just my thought, though - I don't suppose it should be the universal truth
Syaiful Bahri
@syaiful6
Mar 01 2017 12:47
@artfuldev yeah that why i just use 2 streams in my app, they are 2 important part, the actions stream that signal applications to do their work, and state stream (this used to connects view function usually).
i was using cycle js before, and i believe their model suffer state management, and their cyclyc stream. i face issue the stream doesn't emit, different behavior of memory stream which i dont remember, and the orders of streams (sinks?) and drivers are mater.
Bravi
@Bravilogy
Mar 01 2017 13:35
hey guys, if I have an array and want to do something with let's say 2 last elements and then do something else with the rest of the elements and then concat the results together, can I do it in 1 compose function?
Rafi
@Rafi993
Mar 01 2017 14:03
is there any way to do this in point free style
thanks for the help :)
const baseList = [1,2,3,4,5,2,3];
const searchList = [4,5];

reduce(min,Infinity,map(x => findIndex(equals(x), baseList), searchList))
Iain Freestone
@iainfreestone
Mar 01 2017 14:09
reduce(min,Infinity,map(pipe(equals,findIndex(__, baseList)), searchList))
Rafi
@Rafi993
Mar 01 2017 14:12
awesome thanks @iainfreestone
and it would be awesome if we could comment and uncomment selected code in ramda repel it would be great help when trying out things
Bravi
@Bravilogy
Mar 01 2017 14:27
anyone?
James Forbes
@JAForbes
Mar 01 2017 14:30

@Bravilogy few things, you could use converge, or you could use lenses

converge would let you perform n transforms on the same input before passing it to a final function where each transform is an arg to that function.

lenses would let you transform aspects of that list without losing the original the context ( the list itself )

Bravi
@Bravilogy
Mar 01 2017 14:33
yeah but lenses can't deal with multiple indexes, can they?
James Forbes
@JAForbes
Mar 01 2017 14:33
var a = compose( map( multiply(100) ), take(2) )
var b = drop(2)

converge( concat, [a, b])([1,2,3,4,5]) // [100, 200, 3, 4, 5]
Bravi
@Bravilogy
Mar 01 2017 14:33

let's say I have

const arr = [1, 2, 3, 4];

and I want to do inc over first 3 elements, dec on last 2 and then concat them together

James Forbes
@JAForbes
Mar 01 2017 14:34
Well technically they could if you wanted, but.. you can compose multiple lens transformations
Bravi
@Bravilogy
Mar 01 2017 14:34
hmm I'll have a look at converge
I was going down ap road
James Forbes
@JAForbes
Mar 01 2017 14:34
so compose( over( lensIndex(0), f ), over( lensIndex(1), f ) )
after calling f on 0 and 1, you'll get back the full list
Bravi
@Bravilogy
Mar 01 2017 14:35
right
I was kinda trying to avoid lensIndex(0), 1, 2, and so on but ok I'll give it a shot too
thanks!
James Forbes
@JAForbes
Mar 01 2017 14:36
well there's also [0,1].map(lensIndex)
so compose( ... [0,1].map( lensIndex ).map( over ).map( f ) )
Bravi
@Bravilogy
Mar 01 2017 14:37
I think converge is what I'm looking for :)
James Forbes
@JAForbes
Mar 01 2017 14:37
:D
Bravi
@Bravilogy
Mar 01 2017 14:37
thanks for your help
James Forbes
@JAForbes
Mar 01 2017 14:37
my above code wouldn't work anyway, because js' map passes in those extra args :(
Bravi
@Bravilogy
Mar 01 2017 14:39
that's fine, I got the idea :D
yep converge works like a charm
ramda is like a superman
James Forbes
@JAForbes
Mar 01 2017 14:41
yeah I like converge
Rick Medina
@rickmed
Mar 01 2017 14:55
cool discussion about streams :) @JAForbes what are you referring to with "Have an actions stream, scan it."?
jgr0
@anshumanvenkatesh
Mar 01 2017 14:56
Is there any good place where I can practice my ramda-fu? I am looking something similar to http://reactivex.io/learnrx/
Rick Medina
@rickmed
Mar 01 2017 15:09
@anshumanvenkatesh build something!
James Forbes
@JAForbes
Mar 01 2017 15:18

@rickmed thanks. Something like this:

const $ = require('sanctuary-def')


const Activity = $.RecordType({
  activity_id: $.String
  // etc
})

const Type = require('sum-type')($, { checkTypes: true, env: $.env.concat([Activity]) })

const Actions = Type.Named('Action', {
  FetchUser: { user_id: String },
  ReceiveUser: { user_username: String, user_avatar_src: String  }
  FetchActivity: { user_id: String },
  ReceiveActivity: { activity: $.Array(Activity) }
  Like: { post_id: String, user_id: String },
  PageRedirect: { url: String },
  Comment: { ... etc }
})

function update(actions){
  return function(state, action){
    return Action.case({
        PageRedirect: (url ){
           const {user_id} = parseURLSomeHow(url)
           actions(  Action.FetchUser({ user_id })  )
           actions(  Action.FetchActivity({ user_id })  )

           return R.merge( state, {
              loading: true
           })
        }
        ,ReceiveUser: (user){
           R.merge( state, {
              loading: true,
              user
           })
        }
        ,// etc sum-type will throw if you don't implement all cases
    }, action)
  }
}

Now, we scan the actions stream, using our above update function

const actions = Stream.stream()

// this is how we run our app.
const state = Stream.scan( update, initialState, actions )

But we also want to do actually fetch things, so this is where we interpret our actions stream.
You can do it a few ways, here's one.

R.sequence( Stream.stream, [ actions, state ])
  .map(function( [action, state ]){
    return Action.case({
       FetchUser: ( user_id ){

           makeApiRequest(user_id)
               // create the particular action type
               .then(Action.ReceiveUser)
               // inject it into the stream so update can react to it
               .then(actions)
       }
       // do nothing for all other actions
       ,_: noop 
    })
  })

Now if we were testing, we could just pass a user object that matches our schema straight in, and skip the network layer. If this was serverside rendering, we could just call the db inline, and inject the response back in.

We're basically tapping into a stream of pure descriptions of what we want to happen, and then optionally injecting impure responses into the actions stream.

And with sanctuary you get some helpful error messages when your data doesn't pass muster.

And you don't need sanctuary, or union types, you can do the exact same thing with a lot less ceremony.

This is basically the same thing: https://gitter.im/ramda/ramda?at=58b66736de504908222cb0d2

But the approach scales
And be warned that code above is probably full of typos, its not tested, just a sketch
Rick Medina
@rickmed
Mar 01 2017 15:45
@JAForbes very cool.
are you using promises??? shameful! :)
James Forbes
@JAForbes
Mar 01 2017 15:47

Ha! You can use futures :)

In this context promises are fine though.

The entire interpreter is impure, and eager
Rick Medina
@rickmed
Mar 01 2017 15:48
yeah just jk, I use promises sometimes like that as well
Rick Medina
@rickmed
Mar 01 2017 19:22
Off-topic (sorry): are there article writers here? I'm looking for a platform (medium, own domain, github, etc...)
would love some pointers
Drew
@dtipson
Mar 01 2017 19:41
medium is ok
you have to copy in some formatting that they don't actually give you in the interface, and mostly I just use images or gists
Rick Medina
@rickmed
Mar 01 2017 19:44
yeah, I think I'm going with medium (with personal backup) @dtipson thanks!
Keith Alexander
@kwijibo
Mar 01 2017 20:32
@rickmed fwiw, I've been using github pages and https://github.com/gatsbyjs/gatsby
Robert Mennell
@skatcat31
Mar 01 2017 21:01
Okay maybe I'm bad at googling this, but is there a simple explination of what Kleisli composition is?
Robert Mennell
@skatcat31
Mar 01 2017 21:09
And for that matter why I would use it over pipe,?
Rick Medina
@rickmed
Mar 01 2017 21:16
@skatcat31 have you used monads?
anyway, is a way to compose monads as you would with "normal" function composition
Robert Mennell
@skatcat31
Mar 01 2017 21:21
a function that does a specific computation operation(much like most of Ramda)?
Rick Medina
@rickmed
Mar 01 2017 21:27
I don't have a good article about monads at hand (maybe someone else does?), but it will come handy (if you decide) when working with Tasks/Futures, Maybes, Eithers...
Robert Mennell
@skatcat31
Mar 01 2017 21:27
I htink I'm going to go and find a better description of functional programming than I have currently...
let me know how it went!
Brad Compton (he/him)
@Bradcomp
Mar 01 2017 21:30

I don't have a good article about monads at hand (maybe someone else does?)

My favorite intro

Robert Mennell
@skatcat31
Mar 01 2017 21:30
haha I had just foujd the in pictures one.
okay so IF I'm understanding this kind of correctly, a monad is somewhat close to String->Int
IT iwll take a value, and return a new value with context
Robert Mennell
@skatcat31
Mar 01 2017 21:36
time for Frisbee
Gabe Johnson
@gabejohnson
Mar 01 2017 22:04
@Bradcomp that one's great!