These are chat archives for ramda/ramda

20th
Sep 2017
Vince Speelman
@VinSpee
Sep 20 2017 14:35
Hi! I'm trying to refactor this code to be less procedural. Any thoughts?
const getBorderRadius = (theme, props) => {
  if (props) {
    return compose(
      reduce((acc, prop) => {
        if ([
          'br',
          'brx',
          'bry',
          'brt',
          'brr',
          'brb',
          'brl',
          'brtr',
          'brtl',
          'brbr',
          'brbl',
        ].indexOf(prop) > -1) {
          return {
            ...acc,
            ...getPropsForBRValue(prop, props[prop], theme),
          };
        }
      }, {}),
      keys
    )(props)
  }

  throw new Error(createError([
    'scale value',
    'i',
    'init',
  ])(value));
};
Rolf Strijdhorst
@rolfst
Sep 20 2017 15:32
not sure what you try to accomplish here with ...acc, but that value will never get set. also extract the array with values there. and check with arr.includes(prop) instead of indexOf(prop) > -1
Also try to avoid throwing an exception there in the getBorderRadius
if you start with getBorderRadius = (theme, props = {}) => ... you can get rid of the if(props) check
Kurt Milam
@kurtmilam
Sep 20 2017 15:39
acc is fine. It's the accumulator in the reduce function.
return { ...acc, ...fn() }
// is similar (using destructuring) to the following:
return Object.assign( acc, fn() )
Rolf Strijdhorst
@rolfst
Sep 20 2017 15:39
yes but it never gets set
oh wait it does it is the accumulator . I stand corrected
Kurt Milam
@kurtmilam
Sep 20 2017 15:40
It is set with an initial value, {}, then updated on each pass where props.key is contained by the array.
No worries :)
Vince Speelman
@VinSpee
Sep 20 2017 15:50
thanks - I extracted the array and changed the indexof to includes and added default props:
const getBorderRadius = (theme, whitelist, props = {}) => (
  compose(
    reduce((acc, prop) => ({
      ...acc,
      ...getPropsForBRValue(prop, props[prop], theme),
    }), {}),
    filter(contains(__, whitelist)),
    keys,
  )(props)
);
Kurt Milam
@kurtmilam
Sep 20 2017 16:03
@vinspee you should be able to use intersect in place of your filter.
Vince Speelman
@VinSpee
Sep 20 2017 16:10
ooooh nice @kurtmilam!
is it reasonable to make this more point-free? or just a dumb academic exercise?
Kurt Milam
@kurtmilam
Sep 20 2017 17:57
@VinSpee is getPropsForBRValue called elsewhere in the app, or just from within this function?
Either way, it might be interesting to see that function.
Vince Speelman
@VinSpee
Sep 20 2017 18:37
only in this instance. it's essentially a big switch statement that handles aliases
@kurtmilam:
const getPropsForBRValue = (THEME, prop, value = null) => {
  if (isValid('border-radius')(value)) {
    const formattedVal = getSize(THEME, value);
    if (THEME.sizes === null) {
      throw new Error('can\'t generate values without theme sizes');
    }

    switch (prop) {
      case 'br':
        return {
          borderRadius: formattedVal,
        };
      case 'brl':
        return {
          borderTopLeftRadius: formattedVal,
          borderBottomLeftRadius: formattedVal,
        };
      case 'brr':
        return {
          borderTopRightRadius: formattedVal,
          borderBottomRightRadius: formattedVal,
        };
      case 'brt':
        return {
          borderTopLeftRadius: formattedVal,
          borderTopRightRadius: formattedVal,
        };
      case 'brb':
        return {
          borderBottomLeftRadius: formattedVal,
          borderBottomRightRadius: formattedVal,
        };
      case 'brtr':
        return {
          borderTopRightRadius: formattedVal,
        };
      case 'brbr':
        return {
          borderBottomRightRadius: formattedVal,
        };
      case 'brbl':
        return {
          borderBottomLeftRadius: formattedVal,
        };
      case 'brtl':
        return {
          borderTopLeftRadius: formattedVal,
        };

      default:
        return {};
    }
  }
  return {};
};
Rolf Strijdhorst
@rolfst
Sep 20 2017 18:58
seems to me that this is much clearer:
const brRadius = {
 'br': {
          borderRadius: null,
        },
 'brl':{
          borderTopLeftRadius: null,
          borderBottomLeftRadius: null,
        },
 'brr': {
          borderTopRightRadius: null,
          borderBottomRightRadius: null,
        },
'brt':{
          borderTopLeftRadius: null,
          borderTopRightRadius: null,
        },
 'brb': {
          borderBottomLeftRadius: null,
          borderBottomRightRadius: null
        },
'brtr': {
          borderTopRightRadius: null,
        },
'brbr':{
          borderBottomRightRadius: null,
        };
'brbl': {
          borderBottomLeftRadius: null,
        },
'brtl': {
          borderTopLeftRadius: null,
        }
mapObjIndexed((num, key,  value) => {value.[key] = formattedVal}, defaultTo({}, brRadius[prop]))
Kurt Milam
@kurtmilam
Sep 20 2017 19:44
@VinSpee I haven't had time to look at it yet, but if it's only called from within this one function, I'd consider moving it inside the function. I generally only make new functions for things I plan to reuse.
Unless this is a common theme running through the application (like maybe there are a bunch of different but similar getPropsForXXXValue functions around the app). But one of my rules is that I don't extract logic into its own function unless I expect to call that logic from multiple sites.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 19:54
Not to be contrarian, but do you find that this leads to larger and more complicated functions? I tend to break things up pretty small to keep my code organized, even if I am only using something once. Creating functions lets me keep my thoughts in a single layer of abstraction, as well as allowing me to give names to things so I can keep track of what's happening
Kurt Milam
@kurtmilam
Sep 20 2017 19:55
I find that it leads to keeping related logic together in one place, rather than needing to hunt around to piece together what's going on.
I try to abstract reusable patterns, and I have also found that what I call 'premature abstraction' can lead to breaking a flow up in the 'wrong' way such that you abstract out something that is not reusable and obfuscate a reusable abstraction that's now split between two separate functions.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:00
I can see how that would make sense, but I think there are a number of abstractions that almost always make sense.
if (obj.a != null && obj.b && obj.b.length > 0 && obj.c === 'yes' /* ...*/) { /* blah */}

//I would argue for this almost every time, even if the check is only in one place
if (isValid(obj))  { /* blah */}
Kurt Milam
@kurtmilam
Sep 20 2017 20:01
In this case, Vin's internal function is called from within a compose pipeline, and if that's the only call site, I'd tend to favor leaving the function in the pipeline. The large block of code (currently a switch statement) can be moved outside of the function or outside of the compose pipeline, at least, to help keep the containing function from blowing up.
Barney Carroll
@barneycarroll
Sep 20 2017 20:01
@kurtmilam just wanted to drop in and say thanks for rubber ducking my contrived problem yesterday… I ended up using a fold over Promises to daisy chain a sequence of resolves, then wrote Throttle as a Promise. Not ideal but produced the best combination of terse and rationable in context :grimacing:
Kurt Milam
@kurtmilam
Sep 20 2017 20:02
@barneycarroll My pleasure. Sorry I wasn't able to help you find your way to a more Promising solution :)
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:02
:point_up: With the example above, I would definitely split the case statement out into an object and keep it outside the function body. It's too much visual clutter for me to be able to track what's going on if that was in the middle of it

a more Promising solution

:laughing:

Kurt Milam
@kurtmilam
Sep 20 2017 20:03
(pun intended) ;)
I'm glad you were able to get it worked out, though. It was an interesting problem.
brb
Barney Carroll
@barneycarroll
Sep 20 2017 20:05
Haha yeah but TBH promises were just logical here. The whole system depends on asynchrony and a composition of generic FP functions wasn't… isomorphic.
Sure streams might have been better in a pure world of light and beauty, but built-ins did the job here without much deferred logic
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:09
Yeah, until you reach certain limits it can be easier to just work with what you've got
Kurt Milam
@kurtmilam
Sep 20 2017 20:16
I've built my share of solutions on top of Promises, but I sure do like working with observables if I have the choice.
:point_up: September 20, 2017 10:00 PM
I don't run into many examples like that in my code, and if I did, I think I'd be reaching for an abstraction like Sum Types rather than moving those rules outside of my function.
Which is maybe an example of how premature abstraction could lead to missing out on a more powerful, reusable abstraction.
I'd move that switch statement out, but I'd either move it into an object, like @rolfst proposed, or I'd look at using something like a Sum Type.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:22
Sum types are great, and I use them as well, but I still think the abstraction holds.
const eitherFromPred = pred => obj => pred(obj) ? Right(obj) :  Left(obj); //Could be more in depth if necessary

