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

Cleaner stack traces #64

Open
jwoudenberg opened this issue May 6, 2016 · 8 comments
Open

Cleaner stack traces #64

jwoudenberg opened this issue May 6, 2016 · 8 comments

Comments

@jwoudenberg
Copy link

I was wondering what people think of using a lib like stack-utils to clean up the stack trace to ensure the first line of the stack corresponds to the function call that causes the TypeError.

@davidchambers
Copy link
Member

Could you provide an example of some code containing a type error, the current stack trace, and the stack trace we'd see instead were we to use stack-utils?

@jwoudenberg
Copy link
Author

jwoudenberg commented May 6, 2016

Sure! Take the following code with improperly uses Sanctuary's maybe:

const { Just } = require('sanctuary')
Just(4).chain(x => x)

It results in the following error:

/home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary-def/index.js:1268
              throw invalidValue(name,
              ^

TypeError: Invalid value

Maybe#chain :: Maybe a -> Function -> Maybe b
                                      ^^^^^^^
                                         1

1)  4 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber

The value at position 1 is not a member of ‘Maybe b’.

    at invalidValue (/home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary-def/index.js:988:12)
    at /home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary-def/index.js:1268:21
    at /home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary-def/index.js:571:20
    at apply (/home/jasper/tmp/experiments/sanctuary/node_modules/ramda/dist/ramda.js:1184:19)
    at Object.f2 [as apply] (/home/jasper/tmp/experiments/sanctuary/node_modules/ramda/dist/ramda.js:473:22)
    at Maybe.<anonymous> (/home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary/index.js:353:38)
    at Maybe.<anonymous> (/home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary-def/index.js:1264:34)
    at Maybe.chain (/home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary-def/index.js:567:20)
    at Object.<anonymous> (/home/jasper/tmp/experiments/sanctuary/index.js:2:9)
    at Module._compile (module.js:541:32)

stack-utils is pretty dumb, it just filters out some lines of the stack trace based on patterns you provide yourself. Suppose we filter out everything with node_modules/sanctuary/, node_modules/sanctuary-def/ and `node_modules/ramda/, then we're left with:

/home/jasper/tmp/experiments/sanctuary/node_modules/sanctuary-def/index.js:1268
              throw invalidValue(name,
              ^

TypeError: Invalid value

Maybe#chain :: Maybe a -> Function -> Maybe b
                                      ^^^^^^^
                                         1

1)  4 :: Number, FiniteNumber, NonZeroFiniteNumber, Integer, ValidNumber

The value at position 1 is not a member of ‘Maybe b’.

    at Object.<anonymous> (/home/jasper/tmp/experiments/sanctuary/index.js:2:9)
    at Module._compile (module.js:541:32)

Which is arguably is what the end user of the Sanctuary library is really interested in.

For this to work not just with Sanctuary, but any library using sanctuary-def, the consumer of sanctuary-def should be able to provide the patterns sanctuary-def uses to clean up the stack traces.

@davidchambers
Copy link
Member

Thanks for the instructive example. I agree that the filtered stack trace is much nicer. :)

For this to work not just with Sanctuary, but any library using sanctuary-def, the consumer of sanctuary-def should be able to provide the patterns sanctuary-def uses to clean up the stack traces.

Is that so? In the above example, at least, one could drop lines up to and including the last occurrence of /sanctuary-def/ and arrive at the same result.

My concern is that this approach makes assumptions about file locations. In the browser it's common to concatenate source files, precluding us from using file locations to determine where user code ends and sanctuary-def code begins.

@jwoudenberg
Copy link
Author

Is that so? In the above example, at least, one could drop lines up to and including the last occurrence of /sanctuary-def/ and arrive at the same result.

You're right, that could make things easier.

My concern is that this approach makes assumptions about file locations. In the browser it's common to concatenate source files, precluding us from using file locations to determine where user code ends and sanctuary-def code begins.

stack-utils is pulled out of the test runner tape, so definitely not imagined for browser use. It might also be possible to hook in on the first part of the trace by somehow routing the type checks through a common entry point into sanctuary-def. That entry point would have to be named function with a pretty unique name so we can zoom in on it in the stack trace.

@jwoudenberg
Copy link
Author

Another strategy would be to immediately create and store a stack the moment a def-returned function is called (var stack = (new Error()).stack). Pop the top entry of that stack and you have the one from my example. The disadvantage to this approach is that you have to hang on to this stack until perhaps eventually an error is thrown. That might be unacceptable from a performance point of view.

@davidchambers
Copy link
Member

new Error().stack sounds like a good approach to me!

@jwoudenberg
Copy link
Author

Cool! I'll try to find some time next week to try it out.

@davidchambers
Copy link
Member

Excellent. :)

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

2 participants