GithubHelp home page GithubHelp logo

bookkeeper's Introduction

Bookkeeper Build Status

Bookkeeper is a new Haskell library that uses the new OverloadedLabels feature of GHC 8 to provide a new take on datatypes and records:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedLabels #-}
import Bookkeeper


jane :: Book '[ "name" :=> String, "age" :=> Int ]
jane = emptyBook
     & #name =: "Jane"
     & #age =: 30

-- >>> jane
-- Book {age = 30, name = "Jane"}
-- >>> jane ?: #name
-- "Jane"

It bears some similarities to Nikita Volkov's record library, but requires no Template Haskell.

Accesing a field that does not exist is a type error, made nicer with GHCs new custom type errors:

 -- >>> jane ?: #address
--   • The provided Book does not contain the field "address"
--     Book type:
--     '["age" ':-> Int, "name" ':-> String]

The order in which fields are inserted or appear in types does not matter. That is, in:

-- type A = Book '[ "field1" :=> Int,  "field2" :=> Bool]
-- type B = Book '[ "field2" :=> Bool, "field1" :=> Int ]

Types A and B are the same.

You can set, modify, or get fields. See the haddocks for more information.

main :: IO ()
main = return ()

bookkeeper's People

Contributors

bandali0 avatar dmjio avatar gurkenglas avatar jkarni avatar kcsongor avatar meditans avatar mithrandi avatar sid-kap avatar soenkehahn 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bookkeeper's Issues

No lens support

record uses lenses because, as he notes, they already solve the modification part of the record problem. Why don't you?

Better type errors for a relatively simple expression

With reference to the issue #16, the code

