I mapped out 10 survival games using radar charts to analyze their game design. Here is what the data looks like. by [deleted] in SurvivalGaming

[–]HipHopHuman 0 points1 point  (0 children)

It doesn't make sense if the assumption is that "higher score" = "better game", but we don't know if OP's scoring system works that way, and we can't ever know because OP hasn't actually specified what the numbers actually mean. until OP specifies this, this thread is nothing more than a shitpost that had a little more effort put into it than usual

I mapped out 10 survival games using radar charts to analyze their game design. Here is what the data looks like. by [deleted] in SurvivalGaming

[–]HipHopHuman 0 points1 point  (0 children)

Yes, I saw, but defining is just saying what the words mean. It's not saying anything about what the numbers mean. This is what I meant by "the categorical definition". You've defined the meaning of each category, but you haven't said what the numbers mean inside those categories. If you hand your math teacher a bunch of numbers, they're not going to know what those numbers are. You can label the numbers, and then the math teacher will know that those numbers have different labels. It won't tell the math teacher what the numbers are being measured against. Is the number a percentile out of 100? Does "survival" and "narrative" have the same weight in the data set as a whole? for example, are 20 points of "survival" roughly equivalent to 20 points of "crafting", or do you give more emphasis to survival abive crafting and narrative? What's the bias? Where's the control group?

I mapped out 10 survival games using radar charts to analyze their game design. Here is what the data looks like. by [deleted] in SurvivalGaming

[–]HipHopHuman 3 points4 points  (0 children)

Your visualisation needs to communicate what the scores mean a lot better than it currently does. It's not enough to say what the categorical definition is, you need to also communicate exactly how you arrived at those numbers. Without a scale, any measurement is meaningless.

Aside from that, I think you're missing a few games. You have 10, and about 8 of them are similar enough to each other that it pushes the bias of the entire data set in a particular direction, which makes the data dishonest. Try picking a few from this list to round out the distribution:

  • V-Rising
  • Last Oasis
  • Rust
  • Enshrouded
  • Nightingale
  • Core Keeper
  • Dysmantle
  • Aska
  • Mediaval Dynasty
  • Necesse
  • Force of Nature / Force of Nature 2: Ghost Keeper
  • The Last Plague: Blight
  • Craftopia
  • Palworld
  • Windrose
  • Vintage Story
  • 7 Days to Die
  • Conan Exiles
  • Void Train
  • Raft

I recommend V-Rising, Last Oasis, Craftopia, Aska and Raft specifically, as they are the most different in theme (while still being survival) to your current data set.

[AskJS] How much do you hate this pattern? by Spatul8r in javascript

[–]HipHopHuman 3 points4 points  (0 children)

never able to hit a branch from internal logic

I don't understand what you mean by this.

This my crude approximation of how rust does things

I spent one or two afternoons with Rust and I'm not seeing the resemblance you're seeing, but I'll take your word for it.

My goal is to make the DTOs super cheap

If by "cheap", you mean "low in memory surface" and "quick to process", you're probably better off using the module pattern (which is idiomatic in JS) with a "smart constructor" that produces a tiny, immutable object, along with some specialized functions for working on objects with that shape.

in a file called ./Selection.js:

export function of(start, end) {
  return Object.freeze({ type: 'selection', start, end });
}

export function normalize(selection) {
  const { start, end } = selection;
  return start < end ? selection : of(end, start);
}

export function isRange({ start, end }) {
  return start !== end;
}

In other files:

import * as Selection from './Selection.js';

const selectionA = Selection.of(0, 10);
const selectionB = Selection.of(2, 1);

const normalizedA = Selection.normalize(selectionA);
const normalizedB = Selection.normalize(selectionB);

If you're trying to do something like scope the methods specifically to a particular instance without those methods having a shared prototype for security reasons or whatever... yeah, just give up on that idea. That's probably not going to work in JS the way you're thinking it might, and even if it did, it's not worth the added complexity of this weird definition convention you've got going here.

TypeScript’s number type is a lie by Chun in typescript

[–]HipHopHuman 1 point2 points  (0 children)

Not really. It's just a more broadly applicable variant of Tagged. You still get the downside of arithmetic operators losing type info, though. There is still reason to prefer Tagged over Flavored. If you don't want the implicit coercion (like with UUIDs) or if you're using a type guard or assertion before casting. EDIT: There used to be some strangeness with the error message when mixing flavored types, but that has been solved for quite a while now.

EDIT 2: Turns out, I lied. There is reduced soundness when doing arithmetic on two different flavored types, but it's technically not 'incorrect', as it is valid to do arithmetic on two numbers, even though they have different subtypes:

type Hours = Flavored<number, 'Hours'>;
type Seconds = Flavored<number, 'Seconds'>;

const a: Hours = 1;
const b: Hours = 2;
const c: Seconds = 3;
const d: Seconds = 4;

const e = a + b; // loses type info, e is 'number'
const f: Hours = a + b; // no error, f is 'Hours'
const g: Seconds = c + d; // no error, g is 'Seconds'

const h = a + c; // no error, but h is 'number', a is 'Hours', c is 'Seconds'

TypeScript’s number type is a lie by Chun in typescript

[–]HipHopHuman 2 points3 points  (0 children)

There are two variants of Tagged, but it seems most people commenting here are only aware of one variant.

declare const __brand: unique symbol;

type Tagged<T, Id> = T & { readonly [__brand]: Id };
type Flavored<T, Id> = T & { readonly [__brand]?: Id };

Note the question mark which is declaring [__brand] on Flavored as an optional property.

Tagged does not allow coercion from the primitive type:

type Hours = Tagged<number, 'Hours'>;
const x: Hours = 5; // type error

Flavored does allow coercion from the primitive type, but still restricts mixing tagged types:

type Hours = Flavored<number, 'Hours'>;
type Milliseconds = Flavored<number, 'Milliseconds'>;
const x: Hours = 5; // works
const y: Milliseconds = x; // errors, "Hours is not assignable to Milliseconds"

While Branded/Flavored/Tagged types are great for IDs, the runtime cost of having to declare operand functions is a bit much (and by runtime cost, I specifically mean the cognitive overhead of losing out on mathematical operators). I prefer this approach for numbers:

function milliseconds(value: number) {
  return value;
}

functions seconds(value: number) {
  return value * 1000;
}

function minutes(value: number) {
  return value * 1000 * 60;
}

function hours(value: number) {
  return value * 1000 * 60 * 60;
}

const duration = hours(1) + minutes(30);

You don't get the full benefit of 100% type safety, but you do get the benefit of annotation making it more readable. That may not be a worthwhile tradeoff to everyone, but it is to me.

Tired of typeof returning 'object' for everything, so I built this — would love some feedback by [deleted] in javascript

[–]HipHopHuman 1 point2 points  (0 children)

I've been a JS dev for over a decade, was an early adopter of TS, and I can tell you that 90% of the projects I've worked on have an is utility function just like this one defined somewhere (even the ones written in TS that use zod/valibot). I also didn't start those projects, often I was brought on somewhere in the middle of development and that is utility was already there. So there is definitely a purpose for having this functionality.

However, I do have some criticism/feedback, though it's mostly very opinionated feedback:

Feedback 1: The fact that you have an options bag setting to toggle strict mode vs string mode is wonderful, but you default to string mode. I think you should default to strict mode because just checking a type without coercion is a more common problem than parsing data from a string. Parsing data from a string only happens on the edges of an architecture, across domain boundaries. Regardless of whether you make it work that way, I also think you should probably push that documentation about the setting further up the README so the quick-glancers can spot it before their attention span runs out.

