OOP is TRWTF



  • @Zecc said in OOP is TRWTF:

    @Dragoon Sorry, why are you posting this in this thread?

    Wait, which context are we in again?



  • Just glanced over the entire article (I figured reading it in detail probably will give me cancer or something), but the entire post is nothing special among the sea of OOP/FP shillings the JS monkeys have pooped out in the last few years. You know the drill, if it comes from medium it's most probably garbage. Well, at least it's longer than the usual flavor. The author is clearly suffering from terminal frustrated writer syndrome.

    These medium blog posts (read: opinionated pieces, with "shitty" attached to "opinion") almost always has the same elements:

    • complain about OOP in a broad, non-specific sense that does not provide anything useful beyond principles, like claiming eating meat is bad because it has too much "side effect" on animals
    • are not aware of any kind of OO other than enterprise Java OO apparently (they probably think JS has a class OO model too)
    • always descend the argument into analogies, because analogies are explanations, right? It's just like monad tutorials...

    :trwtf: goes to pie_flavor who actually spent the time dissecting every sentence of the article. You're not supposed to roast garbage, it only makes them hotter 🐠


  • BINNED

    @_P_ said in OOP is TRWTF:

    :trwtf: goes to pie_flavor

    but we already knew that


  • Banned

    @Captain said in OOP is TRWTF:

    Haskell expressions are evaluated, and the only thing that executes is the runtime system which builds a tree of computations out of main (i.e. it evaluates them and then executes the compiled code).

    Oh really? Then what does happen when I have conditional statement that checks if file exists? It cannot possibly be evaluated before it gets executed. And if it's executed at the same time it's evaluated, the distinction is meaningless.

    @Captain said in OOP is TRWTF:

    Every time you pass around some IO action foo, you are passing around the same IO action (so that you have referential transparency).

    Am I passing those IO actions to function's return value or somewhere else? If it's somewhere else, then it's a side effect and the function is impure. And if it's return value, I expect it to behave exactly like a return value of a pure function does - in particular, I expect to be able to erase the value from existence because raisins, and the effect of running the code should be as if I didn't run any code at all (except for time spent and memory used, if you feel particularly pedantic today).


  • BINNED

    Let me phrase it this way: How are the side effects of the monad reconciled with pure functions being idempotent, order independent, and all that pure jazz?

    Say if I have the equivalent of:

    print(IO, "Hello ")
    print(IO, "World")
    

    How is it guaranteed that these are in the correct order and executed exactly once? I guess the correct way to do it lies in the sequencing, so the monad allows you to transform the side-effect carrying state into the equivalent of

    print(print(IO, "Hello "), "World")
    

    But does it actually force you to do that, or could you still do it wrong and make unsequenced statements, or have expressions that could be re-ordered / executed more than once even though they pass the IO monad?



  • Re: monads, I'm not sure why we're over-complicating it and turning into analogies again. I mean, that's exactly the pitfall of every monad tutorial. It's like explaining OO patterns with UML diagrams, which is complicating the topic more than clarifying it.

    @Gąska I think this SO answer should explain what you really need to know about monads, which is really not much:

    All of this and more is possible with monads. Of course, all of this is also perfectly possible without monads too. It's just drastically easier using monads.


    My take at moands: when you say something is a monad, you're allowed to perform 2 operations with it:

    • return: Given a value x of type a, give me something of type Monad a.
    • bind/>>=: Given a monad of type Monad a, and a function of type a -> Monad b, produce something of type Monad b.

    Then there are 3 monad laws, but they're just common sense: return doesn't do anything except lifting a value up, so return x >>= f is identical to f x and m >>= return is identical to m, as it just provide you a way to lift the values. Then there's associativity, which just means that you can perform binds in any order.

    So, what's the point then? Well, the point is, to make functional languages actually usable you have to describe side effects in a way that is compatible with the framework of FP. (One of the) ways is to change this:

    • function func(fileName) {checkIfFileExists(fileName);}, where checkIfFileExists is something global

    into

    • given fileSystem, run function func(fileName) {fileSystem.checkIfFileExists(fileName);} (with type String -> IO Bool)

    Now, we don't know what fileSystem is, and we don't need to care about it in our function: it's like we've mocked fileSystem and when our function is run, the runner will supply a fileSystem which can do anything, including 3D-printing fileName and then return FileNotFound. In this way our function stays "pure".

    (And, as you can see, I've used terms like mock, because ultimately what we want to do is some form of IoC/DI to not make a mess everywhere we go. OO, FP, imperative, whatever, ultimately tries to reduce the mess in one way or another via some kind of construct. It just so happens that the IO model Haskell uses is monadic in nature, so they modeled it like that.)



  • @topspin said in OOP is TRWTF:

    Let me phrase it this way: How are the side effects of the monad reconciled with pure functions being idempotent, order independent, and all that pure jazz?

    Say if I have the equivalent of:

    print(IO, "Hello ")
    print(IO, "World")
    

    How is it guaranteed that these are in the correct order and executed exactly once? I guess the correct way to do it lies in the sequencing, so the monad allows you to transform the side-effect carrying state into the equivalent of

    print(print(IO, "Hello "), "World")
    

    But does it actually force you to do that, or could you still do it wrong and make unsequenced statements, or have expressions that could be re-ordered / executed more than once even though they pass the IO monad?

    One of the monad laws says that monad binds are associative, so sequencing wouldn't matter by definition.


  • Banned

    @_P_ I know all that already. We're just arguing semantics now. My take is that since a function prescribes side effects to happen, it's impure. @Captain says this is not true and you can have any number of side effects you want and your function is still pure because evaluation isn't execution or something.


  • BINNED

    @_P_ said in OOP is TRWTF:

    • given fileSystem, run function func(fileName) {fileSystem.checkIfFileExists(fileName);} (with type String -> IO Bool)

    But, assuming everything were pure, the interpreter could call your fileSystem.checkIfFileExists(fileName) more than once, since things are supposed to be immutable and not stateful. It obviously isn't allowed to do that here.


  • BINNED

    @_P_ said in OOP is TRWTF:

    One of the monad laws says that monad binds are associative, so sequencing wouldn't matter by definition.

    :sideways_owl:

    It matters to me if the output is "Hello World", "WorldHello ", or "Hello Hello Hello ".



  • @topspin said in OOP is TRWTF:

    @_P_ said in OOP is TRWTF:

    One of the monad laws says that monad binds are associative, so sequencing wouldn't matter by definition.

    :sideways_owl:

    It matters to me if the output is "Hello World", "WorldHello ", or "Hello Hello Hello ".

    Yes, it can happen, but it'll only happen if the one who implemented the IO monad is :doing_it_wrong: and fucked up the implementation.

    Haskell only enforces typing, it doesn't enforce implementer to formally prove that monad laws are obeyed. If they are obeyed then IO monad should work as you expect (in fact you can formally prove it); otherwise there's no guaranteed and it might as well be UB. So now you know who's :trwtf: there.


  • Banned

    @_P_ IO monad does obey monad laws. Bind being associative means that (a >>= b) >>= c is the same as a >>= (b >>= c), which it is. In both cases, the order will be a,b,c - and this order is guaranteed by sequencing semantics.



  • @Gąska I'm talking about the (hypothetical) scenario that IO monad's implementation is outright wrong. Perhaps I should use WTFIO instead to distinguish it from the actual IO :wtf_owl:



  • @topspin said in OOP is TRWTF:

    It matters to me if the output is "Hello World", "WorldHello ", or "Hello Hello Hello ".

    If I've understood correctly, something like print( "Hello" ) >> print( "World" ) is equivalent to print( print( "Hello" ), "World" ) since >>/>>= is an operator that takes a wrapped value and a function. The function is called by the operator with the unwrapped value as an argument. The function should return a new wrapped value. There is some argument binding shenanigans going on too.


  • Notification Spam Recipient

    @Zenith said in OOP is TRWTF:

    If I only use static functions and pass structs around, am I doing OOP or not?

    Congradulations! You're an adobe enterprise developer.


  • Notification Spam Recipient

    @_P_ said in OOP is TRWTF:

    :trwtf: goes to pie_flavor who actually spent the time dissecting every sentence of the article. You're not supposed to roast garbage, it only makes them hotter 🐠

    To be fair I was tempted to do the same thing but my day job gives me enough aneurysms.



  • @cvi said in OOP is TRWTF:

    @topspin said in OOP is TRWTF:

    It matters to me if the output is "Hello World", "WorldHello ", or "Hello Hello Hello ".

    If I've understood correctly, something like print( "Hello" ) >> print( "World" ) is equivalent to print( print( "Hello" ), "World" ) since >>/>>= is an operator that takes a wrapped value and a function. The function is called by the operator with the unwrapped value as an argument. The function should return a new wrapped value. There is some argument binding shenanigans going on too.

    The pieces fit together in this way:

    1. You have two things, hello = print("Hello") :: IO () and world = print("World") :: IO (). You wrote hello >> world to chain the two IO ()s together
    2. hello >> world has type IO (). Haskell sees that IO is an instance of Monad, and deduces that >> fits in as type IO () -> IO () -> IO (), so >> uses the the implementation from IO (which is written by whoever implemented the monad, not you) and plugs it in, which does all the things automatically. And you can prove that it's equivalent to print("Hello"); print("World"). (You should notice here that <* fits the signature IO () -> IO () -> IO () too, but that does a different thing: it discards the second argument, making hello <* world print("Hello") instead. Also, >> does a different thing if it has a type of, say, State () -> State () -> State (), because State monad has a different implementation. In the end the monad bind/join functionality originates to what the implementer of the typeclass has provided.)
    3. Notice that there is no such a thing as IO () -> () (besides the Unsafe stuff, which is out of the question). So if you want to make use of it, you have to bubble your IO type up until main, which conveniently has type IO ().
    4. When Haskell code is compiled, it basically says "given some World, link main to that World and run it". Again, this part is none of your concern; the standard library has already implemented this for you.
    5. Profit, now you successfully performed IO in a FP setting

    It's not so much as "wrapped" value (it gives the false impression that something is actually being wrapped), but more that when you denote your type as IO, there are lots of stuff being automatically performed and inserted to avoid the process of doing manual print(IOProvider, String) all the time which can be done thanks to the type system. It hand waves these heavy-lifting boilerplates away so that you only need to write hello >> world and not whatever >> would unfold to, which would involve the entire definition of >>= in IO monad.


  • BINNED

    @cvi said in OOP is TRWTF:

    @topspin said in OOP is TRWTF:

    It matters to me if the output is "Hello World", "WorldHello ", or "Hello Hello Hello ".

    If I've understood correctly, something like print( "Hello" ) >> print( "World" ) is equivalent to print( print( "Hello" ), "World" ) since >>/>>= is an operator that takes a wrapped value and a function. The function is called by the operator with the unwrapped value as an argument. The function should return a new wrapped value. There is some argument binding shenanigans going on too.

    That's how I understood / wrote how it achieves sequencing, i.e. the outer call depends on the inner and thus the inner must come first.

    But the remaining question was: what prevents the inner call from happening more than once if it's supposedly pure.

    @_P_ said in OOP is TRWTF:

    Notice that there is no such a thing as IO () -> ()

    Is that the answer to the above??
    Why can't your hello = print("Hello") :: IO () thing not be executed arbitrarily often?



  • @topspin said in OOP is TRWTF:

    what prevents the inner call from happening more than once

    @topspin said in OOP is TRWTF:

    Why can't your hello = print("Hello") :: IO () thing not be executed arbitrarily often?

    By implementing the monad correctly? A correct implementation of IO monad will ensure that the right side of >>= should only be run once, and the compiler itself should ensure that main only executes once, unless it's fucked up, of course.

    Seriously, this is getting nowhere. "But what if standard library does funny things! Why wouldn't you just give me OS kernel access so I can make sure it's done right, because I'd never :doing_it_wrong:!" You might as well reimplement the standard library in C/C++. :wtf_owl:

    Also, yes, Unsafe stuff are Unsafe for a good reason. You simply don't open up functions that does stuff you don't want your users to do. Doing otherwise would be :trwtf: .


  • Discourse touched me in a no-no place

    @pie_flavor said in OOP is TRWTF:

    all functions can be inlined

    You'll have fun if you try that with a recursively-defined function…



  • @dkf said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    all functions can be inlined

    You'll have fun if you try that with a recursively-defined function…

    Don't, he'll pull a "all functions can be TCO-ed" to that. Then your stack trace stops making sense anymore.


  • BINNED

    @_P_ said in OOP is TRWTF:

    Seriously, this is getting nowhere. "But what if standard library does funny things"

    That's not at all what I asked. There was no assumption the IO monad does something stupid, the question is how it is possible to achieve what it achieves within the rules of the functional setting.

    If I have the following two pure functions (Not Haskell syntax since I CBA to look it up):

    def mul(a, b): return a*b
    def factorial(n): return 1 if n <= 1 else n*factorial(n-1)
    

    then in a statement like mul(factorial(5), factorial(5)) there are exactly two calls to factorial(5) in an imperative language. In a functional language, because factorial is pure, there could be one or arbitrarily more calls. If an earlier call gets memoized, there could even be zero calls to factorial.

    Now, this cannot happen with monads. Why? If you were to replace that with something using IO: a call to factorial(IO, 5) printing stuff on the way somehow has to ensure it happens exactly once.


  • ♿ (Parody)

    @_P_ said in OOP is TRWTF:

    like claiming eating meat is bad because it has too much "side effect" on animals

    + ᓀ because a single like is not enough


  • Considered Harmful

    @topspin said in OOP is TRWTF:

    order independent

    Order independent only means you can call the same function with the same inputs and receive the same outputs. The IO monad is effectively an input representing the entire computer (literally - IO a is a typedef to RealWorld -> (a, RealWorld)), and when you call something on it and return it, you get an output different from the input. Therefore, the order of the functions can matter because each function transforms the object further and further.



  • @topspin said in OOP is TRWTF:

    But the remaining question was: what prevents the inner call from happening more than once if it's supposedly pure.

    If I had to make a guess, there's two sides to it.

    On the theoretical side, your print( IO, "Hello" ) -> IO is just a function that changes the global IO state from one where "Hello" hasn't been printed to one where it has. The number of times the function is evaluated doesn't matter, the resulting IO state is always one where "Hello" has been printed exactly once (and that state is represented by the return value). The function doesn't have a side effect, it's just you observing different IO states from the outside.

    On the practical side, the implementation has to call the underlying method to print something somewhere (probably a syscall) somehow, but that's left up to implementation magic. This magic makes sure that even if the function were evaluated multiple times for practical reasons, the syscall only happens once.

    Edit: Like, the key thing about FP (in my possibly wrong opinion), is that you're not describing a fixed sequence of computations. You're describing how an input maps to an output. Once this description exists, the underlying system evaluates it as it pleases.


  • Banned

    @topspin said in OOP is TRWTF:

    Now, this cannot happen with monads. Why? If you were to replace that with something using IO: a call to factorial(IO, 5) printing stuff on the way somehow has to ensure it happens exactly once.

    The IO that gets returned from the first call is (conceptually) different from the IO passed to the first call, so the second call gets different argument and therefore cannot be memoized with the first.


  • Banned

    @cvi said in OOP is TRWTF:

    Once this description exists, the underlying system evaluates it as it pleases.

    This statement is just as true in imperative languages (by which I mean, not in any useful sense, in either case). The execution environment - in case of compiled programs, the operating system - does whatever it wants with the description (machine code) it gets, which might or might not be what the programmer guessed would happen. And conceptually it is, but physically it isn't (most importantly, memory addresses in process are different from physical memory addresses. Also, interrupts.)


  • Discourse touched me in a no-no place

    @_P_ said in OOP is TRWTF:

    "But what if standard library does funny things!"

    When someone gets that argument out, they're either a developer of the standard library or someone who doesn't understand what they're doing. There are very few cases where those overlap (because standard libraries are difficult to write and don't pay particularly well) and the people who don't fit in either category use the argument so rarely that they're a statistical error. 😉


  • Discourse touched me in a no-no place

    @_P_ said in OOP is TRWTF:

    @dkf said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    all functions can be inlined

    You'll have fun if you try that with a recursively-defined function…

    Don't, he'll pull a "all functions can be TCO-ed" to that. Then your stack trace stops making sense anymore.

    If you insist on your stack traces making perfect sense at all times, you're going to pay for that.


  • Discourse touched me in a no-no place

    @_P_ said in OOP is TRWTF:

    @Gąska I'm talking about the (hypothetical) scenario that IO monad's implementation is outright wrong. Perhaps I should use WTFIO instead to distinguish it from the actual IO :wtf_owl:

    That's a useless supposition. You could just as well postulate a write() system call that outputs its data buffer to the target stream twice or not at all, choosing based on a random noise source. That would foul up a lot of imperative programs too if they were instead expecting the system call to write the buffer exactly once. All we've really learned is that it's important that functions do what they're documented to do, and that this is particularly important for functions that modify persistent state. But Captain Obvious could tell us that for free…


  • Considered Harmful

    @_P_ said in OOP is TRWTF:

    @Zecc said in OOP is TRWTF:

    @Dragoon Sorry, why are you posting this in this thread?

    Wait, which context are we in again?

    We were talking about how multithreading is confusing.



  • @Gąska said in OOP is TRWTF:

    which might or might not be what the programmer guessed would happen

    Let's not pull in the programmers into this discussion. That programs don't always do what their author thinks they should do is really unrelated to what programming paradigm they were following.



  • @topspin said in OOP is TRWTF:

    If I have the following two pure functions (Not Haskell syntax since I CBA to look it up):

    def mul(a, b): return a*b
    def factorial(n): return 1 if n <= 1 else n*factorial(n-1)
    

    then in a statement like mul(factorial(5), factorial(5)) there are exactly two calls to factorial(5) in an imperative language. In a functional language, because factorial is pure, there could be one or arbitrarily more calls. If an earlier call gets memoized, there could even be zero calls to factorial.

    Now, this cannot happen with monads. Why? If you were to replace that with something using IO: a call to factorial(IO, 5) printing stuff on the way somehow has to ensure it happens exactly once.

    Okay, that's clearer.

    Yes, but when you do factorial(IO, 5) your factorial has already been coupled with IO so you cannot talk about its functionality separated from the entire world anymore (the output type IO () means "this has something to do with IO and doesn't return any information"). IO is like a nuke in the sense that it has to do with the entire environment as World, once it is involved anything can happen, including blowing up the universe. So usually we don't unleash IO unless it's needed, e.g if you want memoization, use State instead of IO. And if you don't need anything, don't put IO in the type. At least it's clear from the type that whether there are some IO stuff involved, compared to IO in other languages where every IO action is hidden in the code and not at all apparent.

    But, as far as the implementation goes, the program itself does what you specify by the code perfectly. It's just that the world has failed you. Like the typical TDWTF experience.



  • I feel like the Functional Programming enthusiasts just restate the basic ideas about encapsulation and state that all decent programmers know.

    Of course GetCurrentTime() should not read or touch my global variables. Of course Factorial(int) should behave as a pure function. Of course other classes should not be modifying my state. There might not be language level enforcement for most of those rules, but any programmer still knows about them. No one makes any big "shared mutable state for everyone to touch" class, despite what the author keeps saying. The entire goal of software design is to keep information about state confined to the parts of code that need it.

    Yes, I would like C# to have a pure keyword and a whole bunch of stuff to properly express restrictions like that, but that doesn't mean people aren't writing pure functions whenever it's appropriate already.

    And what's the obsession with state and immutability? How does your FP browser remember which tab it's currently focused on anyway, if it can't have a pseudo-global variable that says currentTab=3? Whichever fancy mechanism you come up with, it's always going to be equivalent to that, because the current tab has to be the same for everyone and it has to be able to change. State is inherent to all code. You can manage it, pass it and hide it however you want but it's always there and it's always mutable.


  • Discourse touched me in a no-no place

    @anonymous234 said in OOP is TRWTF:

    And what's the obsession with state and immutability?

    Immutability significantly simplifies the analysis of the program. OTOH, the transform for converting simple imperative code into code where all variables are immutable is relatively well known.



  • @anonymous234 said in OOP is TRWTF:

    I feel like the Functional Programming enthusiasts just restate the basic ideas about encapsulation and state that all decent programmers know.

    Yes, but so does the OO enthusiasts. And to be fair, most FP people don't really hard-sell it like these FP enthusiasts do; I feel that they only pop up because the concept got traction in JS front-end frameworks because these monkeys have been shitting all over the global scope the entire time. But then you should know better than to listen to them. They even think merely the use of first-class functions and array map/filter/reduce constitutes FP :wtf_owl:

    Pragmatically speaking, FP is a very powerful paradigm to add to the arsenal. But so does OO.



  • @Gąska said in OOP is TRWTF:

    @Zenith said in OOP is TRWTF:

    If I only use static functions and pass structs around, am I doing OOP or not?

    Stuck with C?

    No, C#, I just hate objects with two fields and instance functions that don't operate on the instance (like COM). I also like the Win32 model of using structs instead of a zillion parameters for higher level functionality. So I built a library over all of that to the point that the only objects that I deal with regularly are dictionaries, recordsets, and Windows controls.


  • Considered Harmful

    @_P_ said in OOP is TRWTF:

    they probably think JS has a class OO model too

    Well, it does, now.


  • ♿ (Parody)

    @anonymous234 said in OOP is TRWTF:

    And what's the obsession with state and immutability? How does your FP browser remember which tab it's currently focused on anyway, if it can't have a pseudo-global variable that says currentTab=3? Whichever fancy mechanism you come up with, it's always going to be equivalent to that, because the current tab has to be the same for everyone and it has to be able to change.

    But it will be more complicated to use and no one will be able to explain it properly.


  • Considered Harmful

    @boomzilla said in OOP is TRWTF:

    no one will be able to explain it properly

    It's a weird situation where everyone who has attempted to explain it says something mostly coherent and consistent within their own explanation, but completely different than and incompatible with everyone else's explanation.


    Filed under: It's like asking a Christian what Christianity means 🚎


  • Considered Harmful

    So far, my muddled understanding is this:

    • in a procedural/OOP language, you pass arguments into functions or methods and they return values to you
    • the invocations are directly tied in some way to what is being passed in and out
    • in FP, you define a set of mappings from input types to output types without ever looking at the values
    • because you don't look at the values, the compiler is free to perform lots of weird optimization tricks with the set of rules you have given it
    • a monad is a defined class of value types that functions can operate on, with its own unique rules about how that data can be handled and operations rearranged

    Am I anywhere in the ballpark?

    I'm sort of thinking of "looking at" a value in terms of making an observation of quanta - it collapses the wave function and limits it to a given path. TITS it creates a side-effect.


    Filed under: I'm typing TITS instead of i.e. from now on.



  • @error said in OOP is TRWTF:

    • a monad is a defined class of value types that functions can operate on, with its own unique rules about how that data can be handled and operations rearrangedmonoid in the category of endofunctors

    FTFY


  • Banned

    @anonymous234 said in OOP is TRWTF:

    And what's the obsession with state and immutability?

    Reproducibility, testability, parallelization.

    How does your FP browser remember which tab it's currently focused on anyway, if it can't have a pseudo-global variable that says currentTab=3?

    It does have a pseudo-global state. Its changes just aren't expressed as modifying variables - rather, each modification produces (conceptually, hopefully not actually) a brand new state object identical to old state object except for the changed part.


  • Considered Harmful

    @Gąska said in OOP is TRWTF:

    each modification produces (conceptually, hopefully not actually) a brand new state object identical to old state object except for the changed part.

    This part, as well as a series of idempotent side-effect-free operators being piped together, is heavily used by e.g. Rx, React, and Redux.

    We tend to refer to as "reactive" programming, for raisins. error_bot uses it a lot.


  • Notification Spam Recipient

    @error said in OOP is TRWTF:

    So far, my muddled understanding is this:

    • in a procedural/OOP language, you pass arguments into functions or methods and they return values to you
    • the invocations are directly tied in some way to what is being passed in and out
    • in FP, you define a set of mappings from input types to output types without ever looking at the values
    • because you don't look at the values, the compiler is free to perform lots of weird optimization tricks with the set of rules you have given it
    • a monad is a defined class of value types that functions can operate on, with its own unique rules about how that data can be handled and operations rearranged

    Am I anywhere in the ballpark?

    I'm not sure but I am more confused now if that makes you feel better.


  • Notification Spam Recipient

    @error said in OOP is TRWTF:

    the invocations are directly tied in some way to what is being passed in and out
    in FP, you define a set of mappings from input types to output types without ever looking at the values

    If only this wasn't the basic concept of what interfaces are trying to acheive in Java. Scala would be so great.

    *edit If the post comes off as snarky. It's not directed at you. Just Scala and it's zealots in general.



  • @Gąska The distinction allows different ways of thinking about the actually pure values. (Haskell has a very rich denotational semantics, coming purely from the evaluation model and not at all from the execution model, which is irrelevant to the language definition)

    I get that you don't value that, and want to smear the distinction away, but Haskell programmers do.

    The distinction isn't going away just because you don't like that it's made.


  • ♿ (Parody)

    @error said in OOP is TRWTF:

    So far, my muddled understanding is this:

    • in a procedural/OOP language, you pass arguments into functions or methods and they return values to you
    • the invocations are directly tied in some way to what is being passed in and out
    • in FP, you define a set of mappings from input types to output types without ever looking at the values
    • because you don't look at the values, the compiler is free to perform lots of weird optimization tricks with the set of rules you have given it
    • a monad is a defined class of value types that functions can operate on, with its own unique rules about how that data can be handled and operations rearranged

    Am I anywhere in the ballpark?

    I'm sort of thinking of "looking at" a value in terms of making an observation of quanta - it collapses the wave function and limits it to a given path. TITS it creates a side-effect.

    It's more that the compiler / runtime is free to evaluate that stuff lazily and in whatever order it likes (though obviously it needs to evaluate the parameters for a function before the function itself can execute). But monads impose some ordering on them (among other things). So you're able to have code (as in, consecutive statements in a particular code block) execute in a predictable order, which we tend to take for granted in an imperative context but aren't necessarily defined in a functional context.


  • Considered Harmful

    @boomzilla said in OOP is TRWTF:

    the compiler / runtime is free to evaluate that stuff lazily and in whatever order it likes

    @error said in OOP is TRWTF:

    the compiler is free to perform lots of weird optimization tricks with the set of rules you have given it

    @boomzilla said in OOP is TRWTF:

    monads impose some ordering on them

    @error said in OOP is TRWTF:

    a monad is a defined class of value types that functions can operate on, with its own unique rules about how that data can be handled and operations rearranged

    So, yes?


  • Banned

    @Captain said in OOP is TRWTF:

    @Gąska The distinction allows different ways of thinking about the actually pure values.

    I get that you don't value that

    Maybe I would value it if I knew what it was. So what is this other way of thinking? I assume it's something more than just reordering, pre-evaluating, memoizing and every control flow transformation that you can do by exploiting the fact that some computation is pure, which is the single most common optimization method that all imperative compilers and interpreters use.

    The distinction isn't going away just because you don't like that it's made.

    I know. Which is why I hate people who make distinctions and don't bother explaining why they matter. Because it makes me very aware that there's a big part of the picture that I'm missing and leaves me with zero opportunity to learn what it is.


Log in to reply