The salary ranges are very misleading, unfortunately. For example, posthog will only pay 120k gbp for a senior full stack in London, while they’ll pay 220k usd for the same role in Seattle. Ramp also doesn’t pay 200k+ for roles based in EU.
I don't get this pay disparity between Europe and the US. It's totally crazy to me as someone who has lived and worked in both the States and Denmark (and citizen of both). We work the same hours. If you're in a major metropolis you're paying the same amount of money for food, a home, etc. You're getting taxed at a much higher rate, on average, in Europe.
Why do we put up with this? I think EU employees should just say we will take the same pay you're paying your US employees or we won't take the offer. Giving in to the insane expectation is what keeps us underpaid to our US counterparts.
Since Posthog points to their salary calculator in their job posted, I listed the lowest and the highest possible values as the range.
For ramp, the $250k+ salary is listed based on their "who is hiring" post [1]. But you might be right about the salary being applicable to only US candidates.
Otherwise, I pretty much listed what's there on the job pages.
The point in the article was to prefer composition over inheritance, which is sort of like using OOP “the right way”.
It didn’t discourage OOP as a whole, nor do I think it should - making a game without objects would be a nightmare.
Plenty of games were made using plain C and even Assembler.
What is really needed are not objects in a OOP sense, but a way to make a mutable, composite data type. Else you would have to pass tens of arguments to even simplest of functions.
Whatever you do, don’t stop treatment. Follow the doctor’s advice, but at the same time use critical thinking, and seek a second opinion (from a doctor, not alternative medicine) when in doubt.
As for coping from her side - be understanding, encouraging and accepting. If she loses hair, or gets bad skin - don’t make it a big deal.
Choosing treatment is a very personal thing. My father chose to stop his chemo treatments and it improved his standard of living dramatically. (he's still fighting cancer) A friend of mine (a nurse) said she'd never torture her self like she's seen so many people do with some treatments. (She died of cancer a few years ago)
Sometimes other's people's advice isn't based on what is best for you, but on an aggregate across many people, and you could be on the bad side of percentages.
To add maybe one more point to this - it’s so much easier to run parts of the pipeline for devs who aren’t familiar with the environment. It can also serve as a documentation for what’s required to develop.
Won’t setting up a k8s cluster require writing resource definitions? I imagine you’d need to write a statefulset. How’s that better than writing a docker-compose?
I’m not sure how vscode does it, but it allows you to publish ports in real time as well.
Nope you can make a simple pod definition and not worry about a deployment. For a local cluster you'll just have one node and everything is effectively a statefulset. IMHO it's easier to write k8s yaml, there's tons of tooling, clear schemas, etc. You could even script calling the API server directly.
I agree, the title of the article is a bit misleading. In general, you’d still need an IDE installed on the host machine, which can then connect to a runtime on the container. With VS Code and remote containers, it’s quite easy.
Alternatively, maybe it’d be possible to have the container expose an IDE over http (possibly vscode through the browser?).
VNC/RDP via Guacamole or some other VDI solution can provide this with relatively little effort. But it sort of defeats the point. Using your local IDE to connect to a container allows for a snappier, more responsive development experience than a VDI can provide. Plus remote containers is basically just ssh. So a lot less bandwidth and general overhead than a full on VDI solution, and you don't need to have a full VNC server set up on your containers.
Yep, I use it. There are two tricks to mitigate this:
1. Using a :delegated or :cached flag when using a bind mount can speed it up a bit
2. For folders that need a lot of RW, but don’t need to be shared with the host (think node_modules or sbt cache), I bind a docker volume managed by docker-compose. This makes it extremely fast. Here's an example: https://gist.github.com/kamac/3fbb0548339655d37f3d786de19ae6...
> In line with this, observational studies suggest that exercise has a statistically significant preventive effect on premature all cause mortality with active individuals showing up to a 72% lower risk of premature mortality from all causes compared with inactive individuals
Why would they create their own obscure solution? I imagine LUA is very easy to run sandboxed, but I can’t shake the feeling roblox would be better off integrating scripting with JS. It would come with powerful typing via typescript, a great tooling (along with a type checker), and a lot of resources available online.
It’s also especially ironic to vouch for Javascript, and then in the next sentence switch to Typescript, which was precisely created to overcome some of JS defects.
> That's a popular opinion among people who have only used Javascript. Few who have used both would give it a second thought.
I have extensively used both languages for years and you haven't adequately answered my previous criticism [1].
> Why throw all of that away for an inferior language?
As if Lua (either the language, the implementation or the ecosystem) is not inferior.
> Roblox is immensely popular, has a wide install base and thousands of developers with code in Lua already.
This is probably the only reason to justify Luau, and shows yet another problem with the Lua ecosystem: they don't (or rather, can't) share their creations to each other. JavaScript is not a perfect language but the JS ecosystem has successfully converged into a single widespread set of solutions including TypeScript.
string.gsub receives at most three arguments where the final optional argument is the maximum number of replacements, and returns two values where the second value is the number of replacements made. Therefore if the arguments do not have escape sequences the outer string.gsub receives 0 and `<arguments>` doesn't get replaced.
I intentionally asked you to find this bug out because you can acknowledge the particular class of bugs only after biten by that bug, and you didn't seem to even know what might be problematic. In the other words, by now you can't get away by saying "you should give all function calls a name" posthumously.
It's a frequent novice mistake to write `if (a = b)` in C/C++, but any good enough C/C++ programmer will point it out (and modern compilers will flag a warning). Eventually people get used with this problem and the cycle repeats, this time with less novices falling into the trap, so this class of bugs is---while problematic---not considered a huge deal. Given your reaction though, I doubt this is the case for Lua and that's yet, yet another reason to avoid Lua.
Edit: ah, I just realized what you're actually complaining about.
Being able to drop two arguments into a function call is great! It's one of the better things about Lua.
But when you don't want it, wrap the call:
f(a(b,c)) -- this will call f(r1,r2) with two returns
-- vs
f((a(b,c)) -- this will always call f(r1)
And now you know how to prevent this class of bug. Catching it in an intermediate variable is possible but by no means necessary.
Original reply follows:
Alright, yeah, that kinda sucks.
Here's another dumb one: table.insert, if you give it two values, it's table.insert(tab, val) and it inserts at #tab + 1.
If you give it three values, it's table.insert(tab, index, val), and that's bad enough: but if you pass it (tab, val, nil), it interprets val as index.
Which is surprising! you'd think f(a,b) and f(a,b,nil) would be the same thing, and usually, they are. But not in this one case.
But to be fair, here's an example of Lua done well:
local L = require "lpeg"
local end_str_P = L.P "]" * L.P "="^0 * L.P "]"
local function _disp(first, last)
return last - first - 2
end
-- capture an array containing the number of equals signs in each matching
-- long-string close, e.g. "aa]]bbb]=]ccc]==]" returns {0, 1, 2}
local find_str = L.Ct(((-end_str_P * 1)^0
* (L.Cp() * end_str_P * L.Cp()) / _disp)^1)
I'll let you reason about what it does, and why it would be such a pain in the butt with regular expressions.
> But when you don't want it, wrap the call: [...] And now you know how to prevent this class of bug. Catching it in an intermediate variable is possible but by no means necessary.
Well, I knew that; in fact what I've linked in the past discussion was the exact code to handle this case in my type checker. It is seriously confusing that a seemingly excess parenthesis affects the code anyway, and you can't exactly know whether the parenthesis is necessary or not without using a type checker.
There are some other languages that have multiple returns and can pass multiple arguments for nested calls, notably Go, but Go only does that when the inner call is the sole argument to the outer call. And Go does have a type system. Tons of other languages including JavaScript have an explicit "spread" operator which mostly retains the usability and avoids such problems.
> Which is surprising! you'd think f(a,b) and f(a,b,nil) would be the same thing, and usually, they are. But not in this one case.
Or any C function relying on lua_gettop. Seriously, nil should have eliminated in that stack.
> I'll let you reason about what it does, and why it would be such a pain in the butt with regular expressions.
I don't like a quirky EDSL alternative to the regular expression (or what it matters, parsing expression grammars) either. I prefer lpeg.re in that regard.
local re = require "re"
local long_str = re.compile(
[[
end_str <- ']' '='* ']'
long_str <- {| ((!end_str .)+ ({} end_str {}) -> disp)+ |}
]],
{
disp = function (first, last)
return last - first - 2
end,
})
Lpeg itself is a fine library, but its use of 1 as anything or -1 as the end of string is annoying (at the very least `lpeg.P(-1)` should have been `lpeg.Eos` instead). I would still use lpeg proper for constructing patterns programatically.
But you can't give lpeg as an answer to string.gsub and so on. First, the standard library still remains problematic and people will get tripped up (if you have no room for implementing `|`, one can require that `|` is escaped, m'kay?). Second, it's not a default, or at least not what you can easily `require`. The Lua ecosystem is fragmented primarily by not having a universally usable package system (I stress that LuaRocks is not) thus an external library is not always an option.
By the way I don't know why you would want to give that obscure example to brag about Lua. Your code is in fact slightly incorrect: `(L.Cp() ...) / _disp` looks like that the capture is applied only to that parenthesis but it's not, thanks to the operator precedence. The main problem of regular expressions is an inability to refactor and you haven't correctly demonstrated that.
(I'm pretty sure that this won't get a reply, but I'll leave the final post for others.)
> So a deficiency in your code, you chose to blame the language.
The language is to blame if it encourages a deficiency. I've linked to the type checker code to show that this kind of bug is virtually invisible, not like the aforementioned `if (a = b)` case, even to who knew enough to write a type checker.
> So Lua offers two superior ways to process strings over one? and you're still mad?
Lua doesn't offer two superior ways, it's lpeg that offers. And what lpeg offers (first-class PEG) is irrelevant to my problem (Lua the language lets you to accidentally put unintended arguments) anyway.
> Ierusalimchy wrote lpeg specifically to address known deficiencies in the pattern library.
And yet he left the faulty version in the standard library.
> Sure, you can't always use it. Just like you might be stuck on an obsolete version of JS in an embedded system.
You don't need the embedded system to show that you can't always use the JS package system. Web browsers some years ago didn't support that. As a result webpack (among others) happened and we are now comfortable to compile JS down to the bundle. Does Lua have any equivalent?
When I raise the issue with Lua the language you point to Lua the ecosystem and sideskip the fact that Lua the ecosystem itself is fragmented thanks to Lua the implementation. My issue with Lua (either the language, the implementation and the ecosystem) is multitude and pretty much every issue is connected to each other.
> It is, in fact, not.
Your code is different to the following (which is what I consider more readable):
-- [end_str_P and _disp omitted]
-- returns 0 for [[, 1 for [=[, 2 for [==[, and so on
local end_str_len_P = (L.Cp() * end_str_P * L.Cp()) / _disp
local find_str = L.Ct(((-end_str_P * 1)^0 * end_str_len_P)^1)
Your code unintentionally groupped `(-end_str_P * 1)^0 * (L.Cp() * end_str_P * L.Cp())`, as `a * b / c` would be `(a * b) / c` instead of `a * (b / c)`. It is not "incorrect" as the former doesn't contain any capture, but has enough potential to become incorrect. That's what I want to avoid by not using a "quirky EDSL".
I have a pretty decent experience in lua, js, python, perl and a few in other scripting languages, and personally wouldn't just throw js away in general. Yes, it is a legacy freak, but some latest parts of it are at least worth adopting in other languages. To name a few, constructing objects ({a, ...b, c:[...d, e]}), destructuring, arrow functions conciseness, proxies, default arguments. This helps much in scripting and libraries for scripting. Also I find separate objects and arrays being a better approach than just lua-tables. I like lua, really, but some parts of it... you have to struggle with constantly. Select and "..." sematics in general, meta restrictions, a bloaty syntax.
Imo, the next language with a little stricter typing than both js and lua, which takes best parts of these and gets rid of bad practices, and with a typed version in mind, will be a big win for everyone.
I upvoted this, although I disagree about tables vs. separate objects and arrays. If you were to say separate maps and a specialized array type, I would be more inclined to your point of view! Not sold completely, mind you.
In either case, it's a productive disagreement. I agree that Lua isn't a perfected language; merely a very, very good one.
From your list, I would takeaway default arguments as the first thing to bring over to this hypothetical successor language. Arrow functions could be avoided by the simple expedient of renaming the keyword `function` to simply `fn`.
> Roblox is immensely popular, has a wide install base and thousands of developers with code in Lua already.
100% agree till here:
> Why throw all of that away for an inferior language?
I'd say "not the right tool for the job, given their target audience".
Saying that JS is inferior to Lua (Lua's great IK) isn't much of a productive comment—if they built their audience with JS, they wouldn't be pivoting to Lua because it has achieved superiority in the eternal language wars.
It was a provocative comment, sure, but I wouldn't say unproductive: others asked why Lua is superior, and several people answered, myself included.
Heartily agree that the same basic argument would apply if Roblox had started with JS! Lua isn't so much better than JS that it would be worth scrapping collective centuries of labour over.
But given that it is the better language, in my not-so-humble opinion, why on Earth would they switch?
I think the only rational way to argue superiority of Javascript over Lua, is to point to aspects outside of the language itself: larger install base, more libraries, bigger, more vibrant community, better tooling, and so on.
But I prefer to call that the ecosystem, and can confidently say that Javascript, alas, has the superior ecosystem. A pity, because Lua is the superior language.
Lua is smaller, more consistent, and much more flexible with integration since it just C.
Debugging is built into the language in a simple way.
_G gets a table of all global variables
debug.getlocal allows you get any and all variables at each stack frame at that point including name, value and type.
You can even use debug.sethook to have a callback after every function, line, or instruction.
On top of all that you have LuaJIT which not only runs ridiculously fast, but can deal with dynamically loaded files incredibly fast. It even has a direct foreign function interface and allows you to use shared libraries directly without writing any more C or recompiling them.
Your last paragraph is the essence of why I chose Lua for my own projects, especially the FFI, but unfortunately doesn't apply to all of Lua. The divergence between Lua and LuaJIT is certainly not a strength.
The 'stock' Lua interpreter is very fast, very small (good for cache retention) and all its C functions are reentrant, so no global interpreter lock: those apply to both flavors.
As the tiniest of nitpicks, `_G` is the default home of the global environment, but it's just a variable. The correct way to get the environment is `getfenv(1)` or just `_ENV`, depending on your flavor.
> Debugging is built into the language in a simple way.
Lua debugging interface notably lacks breakpoints. You can simulate breakpoints by per-line hooks but it's not performant. Many popular JS engines including V8 do support performant breakpoints by bytecode patching, and Lua do have multiple patches doing the same (well, a common theme) but none is official.
Tables are immensely better than JS objects, in every way. I could entertain a separate array type in the abstract, but given what actually exists, I will take tables over the JS collection of objects, arrays, and maps, any day.
Extensible syntax, which is used to implement "objects", but also to, well, extend the syntax. Indexing, assignment, calling a table like a function, operator overloading: it all works the same way, and it all works. Metatables are superior to prototype chains, simple as.
Coroutines. I don't have the energy to write up my personal take on why these are the best baseline abstraction for cooperative concurrency in a dynamic language; one of these days I'll publish something so I can simply link to it.
Multiple return values. A language without variadic parameters and multiple returns is less dynamic than one which has them, if you're going to go dynamic, go all in.
Environments! This is one of those things which you might not need very often, but when you do: oh man, great to have. Number one thing I would carry over into the browser, where we really don't want to leak context, and JS fights against this constantly.
As minor desiderata, Lua's type coercion, the fact that 0 is truthy, a separate `..` concatenation operator, and optional semicolons which basically cannot bite you: Lua has no need for a separate `==` and `===`, and I consider that a strength. I also think the separate `null` and `undefined` is clunky, but that opinion isn't strongly held; Lua has considered adding an `undef`, and it's an interesting debate which I'm just barely on the "no" side of.
It's not easy to tell all your players that their games don't work anymore or that they cannot use coroutines. Regarding the second point, more concretely: they no longer can attach a function to a game object that defines its behavior over any length of time, and they no longer can call the sleep() function to suspend execution for a length of time.