1. 5

      AOL keyword: Duhem-Quine

      1. 1

        Thanks for this!

      1. 3

        What languages do you know now? Are there any you don’t know that you are particularly interested in learning?

        Any idea what kind of application you want to get started with? You could start building web backends, or CLI apps, or GUI apps. From the project list you wrote, it sounds like you’re more interested in CLI apps. Or at least things that don’t need to bother with high-level web and GUI interfaces. I don’t know what you’ve done so far, but small CLI helper apps are a decent place to start for learning new languages and frameworks.

        1. 2

          I know JS, python, some ruby. A long time ago I had a lot of experience writing C. I’m very interested in Rust, moderately interested in Go, and I’m also thinking about just diving back into C.

          Thanks for the ideas! Yeah perhaps a CLI would be a good start. I was thinking about writing something with a terminal dashboard. That just sounds like a lot of fun. Thats part of the reason why the htop clone seems like a great direction. Make some system calls to get information and just display it.

          1. 2

            Look into Elixir or Erlang if you’re interested in distributed systems!

        1. 8

          This is interesting, because it also comes down to how the reviewer reviews code. If the reviewer just looks at the changes in a PR as a whole, a lot of the value in careful commits becomes about the historical record than ease of reviewing. Still valuable, but there’s something to be said for learning to review code.

          1. 4

            That was one of my motivations to write this in the first place. Now I can just send a link to the ones on my team.

            I agree that how we write history is important on its own merits. You commit once, but it will be read a lot more. Both during review, but also as part of later debugging.

            1. 4

              It’s something I try and stick to even if it takes a bit of time. git add -p <filename> is invaluable for this too.

              When I see people reviewing things as a giant chunk of code, I feel that sometimes it comes from a history of commits not being very useful. When commits are more noise than signal, I don’t blame people for not looking at them.

              Another thing is familiarity with rebasing. I think there’s a lot of talk about rebasing being difficult, which means people don’t attempt it because they’re scared to lose work, which is entirely reasonable, but we need to talk more about recovery techniques like the reflog, etc.

              1. 3

                git add -p <filename> is invaluable for this too.

                Magit makes it painless.

                1. 2

                  The simplest being to make a test branch on which to test. If all goes well, you can reset you branch to the test branch’s commit and proceed from there.

                  I don’t know if people understand the lightness and ease of branches, but it’s simpler than cherry-picking from reflog.

                  Git is excellent in how hard it is to truly fsck up, and people should learn these techniques and not consider them magic tricks.

                2. 1

                  Your motivation resonates with me; I’ve been writing something partially for my coworkers about my thoughts on commits and reviews, plus explaining some of my workflows… Part one is here: https://medium.com/p/c0966a562b10

              1. 1

                What’s front end engineering? Where is the line between design and engineering?

                1. 1

                  I don’t design anything, and I also don’t work on backend systems*. If your job involves e.g. turning mockups and API calls into a feature in a browser, I’d say you’re doing frontend engineering.

                  * … for the most part

                  1. 1

                    oh right, so you’re talking the specific layer between a UI/UX/graphic design person who is not doing any coding, and the immediate next layer of a front end programmer who is not doing any design, but taking ready-for-web graphics and doing lots of front-end-framework, javascript, etc. stuff to connect to the first layer of business logic?

                1. 7

                  Erlang making this hard is the biggest reason I didn’t continue to learn Erlang. I don’t understand how the “just crash” philosophy and actually returning useful error messages are supposed to intersect. I have just assumed since Erlang was made for telecom infrastructure, where I assume it’s reasonable to drop bad input rather than return errors, it just doesn’t have an ergonomic way to return errors. At least based on my experience with switches and routers, which tend to drop bad packets with only a few exceptions.

                  I’d be happy to learn I’m wrong though.

                  On the flip side, Swift guard statements make this pattern delightfully easy. In particular, I like that you can do a guard let and the assignment will occur in the outer scope, as opposed to if let which makes the binding local to the body to the if.

                  For example:

                  guard let value = possiblyReturnsNil() else {
                      // handle error
                  doSomething(value) // valid

                  Notice the else after the guarded expression, that hints how the scoping works. So guard let isn’t quite the same as what you might expect unless let / if not let to be. Aces.

                  1. 5

                    The way you tackle these things in Erlang is by tagging the return values. If you really, really need to return early, you can always erlang:throw/1, which does pretty much what return does in javascript.

                    Admittedly, you can end up with multiply-indented case statements in Erlang, too, but there are ways of dealing with that (depending on your style) that don’t involve non-local returns.

                    1. 2

                      I’m familiar with return tagging. The nested case statements are what bother me. I appreciate the link but that strategy is so over the top and verbose that I absolutely don’t want to do anything like it for generalized input guarding. Throw might be what I want but it seems wrong, maybe because of how I’m used to using exceptions in other languages, maybe not. Like what if your caller doesn’t expect you to throw, and it isn’t your code? You’d have to wrap everything in a catching function. It feels like way more work than it should be just to have access to early returns.

                      1. 5

                        I use the following construct in my code:

                        -spec fold(t(A,B), [fun((A) -> t(A,B))]) -> t(A,B).
                        fold(R, []) when ?is_result(R) -> R;
                        fold({error,E}, _) -> {error, E};
                        fold({ok, V0}, [Fun|Rest]) -> fold(Fun(V0), Rest);
                        fold(Other, []) -> error({badarg, Other});
                        fold(_, Other) -> error({badarg, Other}).

                        If you squint, it’s an early-exit chain of >>= (monadic binds) on the squintly-typed Either type.

                        Then, you can use the following pattern:

                        squinty_either:fold({ok, InitVal}, 
                        [fun(V) -> {ok, x(V)} end,  %% x is total and pure
                        fun(V) -> 
                          case some_pred(V) of   %% case statement inlined, should be sep. fun.
                            true -> {ok, z(V)};
                            false -> {error, {V, not_valid}}
                        fun(V) -> ... ]

                        Any fun that returns {error, E} will cause the entire chain to exit with that tuple. OTOH, returns of {ok, V} will pass on the bare V to the next element in the chain. This means you only code for the relevant path. You can then deconstruct the values in function heads to further reduce case-yness.

                        Regarding the ‘unknown code crashing your process’ problem: yes, I hear you. There is no foolproof solution to this: sometimes you do have to explicitly try-catch, sometimes your running process doesn’t care and can just crash, because the supervision tree is built in such a way that it doesn’t matter.

                        1. 2

                          I like this pattern overall, but I still wish it was more ergonomic. I believe the |> operator in Elixir exists to essentially do this?

                          1. 2

                            Yeah, ergonomics isn’t Erlang’s strong suit. I does help if all your functions have a uniform return type, so you can get by with just referencing them:

                            fold({ok, N}, [ fun add_one/1, fun add_two/1, fun reject_odd_numbers/1 ]).

                            where all the above functions are :: number() -> {ok, number() | {error, any()}

                            In Elixir, the |> operator solves half of the issue (chaining), and the with syntax solves the other part of the issue (logic/dispatch based on previous return value). Yet, there is no succinct way of combining them, i.e. implementing something like Haskell’s >>=.

                            *edited example function name

                            1. 2

                              The fold strategy reminds me of pipeline from Joyent’s vasync library. Node has a callback-based runtime that totally eradicates anything resembling a call stack whenever you have to do IO, so using a library that attaches context to function calls makes sense. Dynamically executing functions just to get a syntax you like seems silly though. What about generating macro code into a header?

                              1. 1

                                It’s been done: https://github.com/rabbitmq/erlando The issue with marco- and parse-transform-based Erlang libraries is that they don’t ‘stick’ in the ecosystem. My guess is that developers are too used to 1:1 mapping of code to bytecode (for reasons of debuggability). Also, parse transforms can be brittle/untestable. Basho’s lager is pretty much the only parse_transform that I’ve seen embraced extensively in the wild.

                                From a different angle, there’s nothing dramatically silly about dynamically executing functions in Erlang. There are tons of places all around OTP that do this: see the {Mod,Fun,Args} interfaces to Supervisors and gen_servers, dynamic callbacks in gen_event, mnesia transactions, etc. fun(Blah) -> expressions even get compiled to ‘named’ functions during lambda lifting, and they are very efficient, unless of course huge environment captures are involved. Erlang is in fact very introspective and dynamic – I’d go as far as to say that it is almost a lisp, at heart. I’m certain Robert Virding played no small part in making it so.

                                1. 1

                                  From a different angle, there’s nothing dramatically silly about dynamically executing functions in Erlang.

                                  Yes that’s true. I was thinking about it purely from force of habit—I normally use C++. I just traced a performance issue in some code using std::function down to an allocation in libstdc++ that only happens if the function is a lambda with captures. 8 bytes made all the difference. So you can see what I’m used to thinking about. ¯\_(ツ)_/¯

                        2. 4

                          The nested case statements are what bother me.

                          I’ve generally addressed this with two solutions: 1) toss the nested handling into a function call 2) case on a tuple that has everything that can be evaluated in it and case on the dot product of options. 2 obviously only works if you nested cases don’t depend on each other.

                          1. 3

                            Elixir addresses nested cases with the with macro.

                            1. 2

                              I should give Elixir a spin, given how much I love Ruby.

                            2. 1

                              I’ve done the nested handling as function calls. It annoys me because it splits up all the code in a Node.js kind of way. But it’s decently ergonomic as the function names end up as comments for the early exit condition, and I think early exit conditions should be commented unless they’re really truly obvious.

                              I haven’t heard of doing a single case over all the inputs before. That’s a fantastic idea! My first thought is I usually order my checks and returns to avoid writing out the Cartesian product of their conditions in if/else statements, so this strategy could be cumbersome. But pattern matching, wildcard _ in particular, should provide opportunities to merge cases. I’ll definitely have to try it out!

                              Thinking about it also makes me curious how aggressively Erlang can optimize pattern matching. For example, suppose you have 4 independent options, one of which is expensive to compute, and there is only one valid case. If you just case over all 4 options anyway, will the expensive one only be computed in some cases? Or will all options be fully evaluated and bound before the pattern matching starts? Can Erlang detect some functions have no side effect? Of course you can manually optimize this pretty easily, but I’m still interested in seeing what Erlang can do there.

                      1. 1

                        Is there a reason one cannot parse a JS string into a simple-ish JavaScript AST object, and vice versa? It seems like if the parser were written in a simple / reasonable way, one could also extend it with a macro system.

                        1. 1

                          I don’t have any experience with them, but implementations of ASTs for JavaScript exist… https://astexplorer.net/ http://jointjs.com/demos/javascript-ast … also macros! http://sweetjs.org/

                        1. 1

                          I’ve been alternately impressed and frustrated by Chrome’s ability to set breakpoints in CoffeeScript and/or JSX… Here’s hoping they’ll improve breakpoint setting as well!

                          1. 3

                            … but the one thing I keep coming back to, that I believe has enduring value in almost all situations, is the audition project:

                            The most significant shift we’ve made is requiring every final candidate to work with us for three to eight weeks on a contract basis.

                            I don’t doubt it’s effectiveness at generating a realistic work sample, but I can’t imagine ageeing to a multi-week project as part of an interview. Even if I were between jobs, just one of these projects would uncomfortably limit my time for generating competing offers. Maybe I’m an outlier, but I have the feeling this would really shrink the application pool, retraining those who are more desperate or star-struck.

                            1. 1

                              Shrinking the application pool may be acceptable or even desirable, if they’re telling themselves that they’re eliminating the not-best applicants…

                            1. 15

                              I would love for this to be an urban fantasy animated film. Google is the villain, building up its wage-fixing cartel, when it is beset by teams of magical unicorns who smash in and rescue the hostage engineers.

                              “Muah ha ha, you’ll need Steve Jobs' personal permission to leave my castle! *crash* No! Guards, guards! Curse you, unicorns!”

                              1. 4

                                and Google is run by literal giants!