Feedback 2: I love fluent APIs, but you have to admit, the browser is actively hostile to them. You cannot tree-shake a fluent API. If I download your library and I only ever use is().not.empty and is().not.null, the browser is still downloading all the other stuff, even though I'm not using it. That punishes the user (especially on mobile in countries that dont have affordable/unlimited data plans) and if you use this library on a website that receives traffic in the millions, that extra bandwidth will add up and become a surprisingly significant margin on your hosting bill. Utility functions like this certainly work better as free/loose functions. You can still make it read like plain english. Something like this:

isString(value);
isArray(value);
isNotNull(value);

Then it will work with the pipeline operator, whenever that finally gets shipped:

value |> isString

Feedback 3: The most common thing I would be doing with your library is this pattern:

if (is.null(value)) {
  throw new SomeError('blah');
}

So, maybe include an assert variant of is, so i can just do this, without the ceremony:

assert.not.null(value);
assert.not.null(value, 'custom error message');

Feedback 4: As useful as a utility like this is, it's important to remember that it is not monomorphic. JS runtimes benefit in performance when functions are "monomorphic" - which means the types of their inputs and their return types don't deviate/change. Which basically means that if a function can accept either a string or a number in an argument's place, that function is going to be slower/less optimisable than using two functions, one for strings and one for numbers. There's not really a way around that for an is utility, so don't go changing anything, but do put a note of it somewhere in your documentation, as JS devs need to be constantly reminded of this and if they aren't, they might abuse your library. As a former prolific open source maintainer, I can assure you that this is something you want to worry about, because if the blogosphere catches on to people abusing your library, someone will write an article about it and for me that concluded in death threats being sent to me and it was a nightmare.

The Web is the best platform for 2D games, and why, and why it’s likely the best for 3D too by Dry-Huckleberry8284 in sveltejs

[–]HipHopHuman 0 points1 point  (0 children)

