Live data from Hacker News

Functor, Applicative, and Monad

typeslogicscats.gitlab.io

91–100 of 125 posts

Re: Functor, Applicative, and Monad

#91
post #39
post #5

Pretty weird that a comment linking a chapter from an oft referred to haskell text is a dead comment here. Surely if the alternate explantion in "Learn You a haskell for great good" is somehow sub-optimal it would be better to explain how rather than kill the comment inside 10 minutes? You see this sort of thing from language warriors fighting silly wars but, yeah, what's wrong with Learn You a Haskell? Why must it b…

It was probably automated. larusso has only made two comments ever, both containing links, so that probably triggered the spam detectors. You can manually resurrect for accidentally dead comments like this by clicking on the comment's time and then clicking "vouch".

I’m personally more the consumer type here at hacker news.

Re: Functor, Applicative, and Monad

#92
post #32

Earlier quoted context omitted.

> And therefore, it's perfectly acceptable to say "I know how monads work with Maybe and List, but not Reader" Because fundamentally, how a Reader implements bind is in no way related to how Maybe or List implement bind. If this is indeed true, what is the point of learning these "patterns"? From a mechanical understanding of the signature of bind and unit, you'd arrive at more sophisticated signatures, say filterM,…

Your example, filterM has the type filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a] from this I can tell you conclusively what it does: it takes a monadic function that returns a Bool after performing some "action" as well as a list of values; it then performs this action on each element and look at whether the result is True or False; when True, keep it otherwise discard it. Now this is already a lot of inf…

"Conclusively" is probably a little strong. You're relying not just on the type, but also the name - the type alone isn't enough here, even without resorting to bottom, and even when requiring every piece be meaningfully used. For instance, with the same type we can write:

    doubleIf :: Applicative m => (a -> m Bool) -> [a] -> m [a]
    doubleIf f values = case values of
        [] -> pure []
        x:xs -> prepend x  f x  doubleIf f xs
      where
        prepend :: a -> Bool -> [a] -> [a]
        prepend x True xs = x:x:xs
        prepend x False xs = x:xs

Re: Functor, Applicative, and Monad

#93

Earlier quoted context omitted.

This is correct, and in contrast to Haskell, where monads are a core part of the language (they are used in the definition of do-notation and list comprehensions) and currently the mainstream way to do IO.

This is misleading. There's no place in the specification of Haskell that specifically defines monads as part of the language. They aren't a language feature. They are a pattern which happens to be expressible in Haskell, and which is supported by libraries: "Haskell's built in support for monads is split among the standard prelude, which exports the most common monad functions, and the Monad module, which contains l…

The Haskell 2010 Report mentions monads all over the place, both in the language and library sections. A Monad instance is literally the only way to do IO in standard Haskell. Monads get special syntax in the form of do-notation and, in an extension, monad comprehensions. It's undeniably a core feature of the language.

Re: Functor, Applicative, and Monad

#94
post #35

I don't think this article is very useful. It doesn't adequately provide an introduction to OCaml code (or adequately explain what a given code snipped is doing) and yet frequently defers to just code to explain a concept. It's an unrealistic expectation to expect an unfamiliar reader to simultaneously infer what a particular code snippet is doing then also go a level deeper and understand the concept that is trying…

OCaml (Reason) was my entry point into Typed FP (after a few failed attempts at learning Haskell). The best thing about OCaml is that it allows side-effects, so one can go a long way without having to touch advanced FP. It is only recently that I've started getting comfortable with concepts cogently explained in this post, so to me it is quite valuable. As an aside, I think OCaml/Reason/Elm should become the de-facto…

I learned typed FP from Haskell, coming from JS, and I'm glad I started there.

It was a bit of a slog, but there are some wonderful books out there. I don't think we should discount the amount of pedagological resources that are available in the language. My favorite is the "First Principles" book.

As a side effect, I found that once I had learned most of Haskell (minus some of the language extensions aimed at type level programming). You pretty much won't find anything in any other typed language that will surprise you.

Re: Functor, Applicative, and Monad

#96
post #85

Earlier quoted context omitted.

Seems similar to type traits in Rust. I skimmed through "Category Theory for Programmers" and it's mentioned this thing is mainly about composition. Composing functions? So you've got a trait (say Functor) that is meant for functions. You know something about functions that have this trait, so you can exploit that knowledge to create compositions of those functions. So then in theory you can write algorithms with as…

Rust traits are pretty much typeclasses. I don't agree with "this thing is mainly about composition" so I don't know what to tell you there. Functor isn't meant for functions, it's meant for types, or rather type constructors (types of kind * -> *). Higher-kinded types are just "what if type parameters could be parameterized types" - I'd argue that from a certain perspective a language that allows them is simpler tha…

That's pretty cool! Too bad Rust doesn't support higher-kinded types...

Re: Functor, Applicative, and Monad

#97
post #32

Earlier quoted context omitted.

Your example, filterM has the type filterM :: Applicative m => (a -> m Bool) -> [a] -> m [a] from this I can tell you conclusively what it does: it takes a monadic function that returns a Bool after performing some "action" as well as a list of values; it then performs this action on each element and look at whether the result is True or False; when True, keep it otherwise discard it. Now this is already a lot of inf…

"Conclusively" is probably a little strong. You're relying not just on the type, but also the name - the type alone isn't enough here, even without resorting to bottom, and even when requiring every piece be meaningfully used. For instance, with the same type we can write: doubleIf :: Applicative m => (a -> m Bool) -> [a] -> m [a] doubleIf f values = case values of [] -> pure [] x:xs -> prepend x f x doubleIf f xs wh…

Maybe a simpler way to illustrate this is two different functions built off of filter:

`keep`: Given a predicate and a collection, filter the collection to retain only items for which predicate evaluates to true.

`discard`: Given a predicate and a collection, filter the collection to discard items for which the predicate evaluates to false.

These are two very reasonable functions that would have the exact same signature, would be appropriate for the same arguments, but have two different behaviors!

Re: Functor, Applicative, and Monad

#98

I don't think this article is very useful. It doesn't adequately provide an introduction to OCaml code (or adequately explain what a given code snipped is doing) and yet frequently defers to just code to explain a concept. It's an unrealistic expectation to expect an unfamiliar reader to simultaneously infer what a particular code snippet is doing then also go a level deeper and understand the concept that is trying…

I majored in math so I was already familiar with some of the concepts, but OCaml is so different from any language I’ve worked in I couldn’t understand how any of it was being applied.

Re: Functor, Applicative, and Monad

#99
post #94
post #35

Earlier quoted context omitted.

OCaml (Reason) was my entry point into Typed FP (after a few failed attempts at learning Haskell). The best thing about OCaml is that it allows side-effects, so one can go a long way without having to touch advanced FP. It is only recently that I've started getting comfortable with concepts cogently explained in this post, so to me it is quite valuable. As an aside, I think OCaml/Reason/Elm should become the de-facto…

I learned typed FP from Haskell, coming from JS, and I'm glad I started there. It was a bit of a slog, but there are some wonderful books out there. I don't think we should discount the amount of pedagological resources that are available in the language. My favorite is the "First Principles" book. As a side effect, I found that once I had learned most of Haskell (minus some of the language extensions aimed at type l…

I understand your point of view and I also don't think my understanding of Typed FP would be complete without sufficient immersion in Haskell. But even with the learning materials, it is too steep a climb for beginner programmers for whom the love of the subject is secondary to their day jobs and regular life.

For people from dynamically typed or static OO background, being able to write functional code with records and sum types alone is a huge quality of life improvement. They should be able to get there with the least amount of effort. Haskell however demands far too much understanding and effort before it can be confidently used in a commercial setting. That excludes mainstream programmers who could otherwise most benefit from the paradigm.

Re: Functor, Applicative, and Monad

#100
post #28

Earlier quoted context omitted.

The point is that Monad, Applicative and Functor are well defined interfaces with laws (properties) you can count on. They are in fact much better, more precisely defined than classic design patterns. And in expressive programming languages (that support higher kinded types or that at least let you encode such types) you can also describe generic code that works over any applicative or monadic type. Having reusable f…

> Having reusable functions that work just as well on lists, maybe/option, reader, io / promise or what have you means these type classes do a very good job at abstracting over data types. These are the purest forms of abstraction. This is where I get kinda lost. Can you give an example of such a reusable function that works for all these things which does something valuable?

Sure. Let's look at alterF for the Map type in the containers library:

http://hackage.haskell.org/package/containers-0.6.2.1/docs/D...

It has the type:

    alterF :: (Functor f, Ord k) => (Maybe a -> f (Maybe a)) -> k -> Map k a -> f (Map k a)
Note that it works for all instances of Functor. Trivial choices allow this to reduce to simple things like insert or lookup:

    insert :: Ord k => k -> a -> Map k a -> Map k a
    insert key val map = runIdentity (alterF (\_ -> Identity (Just val)) key map)

    lookup :: Ord k => k -> Map k a -> Maybe a
    lookup key map = getConst (alterF Const key map)
But those aren't really compelling examples because they just reproduce simpler functionality. It starts to pay off when you start compounding requirements, though. What if you need to insert a value and return the previous one, if it existed?

    insertAndReturnOld :: Ord k => k -> a -> Map k a -> (Maybe c, Map k a)
    insertAndReturnOld key val map = alterF (\old -> (old, Just val)) key map
OK, those are all fine and good, but they're still barely scratching the surface. What if you had problem where when you had a value to insert and there was a previous value at the same key, it was ambiguous which you should use? And let's say the structure of the problem provides interdepencies which restrict the options such that you can't just stack up a list of every possibility for each key. So ideally, you'd like a way to model "insert this value, but if something was already present at this key, give me both possible maps back". Turns out alterF can do that!

    insertNonDet :: Ord k => k -> a -> Map k a -> [Map k a]
    insertNonDet key val map = alterF (maybe [Just val] (\old -> [Just old, Just Val])) key map
Fun facts with that one - thanks to using Functor, it only traverses the tree once, whether it returns one or two results. And thanks to Map being a persistent data type, returning two results only takes O(log n) space more than returning one.

(note: this is all typed on my phone without a compiler to verify. There might be simple mistakes in the above, but it's all conceptually sound.)

That's all still just the start. You can insert an IO operation on the old value to calculate the new one, and it still only traverses the data structure once. Or a huge number of other things. Anything that can be made into a Functor can be used to augment the operation alterF does.

And you know the best part of all this? Despite all that freedom, the type of alterF tells you that you can't change the key associated with a value and even that you can't operate on more than one value in the map. It really is nice when simple things give you lots of options, but make it clear what they don't offer.

Post reply on HN