const result = eitherFromPred(isValid)(obj);
By pulling your validation out into its own function, you can move it where you need it when you find a better abstraction
Refactoring to use a sum type instead of a conditional shouldn't require you to know what your validation logic is
Kurt Milam
@kurtmilam
Sep 20 2017 20:26
eitherFromPred looks very reusable to me.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:27
Absolutely.
But isValid is still only used once
Kurt Milam
@kurtmilam
Sep 20 2017 20:28
Where is it defined?
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:29

https://gitter.im/ramda/ramda?at=59c2cc951081499f1f5581ba

I was referencing my original code, where I suggested pulling the validation into its own function makes sense, even if you only validate in a single place

By moving my validation rules outside my function, I can still move to a better abstraction, and see the structure more clearly
Kurt Milam
@kurtmilam
Sep 20 2017 20:31
Do you define isValid outside of the putative function your if was in?
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:31
Absolutely
Even if it's only used right there
Kurt Milam
@kurtmilam
Sep 20 2017 20:33
Interesting. I think I'd lean toward defining it right inside the call to eitherFromPred.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:33
Rather than starting with a big function and then breaking out the parts to reuse, I tend to start with small atomic functions that do one thing, and then build composite functions to combine and manipulate those atoms
Bringing it back to the original example, I think it totally makes sense to keep getPropsForBRValue rather than move it into the body. I might also break it up further, keeping my reducing function outside the main body as well, and calling getPropsForBRValue from there.
Kurt Milam
@kurtmilam
Sep 20 2017 20:36
That is interesting. I'd say I go about it in an almost opposite manner. When I started doing FP, I wrote lots of small functions, but I realized after a while that the larger blocks of logic were often chopped up in the wrong places and that skipping through a mass of functions that were only ever called from one site to figure out an entire, complex operation was not to my liking.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:37
By breaking things up, we can have, at the bottom, functions that are specific to our data - like getPropsForBRValue, and then functions that operate on structures - library functions like reduce as well as self defined ones like eitherFromPred
Barney Carroll
@barneycarroll
Sep 20 2017 20:37
@kurtmilam I have some kind of tribal bias against observables, but I want to cure it. Can you recommend any material that might jilt my irrational cynicism? :)
Kurt Milam
@kurtmilam
Sep 20 2017 20:42
@barneycarroll unfortunately, I think it can be difficult to get into the mindset of working with observables. I got into them gently, using flyd and mithril's stream, at first. My goal was a stream-based state-management system, and I think I did some neat stuff with it, but I only scratched the surface.
Then I decided to take calmm-js for a spin. It's React/preact - based. I would love to have the time to figure out what it would take to be able to use mithril as a vdom drop-in with calmm.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 20:44
I'm currently planning a project to get up to speed with the calmm stuff
It looks really cool
Kurt Milam
@kurtmilam
Sep 20 2017 20:44
Unfortunately, it took a lot of time for me to start feeling comfortable with observables as they're used in calmm, and I'm still learning, but as I continue to learn it, I continue to be impressed by the power there.
I think it's the best thing since sliced bread.
I just started looking for a job and would love to find an employer who was at least interested in expending some resources to evaluate FRP-flavored frameworks like calmm.
Unfortunately, I get the feeling that those are few and far between right now.
@Bradcomp this boilerplate may be a good basis for your first calmm project.
I've contributed a tiny bit to it.
So that may be enough to scare you away from considering it :D
But it's what I used as a starting point for a project I'm working on atm.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 21:03
@kurtmilam I think I was using that, not as a boilerplate but as a point of reference
Kurt Milam
@kurtmilam
Sep 20 2017 21:04
:+1:
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 21:04