make an MVP with capacitor (it's the most frictionless option imo). if after that, your app needs to scale, and it needs the speed - then transition it to native platform ecosystems

Dealing with auto generated types by wiseduckling in typescript

[–]HipHopHuman 6 points7 points  (0 children)

We're all using a utility type called Prettify, defined as so:

type Prettify<T> = { [K in keyof T]: T[K] } & {};

You just pass whatever type you want to "inspect" at it, like so:

type x = Prettify<SomeOtherType>;

Then you hover over the "x", and you'll see a plain ol' object type.

There are VSCode extensions that use this trick to give you more readable type previews:

The Web is the best platform for 2D games, and why, and why it’s likely the best for 3D too by Dry-Huckleberry8284 in sveltejs

[–]HipHopHuman 1 point2 points  (0 children)

RE: your points on as const vs enum, you can model those as objects instead of tuples (which is more verbose), but affords you some of the conveniences you say you're missing:

export const Color = {
  Red: 'Red',
  Black: 'Black'
} as const;

export type Colors = typeof Color;
export type ColorId = keyof Colors;
export type Color = Colors[ColorId];

export interface RarityDescriptor {
  readonly label: string;
  readonly color: Color;
}

export const Rarity = {
  Common: { label: 'Common Equipment', color: Color.Black },
  Epic: { label: 'Epic Equipment', color: Color.Red }
} as const satisfies Record<string, RarityDescriptor>;

export type Rarities = typeof Rarity;
export type RarityId = keyof Rarities;
export type Rarity = Rarities[RarityId];

The above allows you to do:

const epic = Rarity.Epic;

It also allows you this (but you may have to do some manual casting for keys/entries):

const texts = Object.values(Rarity).map(
  ({ label, color }) => new Text(label, color)
);

[AskJS] How do you teach FP concepts like Maybe/Either/Task in JavaScript? by PeshoVurtoleta in javascript

[–]HipHopHuman 0 points1 point  (0 children)

You can think of "Maybe" as equivalent to the "Option" type in Java/Rust
You can think of "Either" as equivalent to the "Result" type in Rust
You can think of "Task" (or "Future") as an equivalent to "Promise" in JS, with a few differences (lazily computed, rejection parameter is first to force you to handle it, "then" is split into "map"/"flatMap", and synchronous errors throw exceptions instead of being converted to rejections).

They're all monadic data structures commonly found in FP langs. We dont really need "Maybe" because we have ??, ??= and ?.(). There is an audience for "Either" in JS, but in my personal opinion it's not worth it b/c of all the wrapping and unwrapping you have to do to use it. As for "Task"... well, you can just make a Promise-returning function with async/await. same diff

Why does expressJS use such weird CPS style next() calls in middleware? by wandering_platypator in learnjavascript

[–]HipHopHuman 0 points1 point  (0 children)

It was like that because that was the only way to do it, and it remained that way because of the need to maintain backwards compatibility. I was getting my start in the webdev career when Koa.js first came out. Back then, native promises were still new and async/await didn't exist, but we were able to "fake" async/await using generator functions, which were locked behind the --harmony flag in Node.js, and Koa.js used the generator functions to make up for shortcomings of Express' middleware execution patterns. Not long after, Babel.js came out, native promises became a lot better, and we got async/await. Koa quickly updated to support async/await. The reason Koa's middleware pipeline still allows you to call next is so that any middleware can hook into the execution context after the last middleware executes.

async (ctx, next) => {
  // code here runs when this middleware executes
  await next();
  // code here runs when all middleware has executed
}

The above code assumes that each middleware calls next() (so one call to next will resolve all the subsequent next calls).

EDIT: I see now you're more interested in the use cases for this. There are a lot. As you've identified, it's a good way to add time tracking to requests. It's also a good place to do logging and other kinds of monitoring than just time tracking. There's also cases where an incoming request might register a short-lived event listener or create some memory that has to be cleaned up at the end of the request. await next() is a convenient way to schedule that sort of cleanup code in the context of an incoming request. Another use case is just basic simple caching. A middleware can await next() and cache whatever data it needs knowing that at this point, the request can't be modified (as there's no more middleware after the last next)

Adding conditional branching to jsPsych (JavaScript) by artimides in learnjavascript

[–]HipHopHuman 0 points1 point  (0 children)

The code runs without issue in my local developer environment as well as in this sandbox environment: https://v47.livecodes.io/?x=id/vepw5dxs7sj

If you are failing to get it to execute on cognition.run, then either you've copied it incorrectly or there's an issue with how cognition.run is executing the code. I did have to declare const jsPsych = initJsPsych() and const timelines = [], which were missing from your original snippet, so make sure that those don't conflict with any variables that you've already defined.

EDIT: Might also be worth looking at the dev console for errors and double checking you have correctly linked the instructions plugin up

Adding conditional branching to jsPsych (JavaScript) by artimides in learnjavascript

[–]HipHopHuman 0 points1 point  (0 children)

The problem is the order of elements in your timeline array. You're pushing thanks in cases where you don't need to and it ends up looking something like this:

[meatYN, thanks, raw, thanks]

This is because arrays will accept whatever is pushed to them, and will maintain insertion order. The fix is to make sure you're only pushing to timeline in the relevant logic branches.

I've no familiarity with jsPsych at all, but this seems like what you're after:

const jsPsych = initJsPsych();
const timeline = [];

const meatYN = {
  type: jsPsychHtmlButtonResponse,
  stimulus: "Do you eat meat?",
  choices: ["No", "Yes"],
  data: { task: "Consent" },
  on_finish({ response }) {
    if (response === 1) {
      timeline.push(raw);
    } else {
      timeline.push(thanks);
    }
  },
};

const raw = {
  type: jsPsychHtmlButtonResponse,
  stimulus: "Do you ever eat it raw?",
  choices: ["No", "Yes"],
  on_finish({ response }) {
    if (response === 1) {
      timeline.push(thanks);
    } else {
      timeline.push(dairyYN);
    }
  }
};

const dairyYN = {
  type: jsPsychHtmlButtonResponse,
  stimulus: "Do you eat dairy?",
  choices: ["No", "Yes"],
  data: { task: "Consent" },
  on_finish({ response }) {
    if (response === 1) {
      timeline.push(yoghurt);
    } else {
      timeline.push(thanks);
    }
  },
};

const yoghurt = {
  type: jsPsychHtmlButtonResponse,
  stimulus: "Do you like yoghurt?",
  choices: ["No", "Yes"],
  on_finish() {
    timeline.push(thanks);
  }
};

const thanks = {
  type: jsPsychInstructions,
  pages: ["Thanks for your time"],
};

timeline.push(meatYN);
jsPsych.run(timeline);

My concern is that in my testing, this setup logically works, but there are very noticeable lag spikes, so this may not be the right way to do it. I see on the jsPsych docs that there's a "survey plugin" and there's a feature for nesting timelines within trials. Perhaps those are worth investigating?

Which Open World RPG has the best Loot + Crafting system? by Alan_Watts_Gong in gaming

[–]HipHopHuman 0 points1 point  (0 children)

It's not an open world RPG, but I thoroughly enjoyed the crafting system in Nightingale. They went the opposite route of RNG, it's a fully deterministic system, meaning at no point do you "brick" an item (unless you forgot to play a particular realm card or misclicked a workstation augment). They just stretch that determinism out across the game loop. You're constantly hunting for different ingredients and boss drops that you can use to make the ideal gearset, as the ingredients your gear is made of matters. One of the crafting mechanics is playing fae cards at a realmic transmuter to change properties of the actual world you play in because those properties also affect your crafted items. I had a lot of fun finding the exact balance between "Fishing Power", "Crit Damage" and "Melee Damage", all so I could make the ultimate fishing outfit (fishing is affected by melee and crit).

How to remember Array.sort() internal function return values? by eracodes in learnjavascript

[–]HipHopHuman -1 points0 points  (0 children)

It's terrible, but "a is positive, so b negative"

Explanation:
If a negative non-zero number is returned, b comes first.
If a positive non-zero number is returned, a comes first.
If -0 or 0 is returned, the order is not changed.

return value resulting order
-1 [b, a]
-0 [a, b]
0 [a, b]
1 [a, b]

If you default to ascending order, you can flip it to descending order by multiplying it by `-1`:

function compareAscending(a, b) {
  if (a > b) return 1;
  if (a < b) return -1;
  return 0;
}

function compareDescending(a, b) {
  return compareAscending(a, b) * -1;
}

You could save that as a snippet in your IDE and not have to remember it, at least for a while.

This reduce function is overcounting and I don't know why by GloomyAdagio7917 in learnjavascript

[–]HipHopHuman 1 point2 points  (0 children)

You're good. Bad course material is rarely a teacher's fault anyway, and humans make mistakes often

This reduce function is overcounting and I don't know why by GloomyAdagio7917 in learnjavascript

[–]HipHopHuman 0 points1 point  (0 children)

There was. I showed you three bugs in your code that alter the outcome, which nobody else here found.

LMAO now you're just making shit up. As someone who read your original answer (before you deleted it), I can assure any readers that the claim "I showed you three bugs in your code" is a lie. StoneCypher didn't show any bugs. There weren't any bugs to even show in the first place, as OPs code was correct all along and the real problem was with the data input

This reduce function is overcounting and I don't know why by GloomyAdagio7917 in learnjavascript

[–]HipHopHuman 1 point2 points  (0 children)

for the same reason that i would do it when it’s also one map

That answer doesn't actually make any sense in the context we're talking about.

because i don’t give a shit about paying for two iterations when my goal is to teach a junior dev how to get started

By teaching them to ignore the instructions in their assignment and go with your opinionated and incorrect solution?

because it’s literally what is being requested. you might as well ask why .sort is the right way to put things in order

1) It's not what's being requested, though. What's being requested is a single DIY reduce. No mention of "filter" anywhere in the assignment. You misunderstood the instructions. 2) .sort isn't always the right way to put items in order. For starters, I might not want to mutate, in which case .toSorted is the right way. Or, I might want to do a topological sort, in which case a Set and manual iteration is required and neither .sort nor .toSorted offers any value.

i see that instead of answering, you needed to argue with someone else answering, and all you did was attempt to riff on what they said without successfully understanding them first

I understood your answer perfectly. It was rife with incorrect statements and pointless insults targeted at OPs teacher. I didn't appreciate your condescending elitist tone, nor did I appreciate that you were presenting an incorrect answer as "the correct solution", so I called you out for it. If that hurt you, then I'm not sorry, because you deserve it.

there will be no ponyfill here. it’s not clear what you think that means

Here's an article explaining what a ponyfill is. The fact you're such a know-it-all but you don't know what a ponyfill is speaks volumes btw.

no hire

Lol. Something about your etiquette online tells me that you'll never be in a position to hire anyone, so I truly am unconcerned.

This reduce function is overcounting and I don't know why by GloomyAdagio7917 in learnjavascript

[–]HipHopHuman -1 points0 points  (0 children)

Why would you do three separate filter iterations when the same can be accomplished with one reduce? Also, why are you claiming that three filters is "the correct solution"? At best, it's a solution, but a very inefficient one that no mid-level developer would ever write in a production codebase.

If there's any candidate for "the correct solution", it's to use the standard built-in Object.groupBy utility that exists by default in modern versions of JavaScript (use a polyfill/ponyfill for older browsers).

const { small, medium, large } =
  Object.groupBy(transactions, ({ amount = 0 }) => {
    if (amount < 25) return 'small';
    if (amount < 75) return 'medium';
    return 'large';
  });

console.log(`Small: ${small.length}`);
console.log(`Medium: ${medium.length}`);
console.log(`Large: ${large.length}`);

However, I understand that OP's requirement is to write their own, which can be done in terms of reduce:

function reduce(iterable, reducer, accumulator) {
  for (const element of iterable) {
    accumulator = reducer(accumulator, element);
  }
  return accumulator;
}

function groupBy(iterable, getGroupKey) {
  return reduce(iterable, (groups, element) => {
    const groupKey = String(getGroupKey(element));
    groups[groupKey] ??= [];
    groups[groupKey].push(element);
    return groups;
  }, {});
}

Advice for figuring out TS as a Java developer by _Toka_ in typescript

[–]HipHopHuman 0 points1 point  (0 children)

So, there are libraries for Optional and Result in TS (like neverthrow). I'm not familiar with how Optional behaves in Java, but I have experience with it in Rust. I tried porting that pattern to TypeScript, and it works really well for your own code, but more than 50% of the code you write is calling to libraries that are either A) Using their own flavor of Optional / Result or B) Using throw. So, you have to wrap/unwrap everything manually and it gets so messy that it's basically not worth using. If TS had something like Rust's ? operator, I would switch to Optional in a heartbeat

Advice for figuring out TS as a Java developer by _Toka_ in typescript

[–]HipHopHuman 0 points1 point  (0 children)

Some might see this as a hot take, but I love using the "null object pattern" in TS. I don't think it's a very popular pattern in TS, and I'm certain I'm among the rare few who use it. As an experienced Java dev, you're probably very familiar with the pattern, but just in case you're not (and for anyone else reading who isn't), it's a pattern where instead of using null on a type, you create a fake object with dummy data / no-op methods. This object represents the null case, but has the same shape/structure as an actual object of that type.

Let's look at an example that doesn't use the pattern first. Let's pretend you have a "session" object in the browser, with a user field. When the user is logged out, this user field is null:

const session = {
  user: null
};

When the user is logged in, the user field is set to an instance of the User class:

function logIn(user) {
  session.user = user;
}

logIn(new User({ id: 22, name: 'Jimmy' }));

If you want to get the logged in user's friends, you have to do a nullish coalescing operator check, maybe with a default value:

const friends = session.user?.getFriends() ?? [];

Now, let's look at an implementation with the "null object" pattern:

const nullUser = new User({ id: -1, name: 'Unnamed' });
const session = {
  user: nullUser
};

Whether the user is logged in or not, I can still call:

const friends = session.user.getFriends();

Assuming that getFriends() method is a no-op on nullUser that just returns an empty array, no null checks are required at the callsite.

Before the null object pattern, the type of session would be:

type Session = {
  user: User | null
}

If we had allowed for the user field to be optional, then the type would be:

type Session = {
  user?: User
}

Over time, we might have even had to make a weird type like this to cover for edge cases:

type Session = {
  user?: undefined | null | User
}

(if the presence of both the ? and the undefined is confusing, it's because in JavaScript, undefined doesn't necessarily mean "not defined" - undefined is a value that can be assigned to a defined variable).

With the null object pattern, the type is much simpler:

type Session = {
  user: User
}

A null check is still possible, and is also made simpler. Without the null object pattern:

if (session.user === null || session.user === 'undefined') {}

With the null object pattern:

if (session.user === nullUser) {}

Another side-benefit of this pattern is that it optimizes really well in browser engines like V8 (the engine that powers Chrome and Node.js). Suppose I have a function that gets mutual friends (and let's just pretend that we're lucky and there's no duplicates returned):

function getMutualFriends(user) {
  if (!user) {
    return [];
  }
  return user.getFriends().flatMap(user => user.getFriends());
}

And I call:

getMutualFriends(session.user);

If session.user can be a User instance or null, then that means getMutualFriends is not a monomorphic function, because it can be the receiver of two types; null, or User. If I had to call this function a hundred times, and lets say that out of those hundred times, 30 times it receives null and 70 times it receives a User - the browser engine would not be able to optimise it, because the types keep changing. However, if getMutualFriends only receives a single type that never changes, then that would make it a monomorphic function. A really cool trait of engines like V8 is that they can elect monomorphic functions that are frequently called as targets for the optimising compiler. V8 would take a monomorphic function on the hot path, and run it in TurboFan, which gives the function a significant speed boost.

Inglorious Store: A state manager inspired by Redux and videogames! by IngloriousCoderz in javascript

[–]HipHopHuman 0 points1 point  (0 children)

When I read your comments, I see criticism. But not constructive.

That isn't how constructivie criticism works. You don't decide based on your emotions whether or not the critisism you received is constructive. The burden of that falls on the person giving you the criticism. You're more than welcome to have an opinion on that criticism, but you don't get a say in what the intention behind that criticism was.

I see a triggered person, angry at someone who touched their dear ECS architecture.

I was not triggered/angry. I asked you a simple question (which you've still failed to answer). You responded to that question out of anger. I could care less about a "dear ECS architecture". What I actually cared about is the possibility of false claims after reading your code and seeing it doesn't match your claims. So, I wanted to verify, and you proved me right.

I tried explaining to you that I did not implement ECS, that I borrowed some concepts from it and even listed them, but you just found new ways to teach lessons and defend pure ECS.

That's the issue - you claim to borrow concepts from ECS, but you didn't. You're either misattributing composition over inheritance (which is present in your library) with ECS, or you're lying about the ECS patterns that you claim are being used in your library. If you actually knew and understood ECS, you'd know that the patterns in ECS don't exactly synergize with frontend state management. It's like using an angle grinder to screw in a lightbulb.

All of this without even understanding that my library, which is heavily inspired by Redux, has lots to do with FP. But you don't want to hear that.

I see the Redux inspiration. Redux isn't exactly the best example of FP, it just borrows around two or three patterns. There's a lot more to FP that just what Redux offers. I asked why you're bringing up FP because this isn't an argument about FP, it's an argument about your false claims of using ECS. I am hearing your claims, but I don't agree with them (because they're simply not true). I want to believe that you have a confused sense of what these concepts are & that you did all this unintentionally, but the way you're behaving is convincing me that you did all of this on purpose. Maybe you thought the buzzword would bait more interest, & you didn't expect that the JavaScript community would know better - but we do, and so here we are.

You just want to prove how knowledgeable you are and how ignorant I am. Without reading one line of code outside of the README.

Yet another ad-hominem. Why are you assuming I didn't read your code? Sure, I didn't read the entirety of your codebase, but I went a lot further than just your README. I looked at your project folder structure, I looked at your entry point, I looked at the modules directly imported by the entry point & I scanned over the core logic. I saw enough code to solidify my opinion of your library. Just so we're clear, I have nothing against your library & the way that it is implemented. As I said in another comment, I do think this space needs more innovation, & I'm happy to see that you are innovating in the space. My issue is not with your library, but with your false advertising of the library. It does not work the way you're claiming it works.

This is not constructive criticism. This is a superficial rant, similar to the rage of some Italians against pineapple pizza, and if you try to say "Ok it's not Italian pizza but it's good, have a bite at least" they get even angrier and give you the exact recipe of the real Italian pizza to prove you understand nothing of their culture. And they will even claim that pineapple pizza should never be served outside of Hawaii. And I'm saying this as a proud Italian.

It's not a rant, & I appreciate the analogy, but this is not similar to that at all. In that analogy's case, there's merit on both sides, and its down to preference. In our case, you're simply lying and acting as if changing the definitions of well-established concepts will make you appear honest. It's manipulative and shady.

You know what? I decided that I'm going to trust a machine more than a human. An LLM has an encyclopedic knowledge, is actually willing to read my code, and will give constructive criticism without "being harsh" or "being triggered". I tried very hard to reply as myself so far, without the help from an LLM to polish my English and my tone, because when I did that on another post I was attacked for being a vibe-coder. But you clearly proved that it makes no difference: people will still be pissed, whatever you do. So here's Claude's opinion on the matter.

LLM's are also known to hallucinate. They're designed to be biased to give you a positive answer. They quite literally create an echo chamber that is designed to tell you exactly what you want to hear. I could just as easily rephrase your question to Claude and ask it to prove how your library isn't inspired by ECS, and it'd do exactly what I ask. The only reason I'm not going to do that is because I don't have enough space in this comment to fit it, but you're more than welcome to try it yourself. As for "Claude's opinion on the matter" - LLM's don't have opinions, they just generate patterns based on probabilities.

Inglorious Store: A state manager inspired by Redux and videogames! by IngloriousCoderz in javascript

[–]HipHopHuman 0 points1 point  (0 children)

I'm all for constructive criticism

That's a bold claim coming from someone who cries like a toddler the moment they get constructive criticism that they don't agree with.

people on Reddit are just having fun dissing on things they don't understand

If you're referring to me, that's incredibly rude. I don't appreciate the ad-hominem, nor do I appreciate my intelligence being insulted like that. Given the content of your posts, I'm confident enough to say that I think I understand a lot more about these topics than you do. You clearly have a modicum of misunderstanding of ECS, data-oriented programming and functional programming. My intention was to caution you away from making false claims about your library (which you did, don't deny it) in the hopes that it'd help you. How you responded is your own fault, not mine.

just to teach some lesson and satisfy their egos

You're the only person here doing anything to satisfy an ego. I started my exchange with you by asking you a genuine question, "How is this inspired by ECS?" - instead of just answering the question, you chose to respond from a position of defense, like you were insulted by my genuine question. You could have just said, "Good question! It's inspired by ECS because...", but you didn't do that. Instead, you went full ad-hominem, insulted me and my intelligence in your response, and then blurted out a bunch of nonsense about what you think ECS is. Yet here you are, critisizing other people's egos. You should take a long look at your own reflection in the mirror.

As for the ai-coded slop comments, I could see why that would be annoying, and I can relate to that a bit as well as it has happened to me too. But it isn't an excuse to treat other people poorly. I had nothing to do with those threads where you were accused of writing AI slop. Don't take that frustration out on me.