5 ms·
>Have very little idea, I don’t do web development. Then I would say you are a bit behind the curve when it comes to what the majority of people are doing in U
by leafboi 6y ago
>Have very little idea, I don’t do web development.
Then I would say you are a bit behind the curve when it comes to what the majority of people are doing in UI development. The majority of UI development happens on the web. Current practices involve using a functional paradigm to deal with mutable state. The pattern is called Functional Reactive Programming. FRP for short.
>I think the majority of programmers nowadays, and have been for decades, working on internal line of business applications. No reason to generalize their experience over the rest of the software development.
I'm generalizing over a vast majority. The vast majority of SW development is web stuff and basically in web development performance is not the priority. Useability is. Basically people choose languages and frameworks like ruby, php and python over C++ for ease of use. Software development bootcamps emphasize javascript not C++.
These languages are so slow that it doesn't matter if you follow the functional style or not.
>JS is merely passing data to WebGL APIs. These APIs are implemented in web browsers, and underneath browsers in GPU drivers. Both modern browsers and GPU drivers have millions of lines of code written in C or C++, and JS is not performant enough to replace these, not even close.
Yeah I know. Don't write your OS or OS drivers in javascript. In general the platform is implemented by one guy and everyone else operates in his universe. Most web developers and even game developers won't get into the internals of OpenGL.
>Yes, and that’s usually a good thing. Here’s a method: https://docs.microsoft.com/en-us/uwp/api/windows.storage.str.. https://docs.microsoft.com/en-us/uwp/api/windows.storage.str.... You can’t write bytes to a stream without the stream. From API design perspective that’s a feature, not a bug.
You missed the point. Object.write is worse than write(stream) because Object can hold more things than just a stream. If you implemented Object.write instead of write(stream) then your write method is forever tied to irrelevant context. Again see the banana gorilla jungle example.... literally the addOne method cannot be reused without creating a banana, a gorilla, and a jungle.
If you want to restrict your function to operate on certain data types you do this by restricting the input parameters. That's it. There is no need to attach it to irrelevant context when it doesn't even really touch other parts of the state.
>Visibility of that state. Usability of the API. Composability of the overall thing.
In FP you hide private state in functions. It is hidden anyway. "C" below.
A -> function(A){return B(A, C)} -> B (C is hidden state)
If you think about your programs this way where hidden state is ephemeral you never will have objects that are in a state of half correctness. The object only has two states: Existence or non-existence, never a state where only half of it's properties are initialized. The hidden state is forever stored within the function expression, ephemeral and inaccessible as it should be.
This is 100x better than the explicit use of "private." Hidden state in this case is truly hidden by structure rather than by syntactic tags.
Useability is equal. As you yourself as said object.verb() is semantically equivalent to verb(object).
Objects are not more composable than functions. The only way to compose objects are through inheritance, (which is bad) and object composition which requires the parent object to be aware of the child Object.
Example:
class B():
....
class A()
def __init__(self, B)
self.b = B
A is aware of B, therefore A is not modular. You have to customize A to be aware of B to compose it with B.
universal function composition:
def compose(f: A->B, g: B -> C): A -> C {
return function(x: A){return g(f(x)); }
}
def addOne(x: int){
return x + 1;
}
def timesTwo(x: int){
return x * 2;
}
addOneThenTimesTwo = compose(addOne, timesTwo)
addOne is not aware of timesTwo, timesTwo is not aware of addOne, compose is universal meaning it works on any function of type A -> B.
In terms of composition. Combining functions to form other functions hands down beats combining objects to form other objects. Literally no contest. It's one of the defining features of FP. All your FP primitives are literally tiny modular bricks that your compose into data pipelines that pump data from Input to Output. It is at the two ends of this pipeline where the functional style breaks down and you have to either start breaking rules or using complex abstractions to deal with IO and mutable state.
>The internal state of the object returned from setupConnection method can be just the TCP socket, or it can be a socket + AES decryption + a decompressor.
These are all static and stateless IO functions. You're not really doing OOP without an internal mutable var. You could literally rename that class to namespace. (I'm not too familiar with C# but I'm assuming the use of the word static means that the function is not tied to instantiated state meaning all state arrives in as a parameter negating the need for using the word "class" over "namespace")
- Const-me 6y ago> The pattern is called Functional Reactive Programming. Worked with that in .NET a bit, specifically this one: https://github.com/reactiveui/ReactiveUI https://github.com/reactiveui/ReactiveUI Not a big fan. For example, when I wanted to fix a bug, put a breakpoint to the code that failed, and reproduced, there was no way to tell who or why sent that event, the original context was already lost. For this reason I prefer simpler ways with interfaces or lambdas. Not sure if that’s a problem of the approach or the implementation I used. But generally speaking marshalling contexts is complicated and is a common source of bugs, YMMV. > in web development performance is not the priority. Useability is. I can see how it can be the case on servers (one reason is economy, for many companies it makes more sense to rent more EC2 instances instead of writing better software), but I’m not sure same applies to client. In the browser, you have the same 16.6ms performance budget for optimal usability. The fact that JavaScript is generally slower than compiled languages makes things worse. > literally the addOne method cannot be reused without creating a banana, a gorilla, and a jungle. Every design pattern can be abused, and none of them is a silver bullet. If you want to reuse that code, make it a global function, all OO languages have them or equivalents (static classes in C#). But other times, the feature if very useful. See the example linked in my previous comment (I hope C# is readable enough even for non-users), the iReadStream object returned from setupConnection can potentially hold quite a lot of referenced things, GZIP compressor, AES implementation, yet for the user of that object it’s just a trivially simple interface with a single read() method. > If you want to restrict your function to operate on certain data types you do this by restricting the input parameters. Due to polymorphism, OOP allows to write functions operating on data types unknown to either compiler or the programmer writing that code, as long as the types implement the expected functionality. Microsoft replaces both data types and code of their IOutputStream.WriteAsync method with windows updates. Their changes don’t break my code, they don’t even require me to recompile it. For that particular OS kernel API, you might say we aren’t re-implementing it on our side, and you would be correct. Still, that level of flexibility is very useful for making software. At least once the software complexity grows past some threshold: for simpler stuff it often does more harm than good. > This is 100x better than the explicit use of "private." These things are orthogonal. No one forces you to return half-initialized objects, you can throw an exception instead. > Combining functions to form other functions hands down beats combining objects to form other objects. Not every object can be expressed as a function. That C# example I linked above can, because iReadStream interface only has a single method, but if we want to do the same for writing, we now need 2 methods, write() and flush(). > You're not really doing OOP without an internal mutable var. 1. Why not? We have objects, they have methods which mutate their internal state. Looks OOP enough in my book? 2. If I would have actually implemented these objects instead of writing // TODO comments, these implementations all would have a fair bit of internal mutable vars, due to buffering, AES blocks, and other shenanigans.