I'd say I go about it in an almost opposite manner.

I think both are valid approaches. I had some really messy projects I've had to deal with at previous jobs, so I have an aversion to big functions.

Kurt Milam
@kurtmilam
Sep 20 2017 21:08
I certainly try to break functions down when I discover reusable abstractions, and few of my functions are ever what I'd consider 'big'. I strive for lots of small, reusable functions, but I don't tend to break up logic into pieces that are not likely to be reused.
Looking at Vin's original function, it consists of one compose pipeline with three functions (reduce, intersect and keys, plus a reducer fn. My thought on bringing in getPropsForBRValue was that it might make it easier to make the reducer point free. I'd be comfortable with a function of that complexity, but I wouldn't want it to have much more complexity than that, with few exceptions.
Kurt Milam
@kurtmilam
Sep 20 2017 21:14
Also, to be clear, it's certainly not my intention to be argumentative. I appreciate your input and point of view and find it an interesting topic to discuss.
Brad Compton (he/him)
@Bradcomp
Sep 20 2017 21:34

it's certainly not my intention to be argumentative.

Me neither! I hope I haven't come across that way. It's super interesting to me to hear various thought processes on program design

Also, sorry for the delay, had a meeting
Kurt Milam
@kurtmilam
Sep 20 2017 21:43
No worries, all good on both counts!
Barney Carroll
@barneycarroll
Sep 20 2017 22:16
I hadn't heard of calmm. I've been told with great authority that MobX will solve all my problems by people who can't straw man any given problem, so I have a narrative problem :)
Kurt Milam
@kurtmilam
Sep 20 2017 22:17
the author is responsive in the gitter channel.
You may have heard of the partial.lenses library - that's one part of calmm.
I believe the guy who came up with meiosis uses partial.lenses, and I've seen it mentioned a few times in the mithril gitter.
This Introduction to Calmm document is dense, but may be interesting. There are some links to live examples in the wiki.
For me, the reduction in boilerplate was the key. I started toying with React+Redux and felt like there was way too much boilerplate going on.
Kurt Milam
@kurtmilam
Sep 20 2017 22:22
Then I played with mithril, which I like, but I was after a good state mechanism. Started building my own based on streams and partial.lenses, then realized that partial.lenses were a part of a solution that already had a way more evolved system of state management than I had managed, so I abandoned my efforts and decided to give calmm a go.
I feel like there's zero boilerplate and a powerful state-management system that doesn't require much thought or effort to use once you grok observables and lenses.
I abhor boilerplate. I'd prefer to spend more time solving interesting problems and less time typing the same thing repeatedly, and I also think excessive boilerplate introduces unnecessary technical debt to a project.
Kurt Milam
@kurtmilam
Sep 20 2017 22:27
calmm uses React idiomatically, but it wraps it in a way that allows you to forget about it. I've never written 'React' in stuff I've built with calmm outside of import statements.