GithubHelp home page GithubHelp logo

spl / dlist Goto Github PK

View Code? Open in Web Editor NEW
65.0 65.0 15.0 234 KB

Difference lists in Haskell

Home Page: https://hackage.haskell.org/package/dlist

License: BSD 3-Clause "New" or "Revised" License

Haskell 100.00%
data-structures difference-lists haskell

dlist's People

Contributors

23skidoo avatar 414owen avatar basvandijk avatar dependabot[bot] avatar donsbot avatar greenrd avatar hvr avatar jaspa avatar phadej avatar ryanglscott avatar samb avatar sjakobi avatar snoyberg avatar spencerjanssen avatar spl avatar trofi avatar vrom911 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dlist's Issues

Tests have compilation warnings about partial functions with GHC 9.8

tests/DListProperties.hs:70:31: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘head’
    (imported from Data.List, but defined in GHC.List):
    "This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
   |
70 | prop_head = eqOn (not . null) List.head (head . fromList)
   |                               ^^^^^^^^^

tests/DListProperties.hs:73:31: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘tail’
    (imported from Data.List, but defined in GHC.List):
    "This is a partial function, it throws an error on empty lists. Replace it with drop 1, or use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
   |
73 | prop_tail = eqOn (not . null) List.tail (tail . fromList)
   |                               ^^^^^^^^^

It seems that the use of head and tail here is intended, so maybe just switch off the warning.

Add Traversable instance

Do you mind to have the Traversable instance for DList?

I am currently using the following custom traverse function:

traverseDList :: (Applicative f) => (a -> f b) -> DList a -> f (DList b)
traverseDList f = fmap DL.fromList . traverse f . DL.toList

I could make a PR if you think that is a desired addition to the library 🙂

Semigroup instance

Semigroup will be in base from GHC >= 8.0 (GHC #10365). We should now probably add an instance for it, since we can do it without adding another dependency. This may also involve revisiting #11.

Intersperse and Intercalate

These are useful functions to have, especially when DList is being used in conjunction with a Writer, or for the purpose of logging. If there's not any reason preventing these from being added may I make a pull request?

test suite build failures

Building from master i get this:

$ stack test --resolver nightly-2016-07-29
dlist-0.8: build (lib + test)
Preprocessing library dlist-0.8...
Preprocessing test suite 'test' for dlist-0.8...
[2 of 2] Compiling Main             ( tests/Main.hs, .stack-work/dist/x86_64-osx/Cabal-1.24.0.0/build/test/test-tmp/Main.o )

/Users/adam/repos/3rd/dlist/tests/Main.hs:150:27: error:
    • No instance for (Arbitrary (NonEmpty [Int]))
        arising from a use of ‘property’
    • In the expression: property prop_Semigroup_sconcat
      In the expression:
        ("Semigroup sconcat", property prop_Semigroup_sconcat)
      In the expression:
        [("model", property prop_model), ("empty", property prop_empty),
         ("singleton", property prop_singleton),
         ("cons", property prop_cons), ....]
Progress: 1/2
--  While building package dlist-0.8 using:
      /Users/adam/.stack/setup-exe-cache/x86_64-osx/setup-Simple-Cabal-1.24.0.0-ghc-8.0.1 --builddir=.stack-work/dist/x86_64-osx/Cabal-1.24.0.0 build lib:dlist test:test --ghc-options " -ddump-hi -ddump-to-file"
    Process exited with code: ExitFailure 1

And building from the 0.8 tarball:

tests/Main.hs:24:1: error:
    Failed to load interface for ‘OverloadedStrings’
    Use -v to see a list of the files searched for.

Please, link Haddocks to the readme.md

I read through Haddocks & then went to articles that explain DList, as http://h2.jaguarpaw.co.uk/posts/demystifying-dlist/.

Then I added benchmarks to the comparison repo to see "real" results.

Only in the end I see that DList has a tutorial in the readme.md.

Also note that linked article is named "demystifying DList", which implied that DList was considered a mystery when the article was written. Mentioning good 3rd party articles in the docs also can be a cheap efficient way of providing info, if only just to give reassurance that 3rd party article is viable & may give additional angles if reader did not grokked the documentation.

Build with GHC >= 7.0

According to Travis-CI, dlist does not build with GHC versions 7.0, 7.2, and 7.4.

Implementation of `stimes` in `Semigroup` instance

Hey, thanks for this library!

I was looking at the implementation and was wondering if there is a specific reason to use a hand-written implementation for stimes instead of Data.Semigroup.stimesMonoid. If I look at it correctly stimesMonoid uses only a logarithmic number of (<>) and would be preferable to the current implementation. If this is case I can make a pull-request.

Make the DList type abstract

DList a is isomorphic to [a] via fromList/toList, but [a] -> [a] is not isomorphic to [a]. That is, all lists are dlists, and all dlists are lists only if there is no way to construct a y :: DList a from an "unsafe" x :: [a] -> [a].

Currently, the library exports the constructor DL and deconstructor unDL which allows the construction of unsafe dlist values. Consider this GHCi session:

*Data.DList> let x = DL $ \z -> z ++ z
*Data.DList> toList x
[]
*Data.DList> fromList $ toList x
Data.DList.fromList []

The values of x and toList x are clearly not isomorphic. When used:

*Data.DList> append x (singleton 'a')
Data.DList.fromList "aa"
*Data.DList> append (fromList $ toList x) (singleton 'a')
Data.DList.fromList "a"

DList was abstract early on. I don't know why it was changed.

To preserve the isomorphism, I think DList should be an abstract type and the constructor and deconstructor should be removed from the export list.

GHC 9.4: warning about missing `LANGUAGE TypeOperators`

This warning can be fixed in a backwards-compatible way by adding TypeOperators to default-extensions: in the .cabal file.

Building library for dlist-1.0..
[1 of 5] Compiling Data.DList.Internal ( Data/DList/Internal.hs, dist/build/Data/DList/Internal.o, dist/build/Data/DList/Internal.dyn_o )

Data/DList/Internal.hs:642:12: warning: [-Wtype-equality-requires-operators]
    The use of ‘~’ without TypeOperators
    will become an error in a future GHC release.
    Suggested fix: Perhaps you intended to use TypeOperators
    |
642 | instance a ~ Char => IsString (DList a) where
    |            ^
[2 of 5] Compiling Data.DList       ( Data/DList.hs, dist/build/Data/DList.o, dist/build/Data/DList.dyn_o )
[3 of 5] Compiling Data.DList.DNonEmpty.Internal ( Data/DList/DNonEmpty/Internal.hs, dist/build/Data/DList/DNonEmpty/Internal.o, dist/build/Data/DList/DNonEmpty/Internal.dyn_o )

Data/DList/DNonEmpty/Internal.hs:459:12: warning: [-Wtype-equality-requires-operators]
    The use of ‘~’ without TypeOperators
    will become an error in a future GHC release.
    Suggested fix: Perhaps you intended to use TypeOperators
    |
459 | instance a ~ Char => IsString (DNonEmpty a) where
    |            ^

Build with GHC 6.12

Travis-CI reports a failure when attempting to build dlist with GHC 6.12.3:

$ export SRC_TGZ=$(cabal-1.18 info . | awk '{print $2 ".tar.gz";exit}') ; cd dist/; if [ -f "$SRC_TGZ" ]; then cabal-1.18 install "$SRC_TGZ"; else echo "expected '$SRC_TGZ' not found"; exit 1; fi
Warning: Falling back to topdown solver for GHC < 7.
Resolving dependencies...
Configuring dlist-0.5...
Failed to install dlist-0.5
Last 10 lines of the build log ( /home/travis/.cabal/logs/dlist-0.5.log ):
cabal-1.18: Error: some packages failed to install:
dlist-0.5 failed during the configure step. The exception was:
user error (The package 'dlist' requires Cabal library version -any && >=1.9.2
but no suitable version is installed.)

I'm not sure what the problem is. I don't even know if it should be fixed. Putting this here for the record.

Benchmark

Make a set of (micro-)benchmarks demonstrating the performance of dlist over lists with and without left-nested appends. Include Eq, Ord, and Show instances which use toList.

Running the testsuite reports `Prelude.read: no parse`

On current master, cabal test puts the following errors into the log:

*** Failed! Exception: 'Prelude.read: no parse' (after 1 test):
0 :| []
fromList "OverloadedStrings for DList:     success"
fromNonEmpty 'O' :| "verloadedStrings for DNonEmpty: success"

(However, the final result is printed as Test suite test: PASS.)

Source location:

test = do
print $ "OverloadedStrings for DList: " `DList.append` "success"
-- CPP: GHC >= 8 for DNonEmpty
#if __GLASGOW_HASKELL__ >= 800
print $ "OverloadedStrings for DNonEmpty: " `DNonEmpty.append` "success"
#endif

Documented time bounds are incorrect for persistent use

The usual assumption for pure data structures in Haskell is that the documented time bounds may be amortized, but that amortization must hold up under persistent use. That is not true for dlist. snoc and head are both documented as being O(1), and yet the following program runs far longer than I would ever want to wait. Swapping in Data.Sequence for DList lets this run quickly.

import qualified Data.DList as D
import Data.DList (DList)
import Data.Foldable

blob :: DList Int
blob = foldl' D.snoc mempty [1..10^7]

main :: IO ()
main = print $ sum [ D.head (blob `D.snoc` a) | a <- [1..10^6 :: Int]]

I think the best approach is likely to document that DLists are intended to be used in an affine/linear fashion, and that the bounds are only valid in that context.

Please, put the to/fromList conversion cost more clearly

Currently the main info is in fromList

{-|
__@fromList xs@__ is a 'DList' representing the list __@xs@__.
@fromList@ obeys the laws:
@
'toList' . __fromList__ = 'id'
__fromList__ . 'toList' = 'id'
@
This function is implemented with '++'. Repeated uses of @fromList@ are just as
inefficient as repeated uses of '++'. If you find yourself doing some form of
the following (possibly indirectly), you may not be taking advantage of the
'DList' representation and library:
@
__fromList__ . f . 'toList'
@
More likely, you will convert from a list, perform some operation on the
'DList', and convert back to a list:
@
'toList' . g . __fromList__
@
-}
{- ORMOLU_ENABLE -}
{-# INLINE fromList #-}
fromList :: [a] -> DList a
fromList = UnsafeDList . (++)

This paragraph:

If you find yourself doing some form of the following you may not be taking advantage of the 'DList' representation and library:

fromList . f . toList

More likely, you will convert from a list, perform some operation on the 'DList', and convert back to a list: 

toList . g . fromList 

fromList :: [a] -> DList a 
fromList = UnsafeDList . (++)

"If you find yourself doing some form of the following" implies that everything that follows - is counterproductive. & logically both fromList . f . toList & toList . g . fromList for simple things are counterproductive. & "following" statement opens - and never stops, never says where examples & "following" stops & it does not even have a . at the end of sentences - so that means "everything that follows".

"If you find yourself doing some form of the following ... More likely, you will convert ..." - what does that mean = it is an ad hominem probabilistic characteristic, not a statement or explanation, moreover it applies things to me and not to a library or compiler. It not forgot to mention me in the docs, but forgot to mention if the way discussed there is recommended, or as "following" implies - just as counterproductive. Dwelling on ad hominems you we so much - to show a trend in the documentation of use those easy escape words as diversions, instead of the description of the process & objects involved. If to just remove personal directioning from docs - they suddenly both shorter & understandable.
As:

'fromList . f . toList' - does not be take advantage of the 'DList'.
'toList . g . fromList' - takes advantage of the 'DList' optimized operations.

suddenly text 2 times shorter & clear.

Moreover - that paragraph can be removed entirely as tautology - of course doing operations with other type & functions - is not using DList.

Having O notation & stating from/toList costs is enough to infer where it is useful.

I vaguely remember reading about DList directions of conversion, but it was years ago & do not remember if anisotropy (directional properties) came out being imaginary or real, if it is stated clearly in the doc - users would not need to remember it from years ago & would not get puzzled.

Incomplete pattern warning when matching both `Nil` and `Cons`

Hello, I'm getting this warning for a pattern usage like:

foo :: DList a -> IO ()
foo Nil = putStrLn "empty!"
foo (Cons x xs) = print $ x:xs
warning: [-Wincomplete-patterns]
    Pattern match(es) are non-exhaustive
    In an equation for ‘foo’:
        Patterns not matched: _ 

My understanding using pattern synonyms, is that the implementer needs to tell the compiler which pattern set should be considered as complete:

{-# COMPLETE Nil, Cons #-}

Would you accept a PR for that?

Breaks with GHC-8.8.1 new Monad / MonadFail handling of fail

The new Monad instance does not have the method fail. That has been moved exclusively to MonadFail in ghc-8.8.1.

I tried building dlist with the alpha release of the ghc-8.8.1 and the current version results in the following error:

    Configuring microlens-0.4.10...
    Preprocessing library for microlens-0.4.10..
    Building library for microlens-0.4.10..
    [1 of 4] Compiling Lens.Micro.Type  ( src/Lens/Micro/Type.hs, .stack-work/dist/x86_64-linux/Cabal-2.5.0.0/build/Lens/Micro/Type.o )
    [2 of 4] Compiling Lens.Micro.Internal ( src/Lens/Micro/Internal.hs, .stack-work/dist/x86_64-linux/Cabal-2.5.0.0/build/Lens/Micro/Internal.o )
    [3 of 4] Compiling Lens.Micro       ( src/Lens/Micro.hs, .stack-work/dist/x86_64-linux/Cabal-2.5.0.0/build/Lens/Micro.o )
    
    /tmp/stack7255/microlens-0.4.10/src/Lens/Micro.hs:1416:5: error:
        ‘fail’ is not a (visible) method of class ‘Monad’
         |
    1416 |     fail str = StateT $ \ _ -> fail str
         |     ^^^^
    
    /tmp/stack7255/microlens-0.4.10/src/Lens/Micro.hs:1417:16: error:
        The INLINE pragma for ‘fail’ lacks an accompanying binding
          (The INLINE pragma must be given where ‘fail’ is declared)
         |
    1417 |     {-# INLINE fail #-}
         | 

It should be simple enough to fix with some use of the C pre-processor.

Recover bench test

Oops! Seems like I removed things that I shouldn't have in test.yml with #110. Too much enthusiasm and too little oversight, I suppose. Add it back!

Restore `tail :: DList a -> DList a`?

In 1.0, the function tail changed to DList a -> [a], motivated by the removal of the recursor list.

However, what speaks against the following definition?

tail :: DList a -> DList a
tail (UnsafeDList f) = UnsafeDList (Data.List.tail . f)

This would be a proper efficient tail function for DList.
The new tail (dlist-1.0) leaves the realm of DLists, so it seems it is already subsumed by Data.List.tail . toList.

Originally posted by @andreasabel in #69 (comment)

Add Internal Module?

I'd like to use this library, but in order to implement something (basically I need to lift a function [a] -> [a] to a DList a -> DList a function... I think I need the DL constructor. I think what I want to do is this:

myLift f = DL $ f . unDL

Unfortunately DL and unDL don't seem to be exposed. unDL is indirectly exposed with apply, but I don't see DL isn't exported by the module.

Is there a good way to efficiently do what I want to do? Without access to the constructor? Admittedly, I'm not much of a wizard at analyzing Haskell performance, so it's possible I'm missing something, but maybe it would make sense to expose DL in an internal module or something?

This relates to this old PR: #4

I totally agree with not exporting it by default, but if I want to be unsafe, having the option would be nice :).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.