- 22
- 15 612
mcsamr
United States
Приєднався 4 гру 2020
Trying out a channel.
This gives you traits in javascript
Do you have a different go-to for compositional inheritance in javascript?
At 00:50 I said "I'm gonna deal in terms of what things are" when I mean to say "in terms of what things DO". Traits are related to duck-typing, where we care less about what things say they are and more about what they're able to do.
At 00:50 I said "I'm gonna deal in terms of what things are" when I mean to say "in terms of what things DO". Traits are related to duck-typing, where we care less about what things say they are and more about what they're able to do.
Переглядів: 708
Відео
A cool way to do reflection in javascript
Переглядів 3,5 тис.Місяць тому
I of course made a mistake, and it should be [name]: instead of name: Whoops
There's no such thing as an error
Переглядів 852Місяць тому
#referp? Should it be called something else, or is this whole idea just nonsense over-abstraction?
A brief introduction to functional programming in javascript
Переглядів 797Місяць тому
Let me know if you want more ramda
Abstraction is not the enemy... lack of documentation is.
Переглядів 4,6 тис.Місяць тому
Document your stuff.
Minecraft, but it's just regular Minecraft.
Переглядів 1213 роки тому
Minecraft, but it's just regular Minecraft.
You could have solved it with something like ...(favcolor.value && { favcolor: favcolor.value } its shorter than what you had and easier to reason with than what you later developed. But yes its a nice discovery you made regarding reflection.
Not reflection. Also, the payload must be serialized, if sending via REST - remove null values during serialization.
I've been procrastinating to watch this whole video for a long time
I was so invested in this video only to discover the most basic and commonly used pattern for creating objects. No hate though, I liked the way you explained. It would help out beginners I guess. If you watch any beginner level tutorial about javascript objects, you will find this pattern being explained.
Nice [[]]
Huh, I thought this was just normal js object manipulation....
Just send everything and let the backend logic for favourite colour deal with the empty value - it has to check the value anyway.
Its called “spread operator” not “rest operator” 😄 sorry it started to annoy me a bit after you said it multiple times haha
Like 100
...opt('favoriteColor', favoriteColor) function opt(key, ref) { return ref.value ? { [key]: ref.value } : {} }
i dont feel people are arguing against abstractions, nor are the people who would be are saying they shouldn't exist at all. the argument they are trying to make is that *good* abstractions are not something which can be made on accident, and a bad abstraction will do more damage than a good one. needing to keep multiple ideas in your head as you move down abstraction layers is very hard to do, and can be the case when you move to abstract as much as you can. it is much more valuable to consider if it is actually going to improve the quality of your code outright. the misdirection will add overhead to your code, and replacing a one-liner with a different one-liner does not improve anything; it just adds misdirection. it is very hard to not make a lot of abstractions in OOP, and because of that you end up with, imo, a lot of bad abstractions. the methods you show in the video, like `getAdminUsers` or `getNonAdminUsers` dont make the code any more readable, they just redirect anyone trying to understand what is happening in the actual code, like maintainers, back to your method. there is a great video called "Object-Oreneted Programming is Good*', which is more aptly called 'How to write good procedual code' which covers this topic more in depth near the end. i agree with his video entirely, and my code quality has improved since. it's a good watch if you are at all interested in the oposing side. try everything!
I feel like a simpler way to solve this problem is to make a function that takes an object, and returns a new object with only the keys on it that are truthy. But I like the creativity!
What you're saying common sense. But common sense is not very common, even in developers.
It's one way of doing this and it's not completely wrong. However, I think you should utilize the undefined value as is instead of this additional logic. JSON.stringify removes undefined values from an object, so just run every object through JSON.stringify before sending to the server. This is the way axios handles for it, for example, and in my opinion it's the correct use case for undefined values. And you kind of need to stringify the request data anyways, since there is no concept of data types in the HTTP layer, it's just bytes being parsed on both ends. In fact, JSON doesn't even contain undefined value, and null should be used to represent an "empty" value. So tl;dr: prefer to use undefined to omit properties, null to represent empty values.
> if (user.admin === true) { user.onboarded = true } Only in JavaScript.
It's a good thing to learn something new.
Mixins are a similar idea
Hey man, thanks for sharing :) I love the idea of traits as a mechanism for composing behavior out of a bunch of small components, and your tiny implementation of them is awesome! I feel like we need more small but powerful extensions like this. However, do you worry about the tying of implementation details to interface? The interface of `CanVote` eseentially boils down to `['vote', 'mayVote']`, but that's never mentioned anywhere explicitly. Instead, the implementation is passed around directly. I'm not sure if or how this would actually manifest as a problem in practice, it just feels like it could become an issue. Also, small tip: you can use `Array.every()` instead of `Array.reduce()` to make the implementation slightly more succinct! Something like `const hasTrait = (obj, trait) => Object.keys(trait).every(name => typeof obj[name] === 'function')`.
The advantage of duck-typing like this, where to check if something has a trait we just check for the existence of the properly named functions, means we could make another trait CanVote2 with the vote and mayVote functions that do something completely different, and hasTrait on CanVote will still return true for an object from a class with CanVote2, which means we've also kind of created interfaces! The only issue here is we can't guarantee that every trait with the same function names will accept the same type of arguments or return the same type, so it's still less powerful than statically typed interfaces. I'd be very interested to see someone come up with an easy way to do typed interfaces in vanilla js without typescript too! And yea, I forget about Array.every all the time. Really good point. I usually stick to the trinity of map/filter/reduce, so I forget things like any and every exist.
True! The duck-typing aspect means that you can create and sub in new implementations easily, which is dope. I suppose what I'm wondering is if there's value in reifying the interface of a trait (distinct from the implementation) in some way. The way it currently works, if some code wants to check if an object has a trait, then they'll need access to some implementation of that trait. They'll either need to call hasTrait with the original CanVote (with its implementation attached), or they'll need to mock up an empty implementation like { vote() {}, mayVote() {} }. As you say, there's no argument or return type checking, so mocking up an empty implementation like this is trivial, but it still feels a bit strange that in order to test a trait, you need to have an implementation of that trait, y'know? Perhaps that's part of the beauty of this approach though: the implementation and the interface have the same representation! This certainly simplifies the mental model. Separating them like I'm suggesting would complicate things, and may not be needed.
I feel like undefined value should be returned and checked by the API (if the API your are dealing with is correctly setup) not by the client because you are doing work that should be done anyway by the server, so either you are doing twice the work, or the server can be corrupted by a malicious user. Anyway cool to see you are exploring the langage and you had fun!
Please zoom in on your text editor so that viewers can read what you're writing.
Yup. Tools in the box. Use the right tool for the job. Document the job. Everyone loves the construction analogy in software development, so don't use a screwdriver to drive nails and make sure you update the as-built blueprints if you deviate from the provided plans. There are too many developers who don't understand this basic concept and anything that is hard (abstraction, pointers and memory management, etc) is bad because why not.
I love that someone is talking about this. The transfer of information from one human to another is complicated and difficult and deserves more intentionality. I definitely agree that documentation needs to be more than a brief summary. And that need grows exponentially with the size of the company. More developers need to realize that documentation is part of the job too, not just writing code.
a take so based i'm going to have to be nicer to javascript programmers
unrecoverable errors vs recoverable errors. I love it.
I get what you are going for and would argue that you miss the point. First of all you are right in that "thou shall not repeat business logic", it is a fair and accurate point. But abstraction is not just creating functions, it is also about encapsulating things inside objects and using inheritance. On some level, encapsulating things makes some sense, it will enforce some boundaries and avoid traps. However encapsulating things too much or before the needs arise is just as bad as not having encapsulation, that is why a lot of people including me are pushing back on abstraction and especially OOP. The point of fighting abstraction is that you may also never change the admin check or realize that you actually have 5 levels of permission for the user. Trying to predict futur use case before knowing them fully may lead you to create abstractions that are more complex than they need to be or that are just not correct for the use case. Either way you end up in a spot where you need to fight the abstractions that were created because they don't account for the specific thing you need. A recent example from my work is using a big library to receive messages. They have an abstract class for the support of communication and function exposed such as read that is blocking and a function poll that is non blocking. For performance reasons, we would need the poll or read to be blocking (ie get the message as soon as it arrives) but also not just block indefinitely to be able to exit the program gracefully. What happened is the creators didn't thing of that and didn't provide a way of doing this. So now you either change their abstraction or find out by digging into their abstraction that in the end they always use posix sockets and just get a hold on those and block poll with timeout on those. In the end, the abstraction was wrong and the cost of modifying it was high for something that was trivial, and that's the problem, abstraction is not a replacement for dumb straight forward code that does what you need, it is a tool to make dumb staight forward code easier to write/read and understand.
I fell into a similar hole fairly recently when creating my own Result type. Semantic differences between errors can be expressed in much simpler ways that don't attempt to radically diverge from the standard way things are handled in JS. Extending the Error class achieves this handily, but given that you an throw anything in JS, you're not limited to just throwing errors (although that somewhat betrays the notion of writing "idiomatic" JS). Unsurprisingly, a good number of other languages have multiple ways to handle "errors," where some are arguably more aligned with this than others. Go has multiple return values for error handling, but also has panic and recover statements for unusual/extreme cases. Erlang/Elixir's exceptions aren't particularly unusual, but handling them is usually done at higher levels by supervisors, and is therefore reserved for something "unexpected" or generally unable to be handled, otherwise you just return error tuples and handle them with pattern matching. Rust has panics (that are technically recoverable) that are a much more severe form of runtime exceptions and Result types that you pass around for more intelligent handling. Haskell and OCaml are much like a middle ground between Elixir and Rust, with Exception and Option types, but unlike the BEAM languages, exceptions don't usually propagate as far and aren't used as often. After using my JS Result type for awhile, I got tired of dealing with it, though. It's nice to know that I'm handling something that can error when using JSDoc/TS, but it gets really tedious when I have to wrap every single IO operation or external dependency that could possibly fail. The second someone else touches the codebase, there's also no guarantee that the convention will be strictly followed unless I watch PRs like a hawk. Betraying the convention of a language really hampers productivity and the ease in which someone else can pick up where you left off, in my experience. It's probably a better idea to pick a different language with built-ins that cater to this idea than to try and force it into an ecosystem and runtime that isn't particularly friendly to it.
cool
you consistently have L takes on your channel - shows that you have never worked in the industry before (in any meaninful capacity). I wish you luck.
Well, to each their own I guess
Maybe if you elaborated your critisism?
You can do it more simply with function submit() { const payload = Object.fromEntries( Object.entries({ name, email, favoriteColor }) .filter(([_, ref]) => ref?.value != null) .map(([key, ref]) => [key, ref.value]) ); post(payload); } Some of the utility libraries like Lodash/Underscore.js also give you access to a `mapValues` type utility function that does the same as above, but more efficiently and less code on your part. `zip`/`unzip` are also functions in these utility libraries that help a lot with this kind of object transformation and go tegether well with `fromEntries` / `toEntries`.
4 loops under the hood tho - fromEntries - entries - filter -map his the solution 2 loops -entries -spread operator
@@AkioJunichiro Which is why I mentioned utility libraries. In this example however, the number of loops doesn't matter. If it were a bigger object, then sure, but for something with <100 keys, you're not going to see much of a performance difference. Also, optimisation should never come at the cost of readability unless the tradeoff is *really* worth it. Coders spend 90% of their time working reading code and 10% of their writing it
Love that you're learning and having fun :) this video made my brain hurt though ... this is a tutorial on what _not_ to do. Especially using reflection for something that can be solved with a standard. Clever code != Good Code. Bad habits all over the place.
Nice tip, though this makes me wonder how many people have read a book about javascript
Since undefined is not a valid JSON value, when it is serialized and sent to the server, the key should disappear (try JSON.stringify({"hello": null, "world": undefined})). Also you can simplify your code, return { [r]: r.value }
i would always submit all values even when null/undefined
Careful with sending non-nullable values to a PHP backend.
@GeneraluStelaru if it's not nullable, the validation will fail on the php side
Great observations, but I disagree with the proposed solution. Its not about docs, but about tests! Those are real world examples with schema, application, edge cases, short description. Tests must be consistent with code, docs just supposed to be. When I want to quickly catch-up how something works, I'm always looking for tests. So don't waste time on docs, write good tests!
Nice exploration. But over time you come to a realization that simpler is better, especially when you're working in teams. The abstraction you just made is not easily understandable and that might come to bite you in the future. Happens to me sometimes. That doesn't mean you stop doing that. Do it, you're mastering the language.
Teams should RTFM i guess
Just [r]: r.value wouldn’t work? [r] takes the name of the r variable and it can be used as an object key here
[r] wouldn't be the name of the variable, it would try to take the whole value of r, and use it as the key for the new object
js without typescript is the enemy
This seems a lot like the concept of Errors As Values. Have you ever programmed with that paradigm? I agree a lot with you that thinking about your code in terms of a happy path and an unhappy path with the possiblity of invalid state is an absolutely fantastic way to model problems and code and I really like the way you have put it. If you haven't already, then I would suggest you try working in a language with 'Maybe' and or 'Error' types such as Rust, Go, Haskell, Ocaml or others. I think they are an absolutely brilliant way to write clear and correct code, with features such as Rusts "?" operator making the use of them often quite concise too.
I would disagree with you on a subjective point: The most important usage for abstraction is not avoiding copy+paste coding or even making it so a single change to the definition is propagated to all the callsites. Instead I would say that the most important usage for abstraction is about giving a clear and concise name to a block of code. It's an ability you don't have otherwise. When you think about abstraction like this, you still will want to abstract things even if the code isn't being reused in any way.
Also you have error in your opt function, it should return {[name]: value.value} in your case.
You are correct
You overcomplicate things. all you can do is set your value, in your case "favoriteColor" to a value or "undefined". When you do your post request with "JSON.stringify" it remove undefined values from object. That's all. So you can do: {name: name.value, email: email.value, favoriteColor: favoriteColor.value || undefined} And if you need more checks, like validation, then turn it into ternary operator and add your conditions, for example: {name: name.value, email: email.value, favoriteColor: favoriteColor.value && /^#[0-9a-fA-F]{3,8}$/.test(favoriteColor.value) ? favoriteColor.value : undefined}
It seems to me what is more complicated is a matter of opinion.. Having to do the extra step later of removing undefined values seems more complicated to me than having an easy-to-use function like this. But you know, to each their own. Simplicity is incredibly subjective.
@@mcsamr You don't have to remove it manually, it remove by default. Just try: JSON.stringify({a: 1, b: undefined});
I think this is very helpful in large code bases where you work with other devs. I use assert() a lot and I don't likethrowing errors, so I mostly return some kind of result, it doesn't always have to be an object with a message, sometimes a simple false in case something went wrong is enough. When we do this, we lose an advantage of errors though which is bubbling including a stack trace. results don't bubble, you gotta do it manually whereas exceptions bubble automatically and include a lot of information. So in some cases, using exceptions is simply better. as always it depends
Have you tried Elixir? It handles errors similar to Go but has glorious pattern matching to more easily handle the returned value depending on if it has an error or not
I'm looking into both Elixir and Gleam right now
ThePrimeagen is a big advocate for this concept, and he's mentioned many times that he uses asserts a lot, basically to catch bugs. If you can assert that a function or code module is being used as intended, you can greatly reduce a lot of bugs. I had actually used assert at work to ensure that a certain node module is never imported unless our application was started with a certain flag in the config, because if the module was loaded when that flag wasn't enabled, many other issues would occur. My coworker who reviewed my code wasn't a fan of the fact that we are basically defending the code against ourselves and suggested I remove it. I listened to their suggestion, but funnily enough, I accidentally added code that imported that module as part of another change they requested, and I introduced a bug in our software. If only the assert were still present, I would have caught it... Just thought I'd share a relevant story, I'm a huge fan of this kind of defensive programming, especially since it's so easy to compile asserts out of the binary for production builds, in many languages, so there's no performance downgrades. Subscribed via RSS!
I wrote the above message after only watching 8min of the video, but I see the point of the whole video now after watching it all. I think it's definitely interesting! I'm always interesting in ways to make code more declarative and to state different scenarios in a way that is readable, and makes it easy to write "business logic" to handle when things happen outside the happy path.
@@natemaher1389 I like the idea of naming these three possibilities "happy path", "unhappy path", and maybe "no path".
also I think you should rename referp to attempt as that conveys the right meaning. referp makes me think you mean Refer "P" to X which conveys no further meaning about what P is or what it's for
14:06 In this scenario it's easier to just do "if ( i <= 0 ) return 0;" No need for excess calls and/or exceptions that add unnecessary bloat. But yeah I get what you're saying, it's why I named my "errors" as messages instead. If it's a message then it's not necessarily an unacceptable state, just one that needs to be handled.
If you do that you just end up in C land again where sometimes a 0 is an error, often it's success and -1 is the error, or maybe the error is 1-who knows if it's even in the return values, maybe it's whether an out parameter is null. You need your return type to be a proper superset of the output domain of your function, else you cannot effectively communicate these messages via return. Certainly not in a standardised way
@julians.2597 oh but I program in C exclusively because c++ is an absolute mess unlike the way it started. Objective-C is apple specific last I checked and C# is a cross between native and interpreted last I checked. Rust is bloated and gets in my way. Zig is promising but still in development. C is the only low level language above asm that is reliable to do what I want it to do. Gonna read the rest of your message and if need be add another reply
@julians.2597 as for the varying meanings of the return value, as I said I'm making my OWN messages where 0 always = success and any other value means a deviation from intended result. I chose to start with a flat copy of all the microsoft status codes for the sake of easy pass through to public api. All the other ones are caught internally and converted to a more meaningful code. The only thing I don't like about it is the need for files that translate the code to text but that is likewise a good thing because it means the local language can be used to make it easier to read. Basically the library I'm designing is meant to be a layer on top of the os that ensures we devs can stick to just *.o and *.a files when distrobuting instead of faffing about with exes and packaging formats. The only thing causing me to slack off on the project is the per heap memory management code that I'm not looking forward to 😭
Great video friend! To be honest, I hate functional programming. In my experience, the smarter a person who writes in a functional style is, the unmantenable their code is. I worked for several companies on scala, and as a rule there was always a smart productive developer who created code that just drove me crazy. In the last team, it reached the point of complete absurdity, they used code generation, and macros and the project simply could not get build on my working mac, the build required more RAM than my computer could give (no memory settings helped). As a result, I joined a team that rewrote everything they did in java in less than six months.
Interesting point, and I'm 100% pro good documentation. However, as an old coder, I know that this problem was solved a long time ago with Classes. If these vague raw objects were instances of "User" with a method called "isAdmin" and no public "admin boolean" then, boom, no problemo! The main issue is that it's trending to vilanize OOP so people are reinventing the wheel with what they think is functional programming.
There is so much wrong with your statement that I can't even begin.
@@lucemiserlohn You need to decide whether to engage in a full debate or let the point go entirely. Simply pointing out that something is wrong without elaborating doesn't contribute meaningfully to the discussion.
I am so glad to hear someone actually talking about good software engineering rather than the over used OOP bullshit.
@@Nellak2011 it's ironic how OOP would solve this in the blink of an eye. A "User" class with a method "isAdmin" and no public "admin" property. It's trendy to bash on OOP
@@Zizaco It is not just Trendy but based on fundamentals. OOP creates a pit of failure. To be successful in that paradigm you must apply design patterns perfectly and deal with code bloat and boiler plate. It is like balancing a pencil on its tip. Meanwhile Functional Programming and related paradigms like DOP and DDD create a pit of success where it is actually hard to make obfuscated systems. The fundamental reason why is that OOP rigidly combines data and functions which creates coupling, whereas FP and DOP treat data as data and it is free and decoupled. So yea, OOP is trash even when dome perfectly. FP and Procedural are the only ways to program in a valid way.
@@Nellak2011 the last phrase in your comment shows how dogmatic your take is. I'd bet you don't have many years in the industry so you take the words of others on why OOP is bad. DOP is about performance and efficient processing (memory layout), not about design. Also it's not incompatible with OOP. I'm not claiming that OOP is the only valid approach (nor the best), but learning the fundamentals and why it was created in the first place can help avoid a lot of reinventing the wheel type of situations. I'm just pointing out that a 5 line "class" would solve the issue presented in the video in a somewhat simpler way. Mapping a "User" business entity to an "User" ~code perfectly aligns with DDD.
@@Zizaco Dogmatic? My take is the opposite actually. It is based on logical principles such as decoupling. Something OOP is not based on. An analogy may help. OOP says that the Earth is the center of the solar system, so we need complex epicycles (Design Patterns) to explain it and it is such a brittle model to any changes. FP says the sun is the center of the solar system, so we need only one principle to explain everything we see. Everything now all makes sense with one equation and is robust to changes. In FP we dont need design patterns, we just need 1 principle "Make it easy to change" and then an understanding of how to do that with functions. Read a book or something before you talk out of your ass 🤣🤣
Love this video, a video on OOP would be great also