Skip to content

Conversation

@strake
Copy link
Contributor

@strake strake commented May 7, 2018

I often find these useful.

@strake
Copy link
Contributor Author

strake commented May 21, 2018

ping

Copy link
Member

@RyanGlScott RyanGlScott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems sensible to me. However, per the (tacit) conventions of the vector library, you should add versions of uncons and unsnoc to each of the concrete Vector modules:

  • Data.Vector
  • Data.Vector.Unboxed
  • Data.Vector.Storable
  • Data.Vector.Primitive

@strake
Copy link
Contributor Author

strake commented May 21, 2018

Done, PTAL

@RyanGlScott
Copy link
Member

LGTM.

@cartazio, thoughts?

n' = max n 0
len = length v

-- | /O(1)/ Yield the 'head' and 'tail' of the vector, or 'Nothing' if empty.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why aren't we thinking about how to adapt the fusion codes for head/tail as found in Stream.Monadic and/or bundle?

https://github.com/haskell/vector/blob/master/Data/Vector/Fusion/Stream/Monadic.hs#L341
for one of them

@cartazio
Copy link
Contributor

  1. i think this is a VALID / USEFUL contribution

  2. this seems like a solid natureal implementation if we ignore fusion.... but should we? (Ryan points out that eg the partition function does too, but should it?)

  3. how about we figure this out by seeing if we can construct the bundle/stream monad combinator versions of this and compare them

  4. (@RyanGlScott how many things in Vector.Generic dont have an impl in bundle or stream? we may need to review that ... perhaps on another ticket
    )

@cartazio
Copy link
Contributor

roughly: ignoring fusion this looks good ... but if we ignore fusion we should just use Array right? :)

@strake
Copy link
Contributor Author

strake commented May 31, 2018

@cartazio I think uncons would make sense on Streams, but am not sure how to write unsnoc efficiently; is the latter worth doing?

@cartazio
Copy link
Contributor

i think you can look at stream-take and stream-last definitions as starting points perhaps?

I swear I saw some machinery for left vs right streams at some point in vector, but i'm not finding it at the moment

@cartazio
Copy link
Contributor

https://github.com/haskell/vector/blob/master/Data/Vector/Fusion/Stream/Monadic.hs#L143 has length, not srue if thats quite th right form though

@cartazio
Copy link
Contributor

i might be missing something obvious mind you :)

@cartazio
Copy link
Contributor

likewise, we probably want to then ahve bundle versions of those operations perhaps? @dolio ?

@strake
Copy link
Contributor Author

strake commented Mar 26, 2019

@cartazio any word on what needs to be done to accept this PR? (e.g. bundle ops)

@cartazio
Copy link
Contributor

cartazio commented Mar 26, 2019 via email

@strake
Copy link
Contributor Author

strake commented Nov 23, 2019

@cartazio ping

@cartazio
Copy link
Contributor

cartazio commented Nov 24, 2019 via email

@cartazio
Copy link
Contributor

now that the most recent release has been done, i'm catching up on polishing backlog.

i'll look

@lehins
Copy link
Contributor

lehins commented Jun 5, 2020

@strake I do support @cartazio push for thinking about fusion.

With respect to unsnoc it is not feasible to implement it without either duplicated work or materializing the whole vector, because you'd require to iterate the whole stream in order to get to the last element. Therefore your unsnoc implementation is perfectly fine.

As far as uncons is concerned we can convert a vector to a stream, grab the first element from that stream and construct another stream where we drop that first element. I could be wrong of course, since I haven't tried how fusion with rewrite rules will interact with such approach, but I think it should be fine.

uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
uncons str@(Stream step t) = unconsMaybeLoop SPEC t
  where
    unconsMaybeLoop !_ s = do
      r <- step s
      case r of
        Yield x _ -> return $ Just (x, drop 1 str)
        Skip s'   -> unconsMaybeLoop SPEC s'
        Done      -> return Nothing
    {-# INLINE [0] unconsMaybeLoop #-}
{-# INLINE uncons #-}

An even more general and useful approach would be to implement headMaybe in the exact same manner and reuse that to implement uncons:

headMaybe (Stream step t) = headMaybeLoop SPEC t
  where
    headMaybeLoop !_ s = do
      r <- step s
      case r of
        Yield x _ -> return $ Just x
        Skip s'   -> headMaybeLoop SPEC s'
        Done      -> return Nothing
    {-# INLINE [0] headMaybeLoop #-}
{-# INLINE headMaybe #-}

@strake If you'd like to give it a shot that be great if not I'll try it out in a few days myself.

@lehins
Copy link
Contributor

lehins commented Jun 22, 2020

Trying to integrate with fusion only makes it worse. Because we have constant time slicing consecutive uncons/unsonc are very efficient with current implementation.

@lehins lehins merged commit 3f6f737 into haskell:master Jun 22, 2020
@lehins
Copy link
Contributor

lehins commented Jun 22, 2020

@strake Thank you for the PR. Sorry it took so long to get it merged.

lehins added a commit that referenced this pull request Jan 16, 2021
Addition of `uncons`, `unsnoc`.

Co-authored-by: Alexey Kuleshevich <alexey@kuleshevi.ch>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants