turingjump / bookkeeper

BSD 3-Clause "New" or "Revised" License
55 stars 17 forks source link

Monoid instance #6

Open jkarni opened 8 years ago

jkarni commented 8 years ago

For types other than '[]

fizruk commented 8 years ago

Why exclude '[]?

sid-kap commented 7 years ago

@fizruk There's already a Monoid instance for Book '[]

Wizek commented 7 years ago

I've been wondering if it is possible to merge two Books together, and the Monoid instance seems to be what I could be looking for.

I managed to achieve a merging behaviour like so:

> import qualified Data.Type.Map as TMap
Prelude ComposeLTR Bookkeeper Bookkeeper.Internal TMap
> let (Book a) = (emptyBook & #foo =: 1 & #bar =: "bar"); (Book c) = (emptyBook & #baz =: 1) in TMap.union a c $> Book
Book {bar = "bar", baz = 1, foo = 1}

Currently the Monoid instance looks like so:

instance Monoid (Book' '[]) where
  mempty = emptyBook
  _ `mappend` _ = emptyBook

Would it make sense to extend it with the code above? I also found it interesting to notice that TMap.union doesn't seem to support overlaps. Could we have a merge that supports overwrites and/or recursive mappend?

e.g.

((emptyBook & #baz =: "hello ") `overwriteBook` (emptyBook & #baz =: "world"))
  == (emptyBook & #baz =: "world") 

((emptyBook & #baz =: "hello ") <> (emptyBook & #baz =: "world"))
  == (emptyBook & #baz =: "hello world") 
Wizek commented 7 years ago

I've tried to give this a go again just now:

appendOverwrite TMap.Empty x = x
appendOverwrite (TMap.Ext k v xs) ys = TMap.Ext k v (appendOverwrite xs (deleteTMapK k ys))

  where
  unBook (Book a) = a
  deleteTMapK k m = unBook $ delete k (Book m)

Trying to mimic Data.Type.Map.append:

append :: TMap.Map s -> TMap.Map t -> TMap.Map (s TSet.:++ t)
append TMap.Empty x = x
append (TMap.Ext k v xs) ys = TMap.Ext k v (append xs ys)

But I get the type error:

<interactive>:439:32: error:
    • Couldn't match expected type ‘t’ with actual type ‘t1’
        ‘t’ is untouchable
          inside the constraints: t2 ~ '[]
          bound by a pattern with constructor: TMap.Empty :: TMap.Map '[],
                   in an equation for ‘appendOverwrite’
          at <interactive>:439:17-26
      ‘t’ is a rigid type variable bound by
        the inferred type of appendOverwrite :: TMap.Map t2 -> t1 -> t
        at <interactive>:439:1
      ‘t1’ is a rigid type variable bound by
        the inferred type of appendOverwrite :: TMap.Map t2 -> t1 -> t
        at <interactive>:439:1
      Possible fix: add a type signature for ‘appendOverwrite’
    • In the expression: x
      In an equation for ‘appendOverwrite’:
          appendOverwrite TMap.Empty x = x
    • Relevant bindings include
        x :: t1 (bound at <interactive>:439:28)
        appendOverwrite :: TMap.Map t2 -> t1 -> t
          (bound at <interactive>:439:1)

<interactive>:440:86: error:
    • Couldn't match expected type ‘Key field0’
                  with actual type ‘TMap.Var k’
    • In the first argument of ‘deleteTMapK’, namely ‘k’
      In the second argument of ‘appendOverwrite’, namely
        ‘(deleteTMapK k ys)’
      In the third argument of ‘TMap.Ext’, namely
        ‘(appendOverwrite xs (deleteTMapK k ys))’
    • Relevant bindings include
        k :: TMap.Var k (bound at <interactive>:440:27)

I am not sure what is going awry. Maybe I don't have enough experience with -XTypeFamilies (or something else) to decypher this at the moment.

So I thought to go for something a bit simpler, a sub-problem:

deleteAll TMap.Empty x = x
deleteAll (TMap.Ext k _ xs) ys = deleteAll xs (deleteTMapK k ys)

Which resulted in a rather similar type error:

<interactive>:447:26: error:
    • Couldn't match expected type ‘t’ with actual type ‘t1’
        ‘t’ is untouchable
          inside the constraints: t2 ~ '[]
          bound by a pattern with constructor: TMap.Empty :: TMap.Map '[],
                   in an equation for ‘deleteAll’
          at <interactive>:447:11-20
      ‘t’ is a rigid type variable bound by
        the inferred type of deleteAll :: TMap.Map t2 -> t1 -> t
        at <interactive>:447:1
      ‘t1’ is a rigid type variable bound by
        the inferred type of deleteAll :: TMap.Map t2 -> t1 -> t
        at <interactive>:447:1
      Possible fix: add a type signature for ‘deleteAll’
    • In the expression: x
      In an equation for ‘deleteAll’: deleteAll TMap.Empty x = x
    • Relevant bindings include
        x :: t1 (bound at <interactive>:447:22)
        deleteAll :: TMap.Map t2 -> t1 -> t (bound at <interactive>:447:1)

<interactive>:448:60: error:
    • Couldn't match expected type ‘Key field0’
                  with actual type ‘TMap.Var k’
    • In the first argument of ‘deleteTMapK’, namely ‘k’
      In the second argument of ‘deleteAll’, namely ‘(deleteTMapK k ys)’
      In the expression: deleteAll xs (deleteTMapK k ys)
    • Relevant bindings include
        k :: TMap.Var k (bound at <interactive>:448:21)

Any ideas on what may be going wrong above? Or how we could have a working overwriteBook function?

Wizek commented 7 years ago

A bit of an update. Giving the following signature gets rid of the first type error:

appendOverwrite :: TMap.Map s -> TMap.Map t -> TMap.Map (s TSet.:++ t)
appendOverwrite TMap.Empty x = x
appendOverwrite (TMap.Ext k v xs) ys = TMap.Ext k v (appendOverwrite xs (deleteTMapK k ys))

Leaving us with: Couldn't match expected type ‘Key field0’ with actual type ‘TMap.Var k’

Which types are only nominally different, right? Could I convert between them somehow? I've also tried to construct this without unBook . Book but I haven't been successful yet.