These are chat archives for ramda/ramda

12th
Feb 2017
Drew
@dtipson
Feb 12 2017 02:08 UTC
@kwijibo hmmm. Reader.map and Function.contramap seem very similar as well, in a certain sense I suppose. Given a function f and then later another function r, Function(f).contramap(r) is the composition of f(r(x)). And Reader.map is the same
Hardy Jones
@joneshf
Feb 12 2017 02:12 UTC
It had better not be.
If it is, something is broken.
Unless I'm misunderstanding what you're saying.
Keith Alexander
@kwijibo
Feb 12 2017 08:34 UTC
@dtipson My understanding, based on your chain example, is that Reader is Function - or at least, a monadic interface for Function
so contramap in both cases (I think) is const contramap = f => run => { return x => run(f(x)) }
Keith Alexander
@kwijibo
Feb 12 2017 08:53 UTC
R.map treats functions as functors - functions are functors in the sense that they are containers of their return values - you release the value by returning the function
this is why mapping over a function is a composition
Hardy Jones
@joneshf
Feb 12 2017 15:37 UTC

Over the past few years, it's been realized that thinking of a Functorf–as a "container" of things is not a metaphor that is worth latching on to. It's an okay metaphor for coming to grips with certain data types that are Functors. But, it's actively detrimental to understanding what a Functor is and being able to see a data type for it being a Functor. The metaphor breaks down when you move on from examples like Either a, Tuple a, etc. For instance, trying to understand how a function is a Functor. Or how Proxy is a Functor.

What I'm saying is, it's okay to not stretch the "container" metaphor to fit functions as well. In fact, it's recommended that you don't try and stretch it. It is important that you recognize that the "container" metaphor is not a definition, and functions are one example of why it isn't a definition. If you need a different metaphor for functions, that's fine. But, also recognize that it won't be a definition either and you'll be in the same boat for the next thing that doesn't fit either metaphors.

Denis Stoyanov
@xgrommx
Feb 12 2017 15:44 UTC
Type is a function but without body
Wanderley Guimarães
@wanderley
Feb 12 2017 21:58 UTC

So, like this?

const safeSomething = converge(tryCatch, [doSomething, reportError]);

In this example, will doSomething and reportError be executed always?

Im trying to compose a function like so:

function safeSomething(context, value){
    try {
        return doSomething(context)(value);
    catch (e){
        return reportError(context)(value);
    }
}

I've gotten as far as:

const safeSomething = context => tryCatch(doSomething(context), reportError(context));

Can anyone help making this point free?

this is the original code

doSomething and reportError are curried functions. Then converge will just bind context to them and return two functions to tryCatch which one will run the first one and if it throws will run the second one.
Jonah
@jonahx
Feb 12 2017 22:03 UTC
Can anyone recommend a good library for easily creating custom types? Ideally, I want to be able to specify custom errors as well. Essentially, I’m looking for something that would make it easier to write custom types like this:
function NumericRange(min, max) {
  const isNumber = x => typeof x === 'number';
  if (!isNumber(min)) throw new NumericRange.InvalidRangeMin(min);
  if (!isNumber(max)) throw new NumericRange.InvalidRangeMax(max);
  if (min > max)      throw new NumericRange.MinGreaterThanMax({min, max});

  return {
    min: () => min,
    max: () => max,
    setMin: newMin => NumericRange(newMin, max),
    setMax: newMax => NumericRange(min, newMax),
    minMax: () => ({min, max})
  };
}
NumericRange.InvalidRangeMin = ErrorType('InvalidRangeMin', function() {
  return `'${this.data}' is not a valid min value.`
});
NumericRange.InvalidRangeMax = ErrorType('InvalidRangeMax', function() {
  return `'${this.data}' is not a valid max value.`
});
NumericRange.MinGreaterThanMax = ErrorType('MinGreaterThanMax', function() {
  return `Min (${this.data.min}) can't exceed max (${this.data.max}).`
});
Stefano Vozza
@svozza
Feb 12 2017 22:12 UTC
sanctuary-def might be of interest
Alex Deas
@alex-deas
Feb 12 2017 22:13 UTC
@jonahx how would you want to use the error messages, from the looks of it those would be used for user feedback/monitoring?
Jonah
@jonahx
Feb 12 2017 22:15 UTC
@svozza thx, looking now. @alex-deas, I’m not sure I understand the question. I just want clear, domain-specific error messages. Whether it’s for appearing in a log, something airbrake emails me, or something that is user-facing. Although in the latter case, they may be mapped to even more clear, user-appropriate messages, depeding on the use-case.
Alex Deas
@alex-deas
Feb 12 2017 22:18 UTC
In which case sanctuary-def probably wouldn't suit your use case as santuary types are usually turned off in prod. You're better off moving that logic out of the type constructor imo; instead having a function that will do the validation and return an either Error Int
Jonah
@jonahx
Feb 12 2017 22:23 UTC
@alex-deas I’m interesrted in understanding this pov. Could you explain the advantages of this approach? Because my instinct would be that having it in the type constructor is less error prone. I want it to be impossible to create an invalid type. So it seems to me the “validation” in this case is part of the definition of the type itself. To take a simpler example say I wanted an EvenNumber type. It seems odd to me that the validation would be separate and that it would be possible to create an invalid instance of the type
Jonah
@jonahx
Feb 12 2017 22:28 UTC
@alex-deas To be clear, I could see an argument for wanting to use the validation code outside the type constructor as well, so I could see an argument for pulling it out and then using it inside the type ctor as well as somewhere else. But I’m having trouble seeing the argument for not checking in the type ctor as that seems like the whole essence of the type.
Alex Deas
@alex-deas
Feb 12 2017 22:30 UTC
That would - I assume be a case of tying your definition too strictly to your implementation. So let's say you take in a user input of name which you expect to be a string, then want to check it against a Dictionary of valid names, so that Linus would be accepted but foo wouldn't. Pushing that logic into the string means your type is tied to that use case; if we come along to create a lastname input, you'd need to recreate the entire type validation when all you want to do is lookup against a different dictionary. Your type should really be saying something about which functions can be run on it, rather than what value it contains.
Of course, if you want things like a custom error message for number into a constructor where you expect a string, then I've got the wrong end of the stick and what I've said is irrelevant
Keith Alexander
@kwijibo
Feb 12 2017 22:33 UTC
@jonahx have you looked at https://github.com/paldepind/union-type ?
Jonah
@jonahx
Feb 12 2017 22:34 UTC
@kwijibo yes, i have today. and it looks very cool, but i actually don’t see the application here. if i’m missing something please correct me.
@alex-deas, in the example you mentioned wouldn’t it just be that the type is like CheckedName and you have a factory that can produce a specific instance of the type ctor. But I’d still have the same error message like “The name X is not in the list of allowed names” regardless which dictionary I was using. That is, I still see that example fitting into the paradigm I’m suggesting above.
Keith Alexander
@kwijibo
Feb 12 2017 22:36 UTC
@jonahx it lets you define types that validate their arguments
daggy lets you create types, but doesn't validate its arguments
Jonah
@jonahx
Feb 12 2017 22:39 UTC
@kwijibo, again, correct me if i’m wrong, but i thought union-type was more of like a souped up enum or Maybe. That is, your type can take on various values (sometimes a real type, sometimes one or more error states, etc), and union-type allows you to specify all that but still use it nicely like a single type. ie, the switching logic is contained. but the NumericRange type doesn’t fall into the paradigm, so I don’t see the value of defining it with union-type vs just as vanilla type ctor
Keith Alexander
@kwijibo
Feb 12 2017 22:40 UTC
@jonahx it lets you define not just union types, but the types expected by the constructors

if you look at the readme

var Point = Type({Point: [Number, Number]});

if you were to call

Point.Point("Hello", "World")

You'd get a TypeError - you can also define custom validation functions for your types beyond the native js types. I agree it doesn't help you with everything (in that you have to define those functions yourself), but it cuts down on the if(x) throw TypeError( boilerplate a little

Keith Alexander
@kwijibo
Feb 12 2017 22:48 UTC
sanctuary-def seems to be a more industrial strength version of the same kinda thing - and there's something like this in the folktale rewrite too IIRC
Jonah
@jonahx
Feb 12 2017 22:49 UTC
@kwijibo, sanctuary-def seems like what i want, although @alex-deas said it wasn’t meant to be used in production, which would be the one place i need to use it
Keith Alexander
@kwijibo
Feb 12 2017 22:50 UTC
i think he meant that you can (and should) turn type checking off in production for performance reasons
Jonah
@jonahx
Feb 12 2017 22:51 UTC
@kwijibo yeah, but that would defeaut the whole reason i’d be using it.
(maybe i’m misunderstanding)
Keith Alexander
@kwijibo
Feb 12 2017 22:52 UTC
maybe I misunderstand - normally TypeErrors are useful in development to let you know your code is broken - and you don't normally want to ship production code that throws TypeErrors, because it's broken
(ie, you wanna fix your code first so it doesn't throw Type Errors)
Alex Deas
@alex-deas
Feb 12 2017 22:54 UTC
@jonahx Yeah you can have that logic in the type constructor. There are cases when it would fit, although they aren't that common in my experience. From your example it seemed like you might be trying to put too much logic into the constructor; but it's your call. In particular with JS I try to put as much type validation into generic functions because the language has such bad OOTB support for it
Jonah
@jonahx
Feb 12 2017 22:57 UTC
@alex-deas so what I want to do is to have the actual logic in generic functions (isNumber, isBetween, whatever). And I want the custom types to delegate all their work to those. But I still want the custom types. Because that maps naturally to how I think, and will make the code and logic much more readable.
@alex-deas to take a trivial example that might help clarify where i’m coming from, how would you implement an EvenNumber type?
Keith Alexander
@kwijibo
Feb 12 2017 22:59 UTC
@jonahx in your example, are min and max provided by an end-user? should the end user see the error message? or is it a development aid - you as the developer see the TypeErrors because, eg a function isn't returning the type you expect?
Jonah
@jonahx
Feb 12 2017 23:00 UTC
@kwijibo, both cases. sometimes the object will be constructed from data in a db which i know is valid, sometimes it will come from an end user.
Keith Alexander
@kwijibo
Feb 12 2017 23:02 UTC
@jonahx I mean, are you wanting to use TypeError's to validate what a user gives you?
or to validate that you've wired up your functions correctly?
Alex Deas
@alex-deas
Feb 12 2017 23:03 UTC
I wouldn't implement an even number type. Even Numbers aren't a type, Numbers are the type; there are a subset of Numbers which are even. For a more real world example, let's say I have a server that takes a UUID that refers to a user session; anything that matches xxxx-xxxx-xxxx-xxxx is a valid UUID and therefore is of the UUID type. Running that through a function which takes a UUID shouldn't cause a type error; however, whether that UUID refers to a valid session isn't a problem with the type itself
The type explains the shape of a value
Jonah
@jonahx
Feb 12 2017 23:05 UTC
@kwijibo, yeah, at least i was thinking about it. here are the two approaches i’ve been struggling with, keeping in mind i don’t want any duplicated logic. approach 1. all “validation” logic in type constructor. for use on frontend, wrap the types to catch errors and convert them into error messages. approach 2. validation logic seprate. in type constructor, delegate to validation logic. also use separated out validation logic on front end. but with this approach, either your validation gets run twice, or you have a type ctor that doesn’t guarantee validated data (ie, you assume the data has already been validated).
Keith Alexander
@kwijibo
Feb 12 2017 23:05 UTC
( @rpominov has a nice explanation of the difference between expected and unexpected failures here https://github.com/rpominov/fun-task/blob/master/docs/exceptions.md#trycatch)
Jonah
@jonahx
Feb 12 2017 23:07 UTC
@alex-deas, i would disagree that even numbers aren’t a type. EvenNumber is absolutely a valid type. what essential difference do you see between a number matching the UUID format and number passing x % 2 == 0?
Keith Alexander
@kwijibo
Feb 12 2017 23:10 UTC
@jonahx if you did the second, and used eg sanctuary-def, the validation only gets run once in production
and arguably it should get run twice in development because it's performing two different duties
once to validate the user's input, and once to check whether you correctly validated the input before passing it to your constructor
Jonah
@jonahx
Feb 12 2017 23:11 UTC
@kwijibo ok, i think i am misunderstanding something there then. if santuary is def is “off”, how do i use it to do say my frontend validation in prod?
Keith Alexander
@kwijibo
Feb 12 2017 23:12 UTC
you don't - you use your validation function
Jonah
@jonahx
Feb 12 2017 23:13 UTC
@kwijibo but the validation function is just, is this a valid type X?
Keith Alexander
@kwijibo
Feb 12 2017 23:13 UTC
and you use that validation function to create your types
Jonah
@jonahx
Feb 12 2017 23:13 UTC
@kwijibo ah, ok
@kwijibo so you are saying i need to take the 2nd approach….
Keith Alexander
@kwijibo
Feb 12 2017 23:14 UTC
I wouldn't presume to tell you what you need to do :)
but I personally like the 2nd
Jonah
@jonahx
Feb 12 2017 23:14 UTC
@kwijibo keith, keith, ok…. put the gun down. i will separate out the validation function :P
Keith Alexander
@kwijibo
Feb 12 2017 23:16 UTC
if you're handling user input with "expected errors", you can use, eg, Either like @alex-deas suggested
Jonah
@jonahx
Feb 12 2017 23:16 UTC
@kwijibo that also makes problems like “collecting all the errors at once” easier to solve, because presumably in type ctor’s you fail fast and raise only the first error
Keith Alexander
@kwijibo
Feb 12 2017 23:16 UTC
and then when you're creating your types, you can throw the errors and fail fast
Jonah
@jonahx
Feb 12 2017 23:16 UTC
@kwijibo yeah, i was planning to do that either way (pun intended)
@kwijibo @alex-deas thanks both for the discussion
what are your thoughts on using union-types to handle the validation result? Basically an either which I can name more apprpriately, like ValidationResult = Type({Range: ..., RangeError: ….});