c :: (Settable "pitch" Pitch old new, Default (Book' old)) => Book' new
c = set #pitch C def

which now errors with:

    • Could not deduce (Map.Submap
                          (Data.Type.Map.Nub (Data.Type.Set.Sort (old0 Map.:\ "pitch")))
                          old0,
                        (Map.Nubable
                           ((Data.Type.Set.Sort
                               (Data.Type.Set.Filter
                                  'Data.Type.Set.FMin
                                  ("pitch" ':-> Pitch)
                                  (Data.Type.Map.Nub (Data.Type.Set.Sort (old0 Map.:\ "pitch"))))
                             Data.Type.Set.:++ '["pitch" ':-> Pitch])
                            Data.Type.Set.:++ Data.Type.Set.Sort
                                                (Data.Type.Set.Filter
                                                   'Data.Type.Set.FMax
                                                   ("pitch" ':-> Pitch)
                                                   (Data.Type.Map.Nub
                                                      (Data.Type.Set.Sort (old0 Map.:\ "pitch"))))),
                         Data.Type.Map.Sortable
                           ("pitch" ':-> Pitch
                              : Data.Type.Map.Nub (Data.Type.Set.Sort (old0 Map.:\ "pitch")))),
                        new
                        ~
                        Data.Type.Map.Nub
                          ((Data.Type.Set.Sort
                              (Data.Type.Set.Filter
                                 'Data.Type.Set.FMin
                                 ("pitch" ':-> Pitch)
                                 (Data.Type.Map.Nub (Data.Type.Set.Sort (old0 Map.:\ "pitch"))))
                            Data.Type.Set.:++ '["pitch" ':-> Pitch])
                           Data.Type.Set.:++ Data.Type.Set.Sort
                                               (Data.Type.Set.Filter
                                                  'Data.Type.Set.FMax
                                                  ("pitch" ':-> Pitch)
                                                  (Data.Type.Map.Nub
                                                     (Data.Type.Set.Sort (old0 Map.:\ "pitch"))))))
      from the context: (Settable "pitch" Pitch old new,
                         Default (Book' old))
        bound by the type signature for:
                   c :: (Settable "pitch" Pitch old new, Default (Book' old)) =>
                        Book' new
        at /home/carlo/code/haskell/forks/bookkeeper/src/Bookkeeper/Internal.hs:256:6-71
      The type variable ‘old0’ is ambiguous
    • In the ambiguity check for ‘c’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      In the type signature:
        c :: (Settable "pitch" Pitch old new, Default (Book' old)) =>
             Book' new
Failed, modules loaded: Bookkeeper.Internal.Errors.

could expose a more friendly error to the user.

Question: how to create a value with a fixed field when the others are default.

Hi, I have a question that stemmed from a discussion on the design of the music-suite library.

Let's say I have the datatype:

data Pitch = C | D | E | F | G | A | B

And others that reflect other possible aspects of a note, like

data Duration = ...
instance Default Duration where ...
data Voice = ...
instance Default Duration where ...
data RenderingProperties = ...
instance Default RenderingProperties where ...

which all have the Default instance.

Now, on one hand I don't want the users to precommit to a specific set of properties, so the user could define:

type PureNote = Book '["pitch" :=> Pitch]
type SituatedNote = Book '["pitch" :=> Pitch, "duration" :=> Duration]

but on the other hand, I want to be able to write c :: PureNote and c :: SituatedNote, and in general to construct a value of c for every Book that has a pitch field and for which the other field have a Default instance. So I tried writing:

class HasPitch a where
  c :: a

instance ( Default (Book' old)
         ) => HasPitch (Book' (("pitch" :=> Pitch) ': old)) where
  c = Book $ Map.Ext Map.Var C (getBook def)

The problem is that obviously this works only if "pitch" is the symbol at the front of the list, so this doesn't work for the SituatedNote I wrote before (because "duration" < "pitch"). Could you show me how to circumvent that ordering problem and write the right instance?

Show instance generates invalid Haskell code

Most datatypes implement Show in such a way that the result is a valid Haskell code (maybe with some extensions turned on, but still). However, before fixing the Show instance, I would consider a better way to encode Books. E.g. something like this (although I'm not sure if it's possible):

Book [#age =: 30, #name =: "Jane"]

Compile times

Basically every operation in this library is not very smartly implemented in terms of reducing compile times (e.g. sorting an already sorted list!). Fix this.

  • Don't re-sort on every insertion, but insert at the right place [Is O(n * log n), could be O (log n)]
  • delete (and therefore set and modify) resort the map after deletion, when in fact it is already sorted. It may be hard to convince GHC that is already is sorted, though. In any case, submap probably shouldn't be used - we always want to remove just one element.
  • Settable has a constraint (new ~ Map.AsMap...) to prove something in a costly way that we should already know is true.

In general, I think the right approach is not prove to the compiler that certain operations maintain the set properties, and only have those operations accept sets, but instead just assume the arguments are sets and recover safety by not exposing a way of creating a Book' that doesn't have those properties.

Another interesting possibility is seeing if we can use Data.Type.Equality and unsafeCoerce to have the same type equalities for cheaper.

Default instance

Please consider adding an instance for Data.Default.Class. I feel this could be useful in the case of records because it enables thinking about the automatic creation of records with default values.

RULES

Adding RULES would be great, but GHC isn't happy. E.g.:

  {-# RULES  "set/set" forall a b field book. set field a (set field b book) = set field a book
  #-}

/home/jkarni/Documents/progs/bookkeeper/src/Bookkeeper.hs:49:68: error:
    • Couldn't match type ‘Data.Type.Map.Nub
                             ((Data.Type.Set.Sort
                                 (Data.Type.Set.Filter
                                    'Data.Type.Set.FMin
                                    (field 'Data.Type.Map.:-> val1)
                                    (Data.Type.Map.Nub
                                       (Data.Type.Set.Sort (old Data.Type.Map.:\ field))))
                               Data.Type.Set.:++ '[field 'Data.Type.Map.:-> val1])
                              Data.Type.Set.:++ Data.Type.Set.Sort
                                                  (Data.Type.Set.Filter
                                                     'Data.Type.Set.FMax
                                                     (field 'Data.Type.Map.:-> val1)
                                                     (Data.Type.Map.Nub
                                                        (Data.Type.Set.Sort
                                                           (old Data.Type.Map.:\ field)))))’
                     with ‘Data.Type.Map.Nub
                             ((Data.Type.Set.Sort
                                 (Data.Type.Set.Filter
                                    'Data.Type.Set.FMin
                                    (field 'Data.Type.Map.:-> val1)
                                    (Data.Type.Map.Nub
                                       (Data.Type.Set.Sort
                                          (Data.Type.Map.Nub
                                             ((Data.Type.Set.Sort
                                                 (Data.Type.Set.Filter
                                                    'Data.Type.Set.FMin
                                                    (field 'Data.Type.Map.:-> val)
                                                    (Data.Type.Map.Nub
                                                       (Data.Type.Set.Sort
                                                          (old Data.Type.Map.:\ field))))
                                               Data.Type.Set.:++ '[field 'Data.Type.Map.:-> val])
                                              Data.Type.Set.:++ Data.Type.Set.Sort
                                                                  (Data.Type.Set.Filter
                                                                     'Data.Type.Set.FMax
                                                                     (field 'Data.Type.Map.:-> val)
                                                                     (Data.Type.Map.Nub
                                                                        (Data.Type.Set.Sort
                                                                           (old
                                                                            Data.Type.Map.:\ field)))))
                                           Data.Type.Map.:\ field))))
                               Data.Type.Set.:++ '[field 'Data.Type.Map.:-> val1])
                              Data.Type.Set.:++ Data.Type.Set.Sort
                                                  (Data.Type.Set.Filter
                                                     'Data.Type.Set.FMax
                                                     (field 'Data.Type.Map.:-> val1)
                                                     (Data.Type.Map.Nub
                                                        (Data.Type.Set.Sort
                                                           (Data.Type.Map.Nub
                                                              ((Data.Type.Set.Sort
                                                                  (Data.Type.Set.Filter
                                                                     'Data.Type.Set.FMin
                                                                     (field 'Data.Type.Map.:-> val)
                                                                     (Data.Type.Map.Nub
                                                                        (Data.Type.Set.Sort
                                                                           (old
                                                                            Data.Type.Map.:\ field))))
                                                                Data.Type.Set.:++ '[field
                                                                                    'Data.Type.Map.:-> val])
                                                               Data.Type.Set.:++ Data.Type.Set.Sort
                                                                                   (Data.Type.Set.Filter
                                                                                      'Data.Type.Set.FMax
                                                                                      (field
                                                                                       'Data.Type.Map.:-> val)
                                                                                      (Data.Type.Map.Nub
                                                                                         (Data.Type.Set.Sort
                                                                                            (old
                                                                                             Data.Type.Map.:\ field)))))
                                                            Data.Type.Map.:\ field)))))’
        arising from a use of ‘set’
      NB: ‘Data.Type.Map.Nub’ is a type function, and may not be injective
    • In the expression: set field a book
      When checking the transformation rule "set/set"
    • Relevant bindings include
        a :: val1 (bound at src/Bookkeeper.hs:49:19)
        b :: val (bound at src/Bookkeeper.hs:49:21)
        field :: Key field (bound at src/Bookkeeper.hs:49:23)
        book :: Book' old (bound at src/Bookkeeper.hs:49:29)

release that works with ghc-8.2

Would it be possible to publish a release to hackage that works with ghc-8.2? (It seems as if current master already works with that ghc version.)

Can't load test-suite in ghci

Because of missing language extension declarations (DataKinds, TypeOperators, etc.). You should either put them directly in the files or duplicate the ones from the cabal file in the .ghci file.

support ghc-8.2

It seems the base version constraint disallows that ghc version currently.

stack haddock doesnt work

Gurkenglas@Gurkenglas-PC MINGW64 ~/bookkeeper
$ stack haddock
bookkeeper-0.2.0.0: configure
Configuring bookkeeper-0.2.0.0...
bookkeeper-0.2.0.0: build
Preprocessing library bookkeeper-0.2.0.0...
Preprocessing executable 'readme' for bookkeeper-0.2.0.0...
[1 of 1] Compiling Main ( exec\Readme.lhs, .stack-work\dist\b7fec021\build\readme\readme-tmp\Main.o )

C:\Users\GURKEN~1\AppData\Local\Temp\ghc39552_0\ghc_1.lpp:1:1: error:
The IO action main' is not defined in moduleMain'

-- While building package bookkeeper-0.2.0.0 using:
C:\Users\Gurkenglas\AppData\Roaming\stack\setup-exe-cache\x86_64-windows\setup-Simple-Cabal-1.24.0.0-ghc-8.0.1.exe --builddir=.stack-work\dist\b7fec021 build lib:bookkeeper exe:readme --ghc-options " -ddump-hi -ddump-to-file"
Process exited with code: ExitFailure 1

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.