5 ms·
Functors are type constructors, not functions (which map values). But you could be forgiven the misunderstanding, because it is easy to trip over this distincti
by jkhdigital 5y ago
Functors are type constructors, not functions (which map values). But you could be forgiven the misunderstanding, because it is easy to trip over this distinction given the identical syntax.
So, to make this absolutely clear, a monad is a type constructor in functional programming. This is why I personally prefer the more “creative” explanations of monads, because they attempt to explain what the monad is doing to the original type: adding context, or the possibility of a null value, or multiple values, or a probability distribution over values, etc.
- epolanski 5y agoI don't like creative explanations. Definitions are easier. A functor is a PAIR, please notice the emphasis, of a type constructor F and a map function with the following signature: a -> b -> f a -> f b. Its not a type constructor, it's a pair of two things, the type constructor AND its map function. Now that we said what it is we provide the usual simple example of Array, Record or Option, show the type constructors and show how we map them. Is it really so hard to grasp starting from definitions rather than examples and contrived blogs? I wish people understood that fp is much easier to understand by reading definitions.
- brundolf 5y ago> I wish people understood that fp is much easier to understand by reading definitions. The real landscape of its adoption begs to differ. For the great majority of people, examples and analogies are fantastically more efficient for communicating ideas than building up from first principles. You get to re-use existing mental machinery, intuition, etc, until enough experience has been gained to refine it.
- epolanski 5y agoWhat do you mean?
- brundolf 5y agoThese concepts are usually explained via first-principles definitions, and they are usually opaque and unapproachable for most people. That's not a coincidence.
- epolanski 5y agoThere's nothing in this list: - Algebra: Semigroups, Monoids. - Algebraic Data Types: product and sum types. - Functor, Applicative, Monad which covers basically all the fundamentals to be proficient in functional programming that has definitions that are opaque and unapproachable. And all of those have very simple examples, both in real life and especially programming. But definition has to come first, or you have this great mass of people that do not understand what they talking about even when they blog about it. If you can take your time to understand that a type constructor (and take the time to see what it is) and a map function form a pair called functor, you are equipped to learn about monads. It's not rocket science, but yes, it requires a bit of studying and learning, which is what people don't want to hear, and instead choose the long road of contrived example-driven, inaccurate and confusing blog posts.
- brundolf 5y agoAs another commenter pointed out, the key is to use an example as a starting point and then refine from there. "X is like Y, but different in these ways". Comparisons don't muddy the waters; they give people a quantum-leap from 0% understanding to 70% or 90% understanding, from which point the remaining gap can be filled in at the usual rate. To ignore this is to ignore human nature.
- Twisol 5y ago> `a -> b -> f a -> f b` (a -> b) -> F a -> F b; the parentheses are critical. I don't fully disagree with your point, but if you're going to emphasize rigor and definitions, it would be wise to make sure you're not setting people on the fence up for mistakes akin to those in the original post. (You also called it `F` and then used it as `f`.)
- Littlebigo 5y agoTo add, I suspect that the Haskell type notation is not intuitive to people not familiar with it. For anyone wondering, (a -> b) -> f a -> f b means "A function taking a function from one type to another "(a -> b)" and a functor applied with one type "(f a)" and returns a functor applied with another type "(f b)".
- Twisol 5y agoI do think familiarity plays a big role. I find the Haskell syntax much more pleasant than Java, even though I'm perfectly familiar with both. The equivalent signature in Java (if we could abstract over type functions) would be: interface Functor<F<_>> { <A, B> Function<F<A>, F<B>> map(Function<A, B> f); } You'll find very similar in Scala's `cats`, up to some currying and reordering. [0] [0] https://typelevel.org/cats/typeclasses/functor.html https://typelevel.org/cats/typeclasses/functor.html
- Littlebigo 5y agoI absolutely agree, I was just adding the explanation for anyone unfamiliar with the syntax.
- Twisol 5y agoAnd I was building on yours :) I hope you didn't mind.
- kortex 5y ago> I wish people understood that fp is much easier to understand by reading definitions. I wish more FP writers understood that not everyone has the same neurotype, and "easier to understand by reading definitions" is far from a universal statement.
- zabzonk 5y ago> a monad is a type constructor Now explain what a "type constructor" is, otherwise these words are worthless.
- hikarudo 5y agoA type constructor is something that takes a type and gives you a new type. For example, in C, if you have any type Foo, the pointer operator * will give you a new type Foo*. Another example: in Haskell, Maybe is a type constructor, since it takes a type parameter a (Maybe a). So 'Maybe' is a type constructor, but 'Maybe Int' is a type. So a type constructor is like a function, but it operates on types, not on values.
- brundolf 5y agoSo this is a perfect example of how things could be better-explained to the average developer via analogy: "You can think of a type constructor like a generic type, particularly the way those work in languages like TypeScript" Edit: The C example was added after my initial comment, and is better than nothing, though I'd tend to say the pointer example is a bit of a weird one to go with. Of course intuitiveness is relative and I'm not saying an example has to mention TypeScript or whatever to be valid. But I do think, like, C++ templates would be a somewhat clearer comparison to make for that audience.
- hikarudo 5y agoGood point. A generic type is a great example of a type constructor.
- zabzonk 5y ago> the pointer operator * Do you mean declarator?, As in: Foo * f; // f is a pointer to a Foo The operator dereferences something - it certainly does not create a new type. And neither does the above - the concept of pointers to addressable types is innate to the C and C++ type systems - if you create a type Foo, you get pointers to Foo for free.
- 6nf 5y ago>Functors are type constructors So why can't we just call the damn things 'Type constructors'?
- catgary 5y agoNot every type constructor is a functor.
- 6nf 5y agoWhat makes a type constructor a functor, what's the difference
- Twisol 5y agoIn particular, a functor is a type constructor F together with a higher-order function deriving transformations `F T -> F S` given a transformation `T -> S`, subject to some semantic requirements. This is the `map` or `fmap` people refer to as defining the functor. Unfortunately, due to abuse of notation, the type constructor isn't given much focus. But it's true that "type constructor" is the first conceptual hurdle.
- 6nf 5y agoSee this is exactly the point where FP fans lose the rest of us.
- catgary 5y agoIt’s a type constructor that extends to functions. It’s a functor.
- Twisol 5y agoYou asked. I'm happy to dissect it to your level, but I don't know what you may be familiar with, and if you're not comfortable with type functions, I'm going to have a hard time motivating the extra bits a functor has. The List functor is a combination of the type function sending `T` to `List<T>` and the ... what do you want me to call it, a function function? implemented in Java as: static <T, S> Function<List<T>, List<S>> map(Function<T, S> f) { return (ts) -> { var ss = new ArrayList<S>(); for (final var t : ts) ss.add(f.apply(t)); return ss; }; } We call it as map(f).apply(ts). If we want to pass them both at once, we can rewrite the function so that it's not static, but it doesn't much change that it's a mapping operation. But it's not the `map` method that's the important part, so much as the idea that we can (or want to; can't in Java) pass `List` to any function that needs a Functor, which can then use whatever `map` implementation that functor has, without knowing what actual type function it was given. Really, I suspect it's neither the type constructor nor the map function that's the conceptual hurdle. It's abstracting over the type constructor; taking a type constructor as a parameter. You may have never used a language that supports that, or seen a need for it, and this may all seem like needless sophistry until you do.
- SNosTrAnDbLe 5y agoSo monad is used to construct a new Type dynamically instead of writing it? And functor is also the same?
- lmm 5y agoNot exactly dynamically - both a monad and a functor have to be a kind of "function" from type to type. The most common kind is a generic type with one type parameter. (e.g. you can see "List" as the "constructor" that takes "String" to "List<String>", takes "Int" to "List<Int>" and so on)
- SNosTrAnDbLe 5y agoMany thanks!! So is it something like Class<List<String>> thisistypeconstructor(Class<String> strClass) And if I generify the types above, maybe something like <OutputType> thisistypeconstructor(<InputType>)
- lmm 5y agoNot quite - the type constructor is the thing that does the same thing with the types themselves, not with Class (which is sort-of-but-not-really a way to represent types as values). In Java or C# there's no way to actually represent a type constructor (e.g. you can't have a class that takes a type parameter F that could be List or Set, and then you use that type to form F<String> within the class itself and it will be List<String> or Set<String> depending on whether you do new MyClass<List> or new MyClass<Set>), but other languages are more powerful in what they let you do with types. Look up "higher-kinded types" for the general concept.
- gpderetta 5y agoFor example in C++: template<template<class> class F, class T> using apply = F<T>; static_assert(is_same_v<apply<list, string>, list<string> >);