A Look at Library Code

Post Reply
kim
Posts: 183
Joined: Fri Jun 22, 2018 10:35 pm
Has thanked: 0
Been thanked: 2 times
Contact:

A Look at Library Code

Post by kim » Sat Jan 26, 2019 12:28 pm

Have you ever really looked at some of the library code we use every day (or used to)?Let's look at underscore's "sample" method, which produces some amazing surprises if you poke at it a little.

The docs say:

Code: Select all

 _.sample(list, [n]) 
Produce a random sample from the list. Pass a number to return n random elements from the list. Otherwise a single random item will be returned.Easy enough?First up: What is the time complexity of this operation?

I can't fit the answer in this post because there are too many cases.

e.g.,

Code: Select all

_.sample(a, 1)
is slower than

Code: Select all

_.sample(a)
The function is never time-efficient (a 1,000-element array is entirely shuffled to pluck two values).
Next: Allocations?Again, depends on how it's called. For

Code: Select all

n != undefined
, the array is always copied in its entirety (!!!) even if only sampling a small # of elements (isn't this the intended use case?)
What are the error conditions?

Code: Select all

_.sample({ height: "10", width: "10", length: "10" }) is "10"

Code: Select all

_.sample({ height: 10, width: 10, length: 10 })
is undefined

Code: Select all

_.sample({ height: 10, width: 10, length: 10 }, 1)
throws "sample.slice is not a function"

More edge cases?

Code: Select all

_.sample(2, [3, 4]) returns []
, but

Code: Select all

_.sample(2)
returns undefined

Code: Select all

[1, 2, 3].map(_.sample.bind(_, [3, 4, 5])
returns something like

Code: Select all

[[5], 3, 4]
- have fun explaining that one.I bet this works with strings, right? Strings are list-ish?

Code: Select all

_.sample("abcdef")
returns a random character, as expected

Code: Select all

_.sample("abcdef", 3)
is *always* "abc"

Code: Select all

_.sample("abc", "abc")
is ""

So this function doesn't even always return an array, even for 'n' > 1

Underscore was built for size, not speed/predictability, but it's still *incredibly* hard just to describe the behavior of this function. I only tweet about TypeScript, though, so what's the connection?DefinitelyTyped's job is, in part, to:
1) Figure out all the rules the docs don't explain
2) Get people to agree on which of those behaviors constitute "errors"
3) Explain that behavior to a computer using a general-purpose DSL

So sometimes I hear "Type definitions are too complex" and really my response is that it isn't the definitions that are too complex, it's the library behavior. The *true* documentation for "sample" alone would span several written pages.
If you come across some code that is heavily indirected and just says

Code: Select all

_.sample(obj, arg)
you can literally say nothing about the return value without knowing a *lot* about both arguments. Isn't that... bad? Principle of Least Astonishment?Complexity, huge amounts of it, is routinely hidden behind badly-documented library-specific coercion algorithms for the sake of presenting what appears to be a "smaller" API surface area.

In a reasonable world, 'sample' throws on non-array inputs, does the right thing on strings instead of nonsense, and you call sampleKeys or sampleValues to sample from objects.This isn't me dynamic-shaming you, it's that a function should do one predictable job, not guesstimate your intent. You can write dynamic code without having five layers of "Does X if Y is Z, otherwise does Q to M when A is present"
My hope is that long-term, people write functions whose behavior is *easy* to describe, both to a human and to a machine.



john
Posts: 116
Joined: Wed Jun 13, 2018 9:36 am
Has thanked: 0
Been thanked: 1 time
Contact:

Re: A Look at Library Code

Post by john » Mon Jan 28, 2019 7:41 am

thanks for the blog.this exactly what i was looking for .

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest