4 ms·
When you have some spare time write a function that returns a Maybe<Value, Error> and see what a pain it is to compose multiple of them. Then, try store your p
by exdsq 5y ago
When you have some spare time write a function that returns a Maybe<Value, Error> and see what a pain it is to compose multiple of them.
Then, try store your programs state by passing it as a function argument to every function so it stays available for the lifetime of the program (no mutable state? This is basically what happens)
After realizing what a pain this is and wishing for boilerplate, look at the State monad. This is what did it for me :)
- eternityforest 5y agoI don't see why I would ever want to do the first one in real life, when exceptions exist. I'm honestly not even sure what function composition is, or why you would compose two functions instead of just explicitly making a new function that calls the two original ones. The second one makes more sense because of the rumored bug-freeness of side effect free apps, but it seems like for that too, you'd need to understand a lot more than just monads.
- alexmingoia 5y agoFunction composition is connecting the output of one function to the input of another. In math it is (f ∘ g)(x) = f(g(x)) Or in javascript: function f(x) { return x + 1 } function g(x) { return x + 2 } f(g(1)) === 4 In functional languages like Haskell function composition is made easy, because functions can be partially applied. Partial application means that given a function that takes two arguments, I can call it with one argument and it will return a function that takes one argument which returns the result. Whereas in languages like javascript it is difficult because functions can have different numbers of arguments and there's no way to partially apply them or flip their arguments, so it is difficult to chain functions together. And because javascript functions aren't pure, they may not have any result which can be composed with other functions. In a functional language, functions are more like the functions in math. In most non-functional languages, functions are more like a procedure of commands. For example, the javascript function logMe(x) { console.log(x) } doesn't return anything. So it can't be composed functionally, and is not like a "real" function.
- 8note 5y agoJavaScript has bind() on functions which works well enough. You can partially apply arguments, but I don't think you can project them to a different order.
- eternityforest 5y agoJavaScript has closures. When you want to partially apply something(Which is technically never, because we would probably describe it in different words and think of it differently) we just make a new function. When we want to "compose" two functions, there's f(x){return a(b(x))}. It doesn't matter if the signatures are different, you don't need any nice logical idea of composition, you just explicitly build the new function that does what you want. I would say that is a lot easier than real composition, because real composition requires you first learn to see and think in terms of patterns you would want to compose in the first place. Also, function chaining doesn't use intermediate variables. Named variables are like free documentation. With composition you have to actually remember what you're doing. Making everything explicit makes it easier to comment what's happening. Regular programming is to math as cooking is to chemistry. Functional brings the math back to programming. Math is useful but also hard. In OOP, more often you're doing the equivalent of: if crinkle(a(b(x,68), modes.FOO).bar.baz(9)): x = foo() else: x = wipe(7).bar() And I think it's a lot nicer to just spell it out with meaningful variable names, instead of trying to compose it like an equation.
- Zababa 5y agoNot every step has a meaningful name. And usually you already have the name of the function. For example: let batter = mix(water, flour) let saltedBatter = add(salt, batter) let risenSaltedBatter = letToRise(saltedBatter) let bread = bake(batter) on the other hand: let bread = bake(letToRise(add(salt, mix(water, flour))) The first example leads to awkward names and useless variables. The second example feels cleaner to me. Interesting that you take cooking as an example: recipes usually don't name most of the intermediate steps, they describe the actions you take and the ingredients you use. Most recipes also use composition a lot. You start with your actions/functions (cut, cook, bake, dice, etc) and you do a recipe out of them. You don't create new "functions" (actions) specifically for each recipe.
- 8note 5y agoA function that can return a value or throw an exception is pretty similar to Maybe<value,error> I'm not clear why the next function in the pipeline would need to know about the error rather than the caller, but that happens with promise chains too
- exdsq 5y agoMaybe this article will explain my point about errors a little, it also covers function composition, and I think is approachable. I guess the only advice I have for you (or anyone) with monads is to look at concrete examples first and not care about the theoretical bits. Intuition comes from use. Is it useful to know in non-functional languages? Probably not; you'll use them without realising and probably make them yourself without realising! https://medium.com/swlh/monads-in-python-e3c9592285d6 https://medium.com/swlh/monads-in-python-e3c9592285d6
- Zababa 5y ago> I don't see why I would ever want to do the first one in real life, when exceptions exist. Exceptions usually aren't typechecked.
- eternityforest 5y agoLots of languages only let you throw stuff if explicitly declared.
- Zababa 5y agoWhich ones? I'm not aware of any that has only checked exceptions, at least in the mainstream ones.