Skip to content

Unintuitive Functor and Applicative instances #54

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rihardsk opened this issue Jun 30, 2018 · 8 comments
Closed

Unintuitive Functor and Applicative instances #54

rihardsk opened this issue Jun 30, 2018 · 8 comments

Comments

@rihardsk
Copy link

I had expected that S.map and fmap would work the same. E.g., to my mind these should have been equivalent:

x = S.map (+1) $ S.each [1..10]
x' = fmap (+1) $ S.each [1..10] -- this gives a compiler error

Because that's what happens to lists with map and fmap. Instead fmap only affects the result of the stream.

Similarly, with the Applicative instance:

y = S.zipWith (/) (S.each [1..10]) (S.each [2..11])
y' = (/) <$> S.each [1..10] <*> S.each [2..11] -- this gives a compiler error

What is the motivation behind such an implementation? There's certainly a downside for me here since, it seems, I can't have a general function working on Functors that would do the same thing for lists and streams.

@ocharles
Copy link
Contributor

ocharles commented Jul 1, 2018

It's not possible to have fmap and Applicative to do what you want because the kinds don't match. Applicative combines the last type variable, but in Stream the elements are (part of) the first type variable:

Stream (Of a) m r
           ^    ^
           |    `--- This is what `Functor` and `Applicative` uses
           `--- This is what S.map/S.zipWith uses

Then note what you get from S.each:

each :: f a -> Stream (Of a) m () 

Hopefully there it's clear why fmap (+1) isn't going to work - fmap operates on the () that each returns, not the Of a stream type.

@danidiaz
Copy link
Contributor

danidiaz commented Jul 1, 2018

The pipes library includes a newtype that shuffles the Producer type variables around and is a proper ListT: http://hackage.haskell.org/package/pipes-4.3.9/docs/Pipes.html#t:ListT

@treeowl
Copy link
Contributor

treeowl commented Jul 1, 2018

We could add a proper ListT, although the original author of this package wasn't a fan.

@rihardsk
Copy link
Author

rihardsk commented Jul 3, 2018

@ocharles yup, it's clear that Functor and Applicative work on the result part of the type. My question was about the reasoning behind this - why do the instances work this way? My argument was that at least on the surface it seems it would make more sense to have it work like it does for lists (at least in my usecase where i have some functions on Functors that I would like to use with Streams).

I think that a newtype with a changed behavior would indeed be useful.

@chessai
Copy link
Member

chessai commented Oct 3, 2018

see PR #71 . A few small questions posed there; would love your input.

@chessai
Copy link
Member

chessai commented Jun 3, 2019

I'm not sure this even needs to be documented. If you understand haskell's kind system, then this makes perfect sense. Should this be closed?

@andrewthad
Copy link
Contributor

I think it would be fine attach documentation to the Functor instance of Stream. Something like "Operates covariantly on the stream result, not on its elements".

@chessai
Copy link
Member

chessai commented Jun 3, 2019

see #81

@chessai chessai closed this as completed Jun 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants