Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there some way to reduce the pain of range tracking?

There's currently a pull request by Jonathan S. to replace the implementation of Data.IntMap with one explained in this README based on ideas from a blog post by Edward Kmett.

The basic concept Jonathan S. developed from is that an IntMap is a binary tree that looks like this (I've made some slight changes to his development for the sake of consistency):

data IntMap0 a = Empty | NonEmpty (IntMapNE0 a) 
data IntMapNE0 a =
    Tip !Int a
  | Bin { lo :: !Int
        , hi :: !Int
        , left :: !(IntMapNE0 a)
        , right :: !(IntMapNE0 a) }

In this representation, each node has a field indicating the least and greatest key contained in the IntMapNE0. Using just a little bit fiddling allows this to be used as a PATRICIA trie. Jonathan noted that this structure has almost twice as much range information as it needs. Following a left or right spine will produce all the same lo or hi bounds. So he cut those out by only including the bound not determined by the ancestors:

data IntMap1 a = Empty | NonEmpty { topLo :: !Int, child :: !(IntMapNE1 a) }
data IntMapNE1 a =
    Tip a
  | IntMapNE1 { bound :: !Int
              , left :: !(IntMapNE1 a)
              , right :: !(IntMapNE1 a)

Now each node has either a left bound or a right bound, but not both. A right child will have only a left bound, while a left child will have only a right bound.

Jonathan makes one further change, moving the values out of the leaves and into the internal nodes, which places them exactly where they are determined. He also uses phantom types to help track left and right. The final type (for now, anyway) is

data L
data R
newtype IntMap a = IntMap (IntMap_ L a) deriving (Eq)
data IntMap_ t a = NonEmpty !Int a !(Node t a) | Empty deriving (Eq)
data Node t a = Bin !Int a !(Node L a) !(Node R a) | Tip deriving (Eq, Show)

Certain aspects of this new implementation are quite attractive. Most importantly, many of the most-used operations are substantially faster. Less importantly, but very nicely, the bit fiddling involved is much easier to understand.

However, there is one serious pain point: passing the missing range information down through the tree. This isn't so bad for lookups, insertions, etc., but gets pretty seriously hairy in the union and intersection code. Is there some abstraction that would allow this to be done automatically?

A couple extremely vague thoughts:

  1. Could the phantom types be used with a custom class to tie treatment directly to handedness?

  2. The "missing piece" nature is somewhat reminiscent of some zippery situations. Might there be a way to use ideas from that realm?


I've started thinking about using an intermediate type of some sort to provide a symmetrical "view" of the structure, but I'm a bit stuck. I can fairly easily convert back and forth between the basic structure and the fancy one, but that conversion is recursive. I need a way to convert only partially, but I don't know nearly enough about fancily built types to get it done.

like image 515
dfeuer Avatar asked Sep 15 '16 21:09

dfeuer


People also ask

How painful is patellar tracking disorder?

Pain varies depending on the severity of the disorder. An example of a severe case of a tracking disorder is a dislocation. If the patella is completely dislocated, you'll usually feel a lot of pain. Your leg may appear bent or out of shape, and you may not be able to bend or straighten your knee or walk.

Can you run with patellar tracking disorder?

If you have or suspect you have patella tracking disorder, you should avoid activities that make the pain worse or put too much load through the knee such as running or jumping. As well as making your pain worse this can make the problem worse and it will take longer to treat.

How do you fix patella tracking problems?

Most patellar tracking problems can be treated effectively without surgery. Non-surgical treatment may include rest, regular stretching and strengthening exercises, taping or bracing the knee, using ice, and short-term use of non-steroidal anti-inflammatory drugs (NSAIDs).

How long does patellar tracking take to heal?

Recovery from a patellar tracking disorder can take weeks or months. Patients must work with their doctor to avoid movements that cause the condition, continue strengthening exercises even after pain subsides and lose weight.


Video Answer


1 Answers

Is there some abstraction that would allow this to be done automatically?

You should be able to define a set of pattern synonyms that give you that. I’ll start from the second-to-last variant of your code, i.e.:

data IntMap1 a = Empty | NonEmpty { topLo :: !Int, child :: !(IntMapNE1 a) }
data IntMapNE1 a =
    Tip a
  | IntMapNE1 { bound :: !Int
              , left :: !(IntMapNE1 a)
              , right :: !(IntMapNE1 a)

We tuple such a value with the bound from the parent in an Either (indicating whether it is a low or a high bound).

viewLoHi (Left lo, IntMapNE1 hi left right)
    = Just (lo, hi, (Left lo, left), (Right hi, right)
viewLoHi (Right hi, IntMapNE1 lo left right)
    = Just (lo, hi, (Left lo, left), (Right hi, right)
viewLoHi _
    = Nothing

pattern Bin' lo hi left right <- (viewLoHi -> Just (lo, hi, left, right))

The top-level data type is different, so it needs its own pattern synonym

viewLoHi' (NonEmpty lo child) = viewLoHi (Left lo, child)
viewLoHi' Empty = Nothing

pattern NonEmpty' lo hi left right <- (viewLoHi' -> Just (lo, hi, left, right)

Using only NonEmpty' and Bin' as you traverse the tree, the bookkeeping should now be completely hidden. (Code not tested, so there will be typos here)

like image 103
Joachim Breitner Avatar answered Oct 05 '22 01:10

Joachim Breitner