6 ms·
These definitions don't really give you the idea, rather often just code examples.. "The ideas", in my view: Monoid = units that can be joined together Funct
by mjburgess 4y ago
These definitions don't really give you the idea, rather often just code examples..
"The ideas", in my view:
Monoid = units that can be joined together
Functor = context for running a single-input function
Applicative = context for multi-input functions
Monad = context for sequence-dependent operations
Lifting = converting from one context to another
Sum type = something is either A or B or C..
Product type = a record = something is both A and B and C
Partial application = defaulting an argument to a function
Currying = passing some arguments later = rephrasing a function to return a functions of n-1 arguments when given 1, st. the final function will compute the desired result
EDIT:
Context = compiler information that changes how the program will be interpreted (, executed, compiled,...)
Eg., context = run in the future, run across a list, redirect the i/o, ...
- stingraycharles 4y agoI completely understand what you’re saying, but assuming that this guide is aimed at people entirely unfamiliar with these concepts, I’m not sure whether these “ideas” provide any meaningful explanation to them. 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.
- mjburgess 4y agoCurrying is one of those cases where the code is the explanation 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.
- ncmncm 4y agoWe need an explanation why we should care about support for currying. Where it is really just to support variadic argument lists, it is a big hammer for a little problem.
- stingraycharles 4y agoAnd precisely because of the code sample, it should be obvious that currying is not the same as variadic function arguments. 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.
- ncmncm 4y agoSaying "it allows for very concise partial function application" does nothing but repeat the definition. It does not, in particular, offer any reason to want that. What is so special about the first argument, that I want to fix it? Why not the third? Why is what I do to fix the third not just as good for the first? 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.
- Arrrlex 4y agoCurrying support is orthogonal to how function arguments work. Some languages (e.g. OCaml according to this question[1]) combine named parameters with currying, allowing partial application with any of the function's arguments. [1] https://stackoverflow.com/questions/3015019/is-there-a-programming-language-that-performs-currying-when-named-parameters-are#3016423 https://stackoverflow.com/questions/3015019/is-there-a-progr...
- chowells 4y agoIn a language like Haskell, pattern matching was explicitly chosen to be a primitive operation. It's not that there's no way to put it in a library; it's that it was chosen to be one of the small set of ideas everything else is described in terms of. Along with allocation and function application, you've got the entirety of Haskell's evaluation model. (Note: not execution model. That needs a bit more.) Having such a small evaluation model probably should be taken as evidence the primitives were chosen well.
- urxvtcd 4y agoWhile I _think_ I understand monads on some rudimentary level through how join and bind operate, your "just sequencing" doesn't tell me anything. And this is a problem with a lot of these texts. Maybe that's trivial to the writers, but it makes me feel even dumber when I cannot understand a concept I already understand, lol.
- pdpi 4y agoThis is one of those things where looking at the type signature hard enough eventually gives the game away, but most writing on it sucks: 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`
- skybrian 4y agoCaveat: there is the trivial case of calling the middle function zero times. Which does happen (in the case of a Maybe type) but presumably any useful code needs to be able to call the function sometimes.
- samiskin 4y agoThis makes a lot more sense than anything I've read about this in the past, thanks for the explanation!
- pdpi 4y agoThanks, that's reassuring. I've long been frustrated by just how bad monad posts are in general, and been meaning to write something up about it that makes it clearer. It's good to know that I'm at least going in the right direction :)
- 4y ago
- wawjgreen 4y ago
- cratermoon 4y agoAssuming that this guide is aimed at people entirely unfamiliar with these concepts, I think it fails to explain the concept. Yes, it is plain English, but it is not simple or common English, and it's mostly code examples. I'm familiar with the concepts, but not fluent, and functional programming jargon I already knew is not clearer to me now than it was before I read it. Good sample code, though. It only required me to look up a handful of things I wasn't already familiar with.
- b3morales 4y agoI agree, and not just the English. There is use of particular notation that does not explain anything unless you already know what it means: > 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.
- cratermoon 4y agoOh boy. I didn't put on my glasses to read it so I didn't notice that ≍ is not = and that's important. I think in the context it just means the result is in the range of "function applied to object"
- hither_shores 4y ago> What does that sign ≍ mean? Normally, `f ≍ g` means `f \in \Theta(g)`. Here, the author is using it as an equals sign. I have no idea why.
- cratermoon 4y agoExcept it's not and =, it's a ≍ sign, which looks almost identical. I'm not a mathematician and I don't know the deep theory behind functional programming, but the internet tells me that ≍ in Graham, Knuth, and Patashnik's Concrete Mathematics it's defined to mean the same thing as "Big Θ"[1], as you note. 1 https://math.stackexchange.com/questions/764897/what-does-this-asymp-symbol-mean-subject-analytic-number-theory https://math.stackexchange.com/questions/764897/what-does-th...
- jmgimeno 4y agoYes, but you're relying on the undefined concept of context. How do you define context?
- mjburgess 4y ago"The idea" practically, is whatever resolves the polymorphism. So context is a compiler function from syntax->semantics. Eg., `+` is polymorphic, `1 + 1` and `"1" + "1"`... the context is whatever implicitly resolves `+` to `ADD` or to `CONCAT`. The utility of these ideas is, in practice, just they provide a syntax for different kinds of polymorphism. Eg., for monads, we're basically just telling the compiler to change its implementation of `;`
- shikoba 4y ago> Functor = context for running a single-input function No a functor is a "function" over types that can transport the arrows: (A -> B) -> (F A -> F B) for a covariant functor (A -> B) -> (F B -> F A) for a contravariant functor With the sum and product there is also the exponential: B^A = functions from A to B
- mjburgess 4y agoIn my simple language, `F` is that context. And (A->B) is a single-argument function. // Int -> Float oneArgFn(x) = x + 1.1 // List of Int -> List of Float ctxOneArgFn(xs) = ListFunctor(oneArgFn, xs) //ie., map()
- shikoba 4y agoYou're just reducing my idea to a specific case. You missed the generalized idea.
- reuben364 4y agoSingle-argument doesn't capture the difference between functor and applicative. ex1 :: Functor f => f (Int, Int) -> f Int ex1 = map (uncurry (+)) Applies a multi-argument function within a functor. What applicative allows you do is take a product of contexts to a context of products, and lift a value to a context. pure :: a -> f a zip :: (Applicative f) => (f a, f b) -> f (a, b) This can then be used to make ex1 into a multi-argument function of contexts, which is not the same as (uncurry (+)) being a multi-argument function ex2 :: (Functor f) => (f Int, f Int) -> f Int ex2 = ex1 . zip
- mjburgess 4y agoSure, it's a particular interpretation of `(f a, f b) -> f (a, b)` (a, b) is an a -> b (f a, f b) is an f a -> f b Applicative, over an above this, is really just useful when you have (f a, f b, f c, ..)
- init0 4y agoSend a PR to fix it!
- HelloNurse 4y agoA product type, named after Cartesian product, contains other objects of various types, but it is none of A, B or C itself. If types A, B and C have respectively a, b and c distinct values, their sum type has a+b+c values (each value of each type is allowed) and their product type has abc values (the part of the value that belongs to each type can be chosen independently).
- mgaunard 4y agoCurrying is just systematic partial function application for each argument.
- init0 4y agoSend a PR to the repo!
- yowlingcat 4y agoI find your list a lot more helpful (and succinct) than the linked repo in the thread topic. Kudos! Perhaps you should make your own repository -- I would upvote it!
- irrational 4y agoWhat is a unit? What does it mean to join together units? What is a context? What is a single input function? A function with a single parameter? Does multi input mean multiple parameters?