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

List.map2 unexpected behavior #355

Closed
Fresheyeball opened this Issue Aug 17, 2015 · 8 comments

Comments

Projects
None yet
3 participants
@Fresheyeball

Fresheyeball commented Aug 17, 2015

Maybe this is intentional but:

-- haskell
[5,6,6,7,7,8] = (+) <$> [1,2,3] <*> [4,5]
-- elm
[5, 7] = List.map2 (+) [1,2,3] [4,5]

obviously [5,6,6,7,7,8] /= [5,7], why? Is there a utility function to provide the traditional lift behavior?

@jvoigtlaender

This comment has been minimized.

Show comment
Hide comment
@jvoigtlaender

jvoigtlaender Aug 17, 2015

Contributor

As to why:

  • The Applicative instance for standard lists in Haskell is this.
  • But there's also an Applicative instance for so-called ZipLists, here.
  • Of the two, Elm chooses the ZipList version (because, I think, it's the one that is analogous to the Applicative instance for Signal).

Continuing your example, in Haskell you would have:

ZipList [5,7] = (+) <$> ZipList [1,2,3] <*> ZipList [4,5]
Contributor

jvoigtlaender commented Aug 17, 2015

As to why:

  • The Applicative instance for standard lists in Haskell is this.
  • But there's also an Applicative instance for so-called ZipLists, here.
  • Of the two, Elm chooses the ZipList version (because, I think, it's the one that is analogous to the Applicative instance for Signal).

Continuing your example, in Haskell you would have:

ZipList [5,7] = (+) <$> ZipList [1,2,3] <*> ZipList [4,5]
@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Aug 17, 2015

Member

I think this library does what you want.

The idea is that the "exploring all possibilities" version of things is way less common in practice. I have still not used lists that way in a real life Haskell program after like 5+ years.

Member

evancz commented Aug 17, 2015

I think this library does what you want.

The idea is that the "exploring all possibilities" version of things is way less common in practice. I have still not used lists that way in a real life Haskell program after like 5+ years.

@evancz evancz closed this Aug 17, 2015

@Fresheyeball

This comment has been minimized.

Show comment
Hide comment
@Fresheyeball

Fresheyeball Aug 17, 2015

Hmm, I thought the definition of Lists as a Applicative Functor needed to consistent with List as a Monad.
Doesn't this break that? It may be more practical in practice, but it feels unsound (I'm not actually a math guy). NDet also doesn't seem like the appropriate place for this, as the result should be deterministic. Believe it or not, I use this functionality somewhat regularly. Maybe it should be added to List.Extra?

Fresheyeball commented Aug 17, 2015

Hmm, I thought the definition of Lists as a Applicative Functor needed to consistent with List as a Monad.
Doesn't this break that? It may be more practical in practice, but it feels unsound (I'm not actually a math guy). NDet also doesn't seem like the appropriate place for this, as the result should be deterministic. Believe it or not, I use this functionality somewhat regularly. Maybe it should be added to List.Extra?

@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Aug 17, 2015

Member

Maybe talk to the List.Extra folks. There are some discussions on the list about how the applicative laws are not followed exactly with how it works here. I think both sides of this are explained there if you can find it.

Not looking to change this really. The fact that something can be seen as an Applicative Functor does not necessarily mean that's the best API.

Member

evancz commented Aug 17, 2015

Maybe talk to the List.Extra folks. There are some discussions on the list about how the applicative laws are not followed exactly with how it works here. I think both sides of this are explained there if you can find it.

Not looking to change this really. The fact that something can be seen as an Applicative Functor does not necessarily mean that's the best API.

@Fresheyeball

This comment has been minimized.

Show comment
Hide comment
@Fresheyeball

Fresheyeball Aug 18, 2015

Fair enough. I submitted a PR to List.Exta. We will see what they say. Overall I've enjoyed how elm tends to fall on the side of good api in good api vs math when those are in conflict (Error over Either ect). But I feel like the true math thingies still need a home.

https://github.com/circuithub/elm-list-extra/pull/12

Fresheyeball commented Aug 18, 2015

Fair enough. I submitted a PR to List.Exta. We will see what they say. Overall I've enjoyed how elm tends to fall on the side of good api in good api vs math when those are in conflict (Error over Either ect). But I feel like the true math thingies still need a home.

https://github.com/circuithub/elm-list-extra/pull/12

@jvoigtlaender

This comment has been minimized.

Show comment
Hide comment
@jvoigtlaender

jvoigtlaender Aug 18, 2015

Contributor

@Fresheyeball, saying "Lists as a Applicative Functor needed to consistent with List as a Monad" isn't very relevant when there is no Monad instance for Lists (in Elm). Also, as observed already in the original "Applicative Functors" paper, a type constructor (like List) can have more than one useful Applicative Functor instance. I think they even use the above example with the two different ways of making lists Applicative in Haskell:

[5,6,6,7,7,8] = (+) <$> [1,2,3] <*> [4,5]
ZipList [5,7] = (+) <$> ZipList [1,2,3] <*> ZipList [4,5]

Only one of the two (the first) has a corresponding Monad instance. Because none can exist for the second. That doesn't mean the second one is not a useful Applicative instance. There is also nothing unsound about it. It satisfies all the laws expected of Applicatives. (I don't understand Evan's comment about "how the applicative laws are not followed exactly with how it works here". I think they are followed just fine.)

About NDet and determinism, yes, of course the resulting list is a deterministic value. But it models nondetderminism. After all, the "List as a Monad" you mention is typically called the nondeterminism monad.

Contributor

jvoigtlaender commented Aug 18, 2015

@Fresheyeball, saying "Lists as a Applicative Functor needed to consistent with List as a Monad" isn't very relevant when there is no Monad instance for Lists (in Elm). Also, as observed already in the original "Applicative Functors" paper, a type constructor (like List) can have more than one useful Applicative Functor instance. I think they even use the above example with the two different ways of making lists Applicative in Haskell:

[5,6,6,7,7,8] = (+) <$> [1,2,3] <*> [4,5]
ZipList [5,7] = (+) <$> ZipList [1,2,3] <*> ZipList [4,5]

Only one of the two (the first) has a corresponding Monad instance. Because none can exist for the second. That doesn't mean the second one is not a useful Applicative instance. There is also nothing unsound about it. It satisfies all the laws expected of Applicatives. (I don't understand Evan's comment about "how the applicative laws are not followed exactly with how it works here". I think they are followed just fine.)

About NDet and determinism, yes, of course the resulting list is a deterministic value. But it models nondetderminism. After all, the "List as a Monad" you mention is typically called the nondeterminism monad.

@Fresheyeball

This comment has been minimized.

Show comment
Hide comment
@Fresheyeball

Fresheyeball Aug 18, 2015

@jvoigtlaender fascinating! That was all news to me, like I said, I'm not a math guy. So... it sounds like both of the examples you provided are useful and valid Applicative instances, and I was more just accustomed to the first one because I think of lists as Monads and mentally work backward to Applicative.

If this was a system with type-classes where there is an Applicative class and a Monad class, I expect them to align, making the ZipList version a newtype. But this is Elm, and it seems fair that the ZipList version is more useful in practice. It all seems good to me. @circuithub already merged my PR with the other instance.

If you are willing to humor me. Why is the List Monad called the nondeterminism monad when the values produced are deterministic?

Fresheyeball commented Aug 18, 2015

@jvoigtlaender fascinating! That was all news to me, like I said, I'm not a math guy. So... it sounds like both of the examples you provided are useful and valid Applicative instances, and I was more just accustomed to the first one because I think of lists as Monads and mentally work backward to Applicative.

If this was a system with type-classes where there is an Applicative class and a Monad class, I expect them to align, making the ZipList version a newtype. But this is Elm, and it seems fair that the ZipList version is more useful in practice. It all seems good to me. @circuithub already merged my PR with the other instance.

If you are willing to humor me. Why is the List Monad called the nondeterminism monad when the values produced are deterministic?

@jvoigtlaender

This comment has been minimized.

Show comment
Hide comment
@jvoigtlaender

jvoigtlaender Aug 18, 2015

Contributor

About "list monad = nondeterminism monad" (in classic math papers, it would probably be "set monad = nondeterminism monad", but most functional programmers make do with lists): All the answers here and here seem good to me. Also one of Wadler's first monad papers (for functional programmers) contains such a discussion. (It discusses a series of extensions of a base interpreter for a simple language. When it comes to realizing a nondeterministic language feature, the natural monad to choose is the list monad.) A precursor to this, before the general concept of monads was introduced into FP, was his paper "How to Replace Failure by a List of Successes" (easy to find online), which uses lists for modelling backtracking, but uses just list comprehensions instead of a monadic API.

Contributor

jvoigtlaender commented Aug 18, 2015

About "list monad = nondeterminism monad" (in classic math papers, it would probably be "set monad = nondeterminism monad", but most functional programmers make do with lists): All the answers here and here seem good to me. Also one of Wadler's first monad papers (for functional programmers) contains such a discussion. (It discusses a series of extensions of a base interpreter for a simple language. When it comes to realizing a nondeterministic language feature, the natural monad to choose is the list monad.) A precursor to this, before the general concept of monads was introduced into FP, was his paper "How to Replace Failure by a List of Successes" (easy to find online), which uses lists for modelling backtracking, but uses just list comprehensions instead of a monadic API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment