lbrandy.com2023-03-25T19:07:03+00:00http://lbrandy.comLouis Brandylouis@lbrandy.comChatGPT goes to a tech interview2023-03-24T00:00:00+00:00http://lbrandy.com/blog/2023/03/chatgpt-goes-to-a-tech-interview<h1 id="ok-another-interview-phone-screen-lets-do-this">ok, another interview phone-screen. let’s do this.</h1>
<p><em>Hey ChatGPT, welcome to our interview. Today we’re going to do a bit of coding. I’ll leave time at the end for questions from you to me. Are you ready? Great. Can you write me code to validate whether a list of binary tree nodes forms exactly one valid binary tree?</em></p>
<blockquote>
<p>Sure, here’s an example code in Python to validate if a list of binary tree nodes forms exactly one valid tree:</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class TreeNode:
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def validate_tree(nodes):
# Creating a dictionary with key as the node value and value as the TreeNode object
node_dict = {node['val']: TreeNode(node['val']) for node in nodes}
# Iterating through the list of nodes to create the tree structure using parent-child relationships
for node in nodes:
# Getting the node object
parent_node = node_dict[node['val']]
# Getting the left child node object
if node['left'] is not None:
left_child = node_dict[node['left']]
parent_node.left = left_child
# Getting the right child node object
if node['right'] is not None:
right_child = node_dict[node['right']]
parent_node.right = right_child
# Counting the number of root nodes
roots = 0
for node in node_dict.values():
if not node.left and not node.right:
roots += 1
elif (node.left and not node.right) or (not node.left and node.right):
return False
# Validating that only one root node is present in the tree
return roots == 1
</code></pre></div></div>
<blockquote>
<p>The validate_tree function takes a list of nodes, each node represented as a dictionary with the keys val , left , and right , where val is the node value and left and right are the values of the left and right child nodes respectively.</p>
</blockquote>
<blockquote>
<p>The function first creates a dictionary with the node values as the keys and the corresponding TreeNode objects as the values. It then iterates through the input list of nodes to create the tree structure using parent-child relationships.</p>
</blockquote>
<blockquote>
<p>After the tree structure is created, the function counts the number of root nodes present in the tree. A node is considered to be a root node if it has no children. If a node has only one child or has two children, it cannot be a root node. Finally, the function validates whether there is only one root node present in the tree, and returns True if it is valid, and False otherwise.</p>
</blockquote>
<blockquote>
<p>Note that this function assumes that the input list of nodes represents a valid binary tree, i.e., there are no loops in the tree structure and each node has at most two children.</p>
</blockquote>
<h1 id="follow-up-question">follow-up question!</h1>
<p><em>(okokokokok, I’m a smart human, so let’s ask a followup question to catch any cheaters!) So, then, ChatGPT, can you write me a unit test that tests the code you just wrote?</em></p>
<blockquote>
<p>Sure, here’s an example unit test in Python using the built-in unittest module:</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import unittest
class TestValidateTree(unittest.TestCase):
def test_valid_tree(self):
# Valid tree with 1 as root and 2 and 3 as children
nodes = [{'val': 1, 'left': 2, 'right': 3}, {'val': 2, 'left': None, 'right': None}, {'val': 3, 'left': None, 'right': None}]
self.assertTrue(validate_tree(nodes))
# Valid tree with 5 as root and 2 and 7 as children
nodes = [{'val': 5, 'left': 2, 'right': 7}, {'val': 2, 'left': None, 'right': None}, {'val': 7, 'left': None, 'right': None}]
self.assertTrue(validate_tree(nodes))
def test_invalid_tree(self):
# Invalid tree with two root nodes
nodes = [{'val': 1, 'left': None, 'right': None}, {'val': 2, 'left': None, 'right': None}, {'val': 3, 'left': None, 'right': None}]
self.assertFalse(validate_tree(nodes))
# Invalid tree with a node having two children, but no parent
nodes = [{'val': 1, 'left': None, 'right': 2}, {'val': 3, 'left': None, 'right': None}, {'val': 2, 'left': 4, 'right': 5},
{'val': 4, 'left': None, 'right': None}, {'val': 5, 'left': None, 'right': None}]
self.assertFalse(validate_tree(nodes))
# Invalid tree with a node having only one child, but no parent
nodes = [{'val': 1, 'left': None, 'right': 2}, {'val': 3, 'left': None, 'right': None}, {'val': 2, 'left': None, 'right': None},
{'val': 4, 'left': None, 'right': None}]
self.assertFalse(validate_tree(nodes))
if __name__ == '__main__':
unittest.main()
</code></pre></div></div>
<blockquote>
<p>The validate_tree function takes a list of nodes, each node represented as a dictionary with the keys val , left , and right , where val is the node value and left and right are the values of the left and right child nodes respectively.</p>
</blockquote>
<blockquote>
<p>The function first creates a dictionary with the node values as the keys and the corresponding TreeNode objects as the values. It then iterates through the input list of nodes to create the tree structure using parent-child relationships.</p>
</blockquote>
<blockquote>
<p>After the tree structure is created, the function counts the number of root nodes present in the tree. A node is considered to be a root node if it has no children. If a node has only one child or has two children, it cannot be a root node. Finally, the function validates whether there is only one root node present in the tree, and returns True if it is valid, and False otherwise.</p>
</blockquote>
<blockquote>
<p>Note that this function assumes that the input list of nodes represents a valid binary tree, i.e., there are no loops in the tree structure and each node has at most two children.</p>
</blockquote>
<h1 id="whoa-editors-comment">whoa (editor’s comment)</h1>
<p>So, the above was presented without (much) comment. It represented a very simple question/response from me to chatgpt (using <a href="http://chat-gpt.com">chat-gpt.com</a>). The intention is to show what the very near future of a conventional “tech interview” might look like in our collectively brave new world.</p>
<p>The two questions asked to ChatGPT were:</p>
<ol>
<li>Can you write me code to validate whether a list of binary tree nodes forms exactly one valid tree?</li>
<li>Can you write a unit test to test the code you just wrote?</li>
</ol>
<p>Actual pastebin of the interchange:</p>
<ul>
<li><a href="http://pastebin.com/tYbuaJhE">http://pastebin.com/tYbuaJhE</a></li>
<li><a href="http://pastebin.com/dLv3ecjn">http://pastebin.com/dLv3ecjn</a></li>
</ul>
<h1 id="some-commentary">some commentary</h1>
<p>First, if you look closely at the code, or the test, it’s not <em>great</em>, or ideal… but holy cow does it <em>look</em> legit at a glance. I think careful inspection of the code will reveal problems and edge cases not properly handled, but… still… wow.</p>
<p>Second, I’m genuinely alarmed by the seemingly plausible follow-up answer. It just looks shockingly good before I examine the details. In my own mind I somewhat expected ChatGPT to regurgitate a half-decent answer to a conventional “coding” question. I did <em>not</em> expect it to produce a half-sensible answer to followup questions.</p>
<p>Last, one of my long-held beliefs is that human interviewers for these kind of “coding” interviews <em>vastly</em> overrate their ability to detect “preparedness”, “cache hits” or, worse, cheaters. My guess is that many, many, many interviewers are fooled by people actively cheating or who have actively prepared an answer to their “known” leetcode question. Many interviewers, IMO mistakenly, believe that they can suss out such things with their clever followups. I am not convinced. There’s a much longer discussion to be had here about what this implies when we consider the value of coding interviews, overall.</p>
Haxl project at Facebook2015-02-26T00:00:00+00:00http://lbrandy.com/blog/2015/02/haxl<p>One of the things I’ve been working on the last few years, when they pry me away from my C++, is the Haxl project at facebook. Here’s some links…</p>
<h2 id="haxls-icfp-paper">Haxl’s ICFP paper</h2>
<p><a href="http://dl.acm.org/citation.cfm?id=2628144">There is no Fork: an Abstraction for Efficient,
Concurrent, and Concise Data Access</a></p>
<h2 id="securityscale-2014-talk-by-me">Security@Scale 2014 talk (by me)</h2>
<p>Here is a talk I gave on this topic. This talk is very light on the Haskell/language details and heavier on the motivation and how it fits into practice at Facebook.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/UMbc6iyH-xQ" frameborder="0" allowfullscreen=""></iframe>
<h2 id="icfp-2014-haxl-talk-by-simon-marlow">ICFP 2014 Haxl talk (by Simon Marlow)</h2>
<p>If you want a more detailed talk on the language bits, here’s Simon Marlow’s talk at ICFP:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/jG9PWdV1wso" frameborder="0" allowfullscreen=""></iframe>
The redditor's delusion2013-04-25T00:00:00+00:00http://lbrandy.com/blog/2013/04/redditors-delusion<p>I know that neither of these phenomena originate in these communities. I just frequent both of them, and see them almost every day.</p>
<h2 id="the-redditors-delusion">The redditor’s delusion</h2>
<p>The mistaken belief that the predominant opinion of your favorite website’s community is somehow a representative sample of some larger population. This is especially true if your favorite website’s community reinforces, and enforces, the dominant opinion with upvote and downvote arrows.</p>
<h2 id="the-hacker-newsians-delusion">The Hacker Newsian’s delusion</h2>
<p>The mistaken belief that you are very special, a thought leader, and therefore anything you think, or you notice, is definitely a trend or is otherwise representative of some larger population. An extreme version of the redditor’s delusion. You should probably post your opinion somewhere prominent, without qualification, nuance, or perspective. Perhaps in the form of an assertion, if not proclamation. This <em>is</em> what is happening. You’ve noticed it.</p>
C++11 variadic templates, for the doubters.2013-03-23T00:00:00+00:00http://lbrandy.com/blog/2013/03/variadic_templates<p>One of the simple pleasures of working at facebook is that, because we fully control the deployment environments, I’ve been using gcc 4.7 for awhile. That means we get to seriously embrace C++11.</p>
<p>A variadic template, for those that don’t know, is a C++ template that takes an arbitrary numbers of types. If you are an experienced C++ programmer, this might seem useful, or intriguing. This post probably isn’t for you. If this terrifies you, read on.</p>
<p>Another simple pleasure is that I get to work with many non-C++ programmers. Some never use it, some occasionally. Almost all have opinions, though. Something about variadic templates causes anti-C++ sentiments, amongst journeymen and apostates alike, to crystallize.</p>
<p>My goal here is simply to motivate and give a quick tour of places where variadic templates are extremely useful, both in our codebase, and in the standard itself. For the doubters. Some of the patterns here are almost certain to show up in any non-trivial codebase. Some are already standardized, and I’ll hit on those as we go.</p>
<h2 id="variadic-templated-objects">Variadic Templated Objects</h2>
<p>It’s pretty easy to come up with a use for an object that accepts a list of types. The standard provides <code class="language-plaintext highlighter-rouge">std::tuple</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>std::tuple<int, float, string> bob = {1, 12.0, "hello"};
</code></pre></div></div>
<p>You can also imagine implementing the algebraic cousin to the typesafe tuple, the typesafe union, in a similar fashion. Check out: <a href="https://github.com/facebook/folly/blob/master/folly/DiscriminatedPtr.h"><code class="language-plaintext highlighter-rouge">folly::DiscriminatedPtr</code></a> which is a very space-optimized, typesafe union of pointers. (An astute reader would point out that <code class="language-plaintext highlighter-rouge">boost::variant</code> fits into this class of objects as well. It’s conceivable that you could implement <code class="language-plaintext highlighter-rouge">boost::variant</code> as a variadic template, but there are, in my understanding, a bunch of implementation details that make this tricky. I’ll dodge the issue entirely. This post is already going to be long.)</p>
<p>While easily motivated, example implementations here are long, if not a bit complex, revolving mostly around compile-time recursion. If you are interested, check out <a href="http://www.youtube.com/watch?v=_zgq6_zFNGY">this talk by Andrei Alexandrescu</a> where he goes through variadic templates, and <code class="language-plaintext highlighter-rouge">std::tuple</code> in detail.</p>
<h2 id="variadic-functions-now-with-safety">Variadic Functions, now with safety</h2>
<p>The other place where variadic templates can be used is to implement typesafe variadic functions. There aren’t many variadic functions around, and they are often actively discouraged. The most famous, though is <code class="language-plaintext highlighter-rouge">printf</code> (and all of his associated friends). Variadic templates gives us the opportunity to create typesafe <code class="language-plaintext highlighter-rouge">printf</code>. That is to say a version of printf that ensures the parameters to the function match the types specified in the format specifier. Again, I’m going to skate on by, but if you want details, see the talk above.</p>
<p>Here’s another example stolen from <code class="language-plaintext highlighter-rouge">facebook::folly</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>namespace folly {
template <typename T, typename... Ts>
size_t hash_combine(const T& t, const Ts&... ts) {
size_t seed = std::hash<T>()(t);
if (sizeof...(ts) == 0) {
return seed;
}
size_t remainder = hash_combine(ts...); // not recursion!
return hash_128_to_64(seed, remainder);
}
} //namespace
// Simple example
struct Obj {
int x;
string y;
float z;
size_t hash() const { return folly::hash_combine(x,y,z); }
};
</code></pre></div></div>
<p>Note that <code class="language-plaintext highlighter-rouge">folly::hash_combine</code> is completely typesafe. It will only hash types that have a <code class="language-plaintext highlighter-rouge">std::hash</code> specialization, and if you try to pass a type that doesn’t have such a specialization, it will fail to compile.</p>
<h2 id="the-other-great-hidden-variadic-function">The other great, hidden, variadic function</h2>
<p>There is another widespread variadic function that is quite a bit more subtle. I would go as far as saying its existence has been haunting C++ for a long time. It’s only now, with variadic templates, that we are able to fully come to terms with it. The greatest variadic function of them all is the lowly constructor.</p>
<p>Given an arbitrary type <code class="language-plaintext highlighter-rouge">T</code>, the constructor is a “variadic function”. The copy constructor is well defined for T, but not the constructor. This has been a plague on templates since their introduction. Templates, unable to deal with constructors that take an unknown number of parameters of unknown types, have implemented various workarounds often resulting in reduced efficiency. Put another way, <strong>templates can happily copy objects on your behalf, but they could never easily create objects on your behalf.</strong></p>
<h3 id="containert"><code class="language-plaintext highlighter-rouge">Container<T></code></h3>
<p>Containers, arguably the preeminent use of templates, have suffered from this issue for a long time. <strong>Containers often want to create objects HOW you want, but WHERE they want.</strong> Container templates have worked around this problem in two ways 1) use the default constructor and then give you access to modify it, or 2) let you construct the object, and then copy it to the correct location.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vector<Obj> o;
Obj obj(param1, param2); // I construct it the way I want...
o.push_back(obj); // ... and you copy it into the place you want it
map<int, Obj> map;
map[1] = Obj(param1, param2); // DEFAULT construct, then operator=
</code></pre></div></div>
<p>What you’d like to be able to do is give the container the parameters it needs and have it construct the object where it wants, but the way you want it…</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vector<Obj> o;
o.emplace_back(param1, param2); // doesn't take Obj, takes parameters to MAKE an Obj
</code></pre></div></div>
<p>This problem is now largely resolved by the introduction of the <code class="language-plaintext highlighter-rouge">emplace</code> family of container methods. <code class="language-plaintext highlighter-rouge">Emplace</code> is a variadic function that simply forwards any parameters you pass it to the constructor of the correct type. This allows you to give a container everything it needs to construct your object, and it will do so using the construction you want, but putting the object where it wants.</p>
<p>No copies, no moves.</p>
<h2 id="creating-objects-on-your-behalf">Creating objects on your behalf</h2>
<p>Any template that creates objects on your behalf is likely to benefit from variadic templates due to the fact that these functions can now be parameterized to call any constructor on that object. This will be all but required for containers. But it is also true for any attempt to generalize logic around object construction. It’s actually pretty easy to come up with situations where wrapping construction in some logic makes code better. The standard gives us one:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>std::shared_ptr<Obj> sharedObj = std::make_shared<Obj>(param1, param2);
auto uniqObj = make_unique<Obj>(param1, params2); // not std yet :(
</code></pre></div></div>
<p>There is no <code class="language-plaintext highlighter-rouge">std::make_unique</code>… yet. But it’s easy enough to implement using a variadic template.</p>
<p>Another idea (whether or not it is a -good- idea is probably arguable): a simple caching framework. Given a certain implemented interface for a type, it’s construction/caching can be abstracted away. If the object exists in some cache (in memory, memcache, whatever), construct by deserializing from the cache, otherwise do the specified (presumably expensive) constructor and set the resulting serialized value into the cache for next time.</p>
<h3 id="an-arena-allocator">An Arena Allocator</h3>
<p><a href="https://github.com/facebook/folly/blob/master/folly/Arena.h"><code class="language-plaintext highlighter-rouge">folly::Arena</code></a> is an arena allocator. Arena allocators can be summarized with a simple line of code:</p>
<pre>
void deallocate(void* p) {
// Deallocate? Never!
}
</pre>
<p>An arena simply allocates memory in a never-ending forward fashion. It ignores all attempts to deallocate until it’s destroyed, when all of its memory is freed at once. Arenas are extremely useful for servers and other per-request models because you can often avoid almost all memory overhead during the latency-inducing formation of the response, and then free up all memory, easily and quickly, once the response is gone.</p>
<p>How would you construct an arbitrary object inside this arena? Make <code class="language-plaintext highlighter-rouge">arenaNew</code>? Before C++11 this would be ugly. Now, it’s relatively easy.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template <typename T, typename ...Args>
T* arenaNew(folly::SysArena* arena, Args&&... params) {
auto mem = arena->allocate(sizeof(T));
return new (mem) T(std::forward<Args>(params)...);
}
void f() {
folly::SysArena arena;
auto ptr = arenaNew<Obj>(&arena, param1, param2);
// ...
}
</code></pre></div></div>
<p>Warning: this code is a bit incomplete, because it’s only “safe” for types with trivial destructors (and woe be to the unwitting programmer who <code class="language-plaintext highlighter-rouge">delete</code>s one of these pointers).</p>
<p>The syntax is new, and perhaps strange, but the concept is pretty simple. We pass in the arena we want to use, and the params to use on the constructor of <code class="language-plaintext highlighter-rouge">T</code>. We allocate the memory we need from the arena, and then construct T using placement new, forwarding the parameters into the constructor.</p>
<h3 id="tldr">tldr</h3>
<p>Variadic templates fill a surprisingly frequent need in generic C++ code. Some places where variadic templates are likely to be useful:</p>
<ul>
<li>Specialized tuples and unions. Functions that operator on them generically.</li>
<li>Typesafe variadic functions</li>
<li>Templates that want to construct objects on your behalf (including containers)</li>
</ul>
C++11's six dots2013-02-18T00:00:00+00:00http://lbrandy.com/blog/2013/02/c11s-six-dots<p>I was mucking around the other day and discovered this nice little oddity:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>template <typename... Args>
void foo(Args......);
</code></pre></div></div>
<p>As it turns out, <code class="language-plaintext highlighter-rouge">......</code> can be totally valid C++11. This is what happens when backward compatibility mixes with new hotness.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// These are all equivalent.
template <typename... Args> void foo1(Args......);
template <typename... Args> void foo2(Args... ...);
template <typename... Args> void foo3(Args..., ...);
</code></pre></div></div>
<p>Hopefully the last one shows what is happening here. This is two ellipsis operators, one for varadiac templates, and the second for old C-style varargs. Needless to say, I really hope you never need to use this, but if you do, I really hope you’ve carefully exhausted all other options.</p>
<p>I really wanted to end this post with an example of something at least believably useful, but that was almost impossible.</p>
i'm back!2012-05-05T00:00:00+00:00http://lbrandy.com/blog/2012/05/im-back<p>Ok 1year+ off, and it’s time to get back into it. Once you break the habit it’s hard to restart. A big reason I haven’t restarted writing is because wordpress just depresses me. This place has fallen into total disarray. Spam comments. I’m running an old version, meh. Anyway…
Step #1. Kill wordpress. Tomorrow I’m going to have a personal hackathon to write a wordpress->something converter. Something is almost certainly a static blog generator. I’m thinking ruhoh.</p>
<p>Step #2. Upload all my old svn repos to github because why the hell not. I have the code for my sc2 replay aggregator site, the code for that time I hacked (in the good way) the allegheny county tax system, and the project I did to get an interview at reddit.</p>
<p>Step #3. Profit and stuff.</p>
<p>The good news is I have tons of stuff to possibly write about, and a bunch of drafts that I can polish, so yay content.</p>
I'm buying facebook at $50b2011-01-30T00:00:00+00:00http://lbrandy.com/blog/2011/01/im-buying-facebook-at-50b<p>I’ve been silent for a few weeks (months, gah) now, but with good reason. I’ve spent the better part of the last few weeks interviewing all over SF and silicon valley. I’ll just skip to the end: I’ve accepted a job at Facebook and my wife and I will be relocating to silicon valley in the next few weeks.</p>
<p>So up first, my current job. As you may or may not know, I work at a startup face recognition company in Pittsburgh. Our decision to relocate was largely for personal reasons, and choosing to leave my job wasn’t easy. It’s an awesome place to work. If you are interested, we are still hiring.</p>
<h3>Interviewing</h3>
<p>The job market in California seems quite good for software engineers right now. If you are thinking about heading west, I would wager a guess that now is a great time to do it. The whole process was extremely quick (once I was in contact with a recruiter). I ended up with a handful of offers from several really awesome places (names ranging from the biggest, to awesome little places you’ve only maybe heard of). Having spent quite a bit of time the last 5 years being the interviewer, it was quite enlightening to be on the other side of it, for once. To be sure, there’s another blog post hiding behind that last sentence.</p>
<p>Though the vast majority of my interviews ended up with offers, it wasn’t all good news. I actually got phone-screened by one startup company. I have tons of opinions about the quality of my interviewers throughout this process (and it’s not all good, by any means), but I can’t blame that phone screener. I bumblefucked my way through a fairly easy question (I was unprepared, more on that in a bit) and knew I was blowing it. My wife tried to make me feel better after the call but I told her, having done a ton phone screens myself, that was a definite ‘no’. In fact, if he wanted to interview me after that phone screen, I’d have questioned his whole interview philosophy to begin with! There’s probably another blog post hiding there, too.</p>
<p>In the end, the blown phone screen was a nice wake-up call that I needed to do some more review. I did, and the rest of my interviews went extremely well. Had that phone screen been later in the process, I’m certain it would have turned out much better for me, but c’est la vie.</p>
<h3>Facebook</h3>
<p>In the end, the decision to choose Facebook was fairly difficult (and somewhat personal) because I had such a fantastic group of offers. All of them had huge upsides, and the thought of saying “no” to any one elicited this “are you crazy?” feeling in me. The reasons Facebook stood out, for me, was because 1) they have tons of the types of problems I want to learn about, and solve, 2) they are one of the few places in the world where you can learn about things at that scale, and 3) over the next few years, Facebook is almost certainly going to be a fascinating place to be.</p>
<p>This post was mainly an update to anyone who frequently reads. In the next few posts, I’ll go back to the more general style, probably talking a bit more about the interview and the interviewing process. I feel like I could write a book, at this point.</p>