Demonstrating by example what e.g. currying actually looks like is much more powerful, at least from my point of view. In that regard, I’m actually pleasantly surprised this guide does a very good job at that.
Demonstrating by example what e.g. currying actually looks like is much more powerful, at least from my point of view. In that regard, I’m actually pleasantly surprised this guide does a very good job at that.
I think in many cases this isnt right, eg., Monads. The reason flatMap() "flattens" is just that "flattening" is really just sequencing, denesting the type using a function requires a sequenced function call: f(g(..))
This applies to many of these "functional design patterns"... theyre just ways of expressing often trivial ideas (such as sequencing) under some constraints.
Instead, it allows for very concise partial function application, as demonstrated by the code. I can just call any function with a subset of its arguments, and it automatically returns a “new function” which you can call with the remainder of the arguments. It allows you to reason about calling functions in a different way.
Currying is one of those concepts done very well in languages such as Haskell (not a coincidence, as both derive their name from Haskell Curry).
But saying it’s a “big hammer to implement variadic function arguments” is like saying that pattern matching is a big hammer to implement a switch statement. Yes, it provides that functionality, but also much more in a very elegant manner.
Pattern matching is a good example of a language feature included because they could not figure out how to provide features expressive enough to be composed to implement the feature in a library.
[1] https://stackoverflow.com/questions/3015019/is-there-a-progr...
Actually... not really? You need the foreign function interface to have anything useful to execute, but (unless you're talking about something else?) the execution model is basically just a State monad carrying a unique magic token, built on top of the same evaluation model as everything else.
Edward Kmett did some work to describe IO as an external interpreter working through a free monad. That approach provides very neat semantics that include multiple threads and FFI easily.
But IO needs something to make it go, and that something needs capabilities that aren't necessary for the evaluation model.
Er, yes: specifically, execution is drivenby trying to pull the final RealWorld token out of a expression along the lines of `runState (RealWorld#) (main)`, which requires the evaluation of (the internal RealWorld -> (RealWorld,a) functions of) IO actions provided by FFI functions (hence why you need FFI to have anything useful (or more accurately, side-effectual) to execute).
> until you need to explain multiple threads
I don't. It's a terrible idea that's effectively the apotheosis of premature (and ill-targeted) optimization as the root of all evil.
I speak many computer languages, and one of the ways I measure them is, "what do I miss from X when using Y?" The answers will often surprise you; the thing you'd swear up and down you'd miss from X you may never think of again if you leave the language, and some feature you hardly consider while you're in the thick of programming in X may turn out to be the thing you miss in every other language for the rest of your life. For me, one of the things I deeply miss from Haskell when programming elsewhere is the fluidity of the use of partial function application through the currying syntax. I see so many people trying to force it out of Haskell into some other language but there just isn't any comparison to the fluidity of
map someFunc . filter other . map (makeMap x y) $ userList
Currying enables that syntax. If you don't see how, I'm not surprised. Sit down and try to write a syntax extension for an Algol-descended language that makes it work that well. Be honest about it. Run some non-trivial expressions through it. What I show above you should consider the minimum level of expression to play with, not the maximum. If you pick something like Python to work in, be sure to consider the interaction with all the types of arguments, like positional, keyword, default, etc. It can absolutely be done, but short of re-importing currying through the back door I guarantee the result is a lot less fluid.The question about "what is special about the first argument" is backwards. The utility of the first argument is something you choose at writing time, not use time. The reason why map's first argument is the function to map on and the second is the list to map over is nothing more and nothing less than most of the time, users of map use it as I show above. There's no deep mathematical or group theory reason. If you don't like it there are simple functions to change the orders around, and the goal of picking function argument orders is just to minimize the amount of such functions in the code. Nothing more, nothing less.
(Though, functional programmers would probably pass up the opportunity and use one-letter variable names.)
let f = makeMap x y
in map someFunc . filter other . map f $ userList
I suspect not as it certainly doesn't seem to improve the situation. Perhaps you could elaborate?Edit to add: I was focused on the "write out the callback given to map using a let block" part and not the meaningful name part, but that's perhaps what's confusing about this to me, as (makeMap x y) seems like the most clear name possible here, what else could you do? This?
let mapOfXy = makeMap x y
in map someFunc . filter other . map mapOfXy $ userList
It only obscures things.In real code I probably would have named it, but example code is always silly.
f(x,y)
to "partially apply" it on x=3 with just: function(y) { return f(3,y); }
?And then I've "partially applied" f. Is this what currying is?
The syntax I posted is ambivalent about which arguments you're partially applying, isn't that superior to only being able to provide the first argument?
f(x,y) = ... // arity 2
f = \x -> \y -> ... // sequence of two unary functions
f x y = ... // more compact representation of the above
Regarding partial application, your JS (?) example is basically what happens under the hood with Haskell, but without the ceremony: f x y = ...
fByThree = f 3
fByThree y = f 3 y
Those last two are equivalent, but you don't have to include the y parameter explicitly, because f 3 returns a function that expects another parameter (like the function you wrote out explicitly).And the Haskell version is more general, since it doesn't require a unique function to be written for each possible partial application. Of course, you can do this in languages with closures:
function fByX(x) {
return y => f(x,y);
}
fByThree = fByX(3);
But in Haskell that extra function isn't needed, it's just already there and called f. Regarding your last statement, there are also combinators in Haskell that allow you to do things like this: fXbyThree = flip f 3
// equivalent to:
fXByThree x = f x 3
// JS
function fXByThree(x) { return f(x,3); }
// or
function fXByFixedY(y) { return x => f(x,y); }
fXByThree = fXByFixedY(3);
So I'm not sure it's strictly superior, it is more explicit though.The end result is you need a lot more noise in the partial call to indicate what it is you are doing, no matter how you slice it, and it interacts poorly with default parameters (which almost every language has nowadays) anyhow.
bind :: m a -> (a -> m b) -> m b
Because that function in the middle takes an `a`, your implementation of `bind` needs to be able to take an `m a` and pull an `a` out of it, which means it also has to evaluate however much of `m` is needed to actually get to that `a`.Because that function in the middle returns an `m b`, binding again with a function `b -> m c` requires you to pull `b` out of `m b`, which in turn forces pulling an `a` out of `m a` to make progress. This is where you force sequentiality — you can only evaluate the `m` in `m b` after you've evaluated the `m` in `m a`
In order to be a functor, you need `fmap (f . g) == fmap f . fmap g`.
The problem is that some functions (for instance, Data.Set.showTree) can produce different results for two values that compare equal.
For instance, imagine we have a set containing (only) the lists `[1,2]` and `[2,1]`; let's call it s. How many elements are in `fmap (Data.Set.showTree . Data.Set.fromList) s`? Two, because the result of fromList winds up being order dependent in the structure of the internal tree, even though both Sets represent the same set of values. On the other hand, how many elements in `fmap showTree . fmap fromList`? Just one, because the Set has a chance to "see" the output of fromList and notice that they are equal according to Eq.
Does this violation of the functor laws matter? Maybe; likely not; it depends on your use case.
But mathematically it means that the thing we are working with is not a functor, and so the thing we are working with is also not a monad (as all monads are functors).
> You have to know of what you're talking.
And the language (/ecosystem) gives you no way of specifying, so we have to be somewhat conservative. I've actually been thinking (partly driven by trains of thought such as under discussion here) that it might be very interesting to have a language that let you reason explicitly about different sorts of equality (tentatively defined, in the context of the language, as indistinguishability relative to some interface).
Good sample code, though. It only required me to look up a handful of things I wasn't already familiar with.
> f is a morphism from a -> b, and g is a morphism from b -> c; g(f(x)) must be equivalent to (g • f)(x)
If I don't already understand (g • f)(x) this is not helpful at all. This other one especially jumped out at me (perhaps because I've personally never seen this "bent equals sign" ≍ before):
> A functor must adhere to two rules:
> Preserves identity
> object.map(x => x) ≍ object
What does that sign ≍ mean? If I don't know, I am no closer to understanding functors. And the sign is not explained anywhere in the document.
I think in the context it just means the result is in the range of "function applied to object"
Normally, `f ≍ g` means `f \in \Theta(g)`. Here, the author is using it as an equals sign. I have no idea why.
1 https://math.stackexchange.com/questions/764897/what-does-th...