Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: R.throw #1317

Closed
reggi opened this issue Jul 29, 2015 · 7 comments
Closed

Feature request: R.throw #1317

reggi opened this issue Jul 29, 2015 · 7 comments

Comments

@reggi
Copy link

reggi commented Jul 29, 2015

Here's what the code looks like, I used a library called argx to allow for a dynamic range of argument options.

var argx = require("argx")
R.throw = function(){
  var args = argx(arguments)
  var fn = args.shift(Function)
  var obj = args.shift(Object)
  var str = args.shift(String)
  if(fn && str) throw new fn(string)
  if(obj) throw obj
  if(str) throw new Error(str)
  throw new Error()
}
// R.throw(new Error("an error"))
// R.throw(Error, "an error")
// R.throw("an error")
// R.throw()

The practical implementation of this is if you want to throw a specific message out of a promise .catch, for instance.

myPromise().catch(R.throw("bad apple"))

Thoughts?

@jethrolarson
Copy link
Contributor

I don't think that this is very idiomatic. Ramda's functions all return something and ideally never raise exceptions. Where a process may fail the recommendation is to use Maybe (either from ramda-fantasy or other libs) to capture that possibility in a composition safe way

@davidchambers
Copy link
Member

A function which raises an exception by design is out of the question for Ramda, as far as I'm concerned. Using exceptions for control flow is an imperative practice Ramda should not encourage.

A function which returns an Error object, on the other hand, is something the library could include.

@reggi
Copy link
Author

reggi commented Jul 29, 2015

@jethrolarson Thanks for your reply. I was just under the impression that Ramda and functional programming over all was that it made functions easier to write and move around.

I find it problematic that Ramda does not express any interest dealing with errors. Even in the example of ifElse does nothing if the function is not an array.

var flattenArrays = R.map(R.ifElse(Array.isArray, R.flatten, R.identity));

Which should demonstrate something like this.

var flattenArrays = R.map(R.ifElse(Array.isArray, R.flatten, function(){
  throw new Error("not an array")
}))

or with R.throw

var flattenArrays = R.map(R.ifElse(Array.isArray, R.flatten, R.throw("not an array")))

@davidchambers I'm not sure what good a function that returns an error object is. That would be the equivalent to R.always(new Error("not an array")) I'd still have to open up a function call to throw it.

@davidchambers
Copy link
Member

The trick is to stop thinking in terms of exceptions. Instead, think about data structures which encode the notion of success or failure.

Consider a head function which returns the first item of the given list. We could give it this type:

head :: [a] -> a

What do we do in the case of []? Our only option is to raise an exception, since we don't even know the type of the value the caller is expecting us to return.

We could instead adopt the JavaScript approach:

head :: [a] -> (a | Undefined)

This avoids an exception, but means every caller must now type check the return value. It would be all to easy to write something like toUpper(head(words)) and have it explode if words is [].

The solution is to encode the possibility of failure in the return type:

head :: [a] -> Maybe a

We can then provide functions which making working with a value of type Maybe a almost as convenient as working with a value of type a directly.

This contrived example shows how failures resulting from many different operations—JSON parsing, missing object properties, incorrectly typed values, and float parsing—can be encoded in a type to allow us to remove exception handling, null checks, type checks, and NaN checks.

@reggi
Copy link
Author

reggi commented Jul 29, 2015

@davidchambers Interesting. That may have entirely gone way over my head and may need several readings. When it does come to control flow, errors do come up especially as we've been talking about with promises.

I originally made a mistake. In both examples the ifElse() and the .catch() what I do need is a function that returns a function that throws an error. Just like @davidchambers said. Beacuse, duh, this with the way I wrote R.throw this R.always(R.throw("not an array")) just throws the error.

So what I'd need is this.

R.throw = function(){
  var args = argx(arguments)
  return function(){
    var fn = args.shift(Function)
    var obj = args.shift(Object)
    var str = args.shift(String)
    if(fn && str) throw new fn(string)
    if(obj) throw obj
    if(str) throw new Error(str)
    throw new Error()  
  }
}

@alexeygolev
Copy link

I agree with @davidchambers — throwing errors doesn't really help with writing "errorless" code... because this approach implies the existence of somebody actually fixing those errors. Using Maybe or Either (or Future for promises) implies that your code knows how to deal with a possible error or at least can safely ignore it.
However I do believe there is a need for errors and warnings on development stage (e.g. as a poor man's compiler checker).But sticking throw into functions composition would make it impossible to automatically eliminate on build stage.

@jenshedqvist
Copy link

I think Ramda tries to help you write better code by minimising that tge functions themself fail. There is this kind of monomorphic(?) approach where a function is super clear with what it expects and what it returns, often one or a couple of simple value in and out. That makes them reliable and makes it easy for you to find YOUR errors, "crap in is crap out" and you can easily guard for that as @davidchambers said before.

You should not feel limited by the API as well, just use compose your own control flow sauce around the Ramda, such as promises, catch, error handling etc. For me, Ramda is a library to write quality logic and code, it's not environment biased "making it easy for you to code in the browser". throw new Error is therefor definitely too environment specific.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants