Improve efficiency of Context
implementation
The old list-based implementation was tuned for Morte and more efficient for type-checking typical Morte expressions (such as Church-encoded bytes), that had multiple occurrences of the same key. I'm still not sure why it was more efficient, though, but it may have been due to better use of laziness. However, these sorts of expressions are rare in idiomatic Dhall code, so we use the more direct approach using a `Map` for faster lookup of the outer keys and a `Seq` for faster indexing into the elements associated with a given key.
This commit is contained in:
parent
e4abd4fdfc
commit
3d9661817f
|
@ -13,45 +13,69 @@ module Dhall.Context (
|
|||
, toList
|
||||
) where
|
||||
|
||||
import Data.Map (Map)
|
||||
import Data.Monoid ((<>))
|
||||
import Data.Sequence (Seq, (<|))
|
||||
import Data.Text.Lazy (Text)
|
||||
import Prelude hiding (lookup)
|
||||
|
||||
-- | Bound variable names and their types
|
||||
newtype Context a = Context { getContext :: [(Text, a)] }
|
||||
import qualified Data.Foldable
|
||||
import qualified Data.Map
|
||||
import qualified Data.Sequence
|
||||
|
||||
{-| A @(Context a)@ associates `Text` labels with values of type @a@
|
||||
|
||||
The `Context` is used for type-checking when @(a = Expr X)@
|
||||
|
||||
* You create a `Context` using `empty` and `insert`
|
||||
* You transform a `Context` using `fmap`
|
||||
* You consume a `Context` using `lookup` and `toList`
|
||||
|
||||
The difference between a `Context` and a `Map` is that a `Context` lets you
|
||||
have multiple occurrences of the same key and you can query for the @n@th
|
||||
occurrence of a given key.
|
||||
-}
|
||||
newtype Context a = Context { getContext :: Map Text (Seq a) }
|
||||
deriving (Functor)
|
||||
|
||||
-- | An empty context with no key-value pairs
|
||||
empty :: Context a
|
||||
empty = Context []
|
||||
empty = Context Data.Map.empty
|
||||
|
||||
-- | Add a key-value pair to the `Context`
|
||||
insert :: Text -> a -> Context a -> Context a
|
||||
insert k v (Context kvs) = Context ((k, v) : kvs)
|
||||
insert k v (Context kvs) =
|
||||
Context (Data.Map.insertWith (<>) k (Data.Sequence.singleton v) kvs)
|
||||
{-# INLINABLE insert #-}
|
||||
|
||||
{-| Lookup a key by name and index
|
||||
{-| Look up a key by name and index
|
||||
|
||||
> lookup _ _ empty = Nothing
|
||||
> lookup k 0 (insert k v c) = Just v
|
||||
> lookup k n (insert k v c) = lookup k (n - 1) c -- 1 <= n
|
||||
> lookup k n (insert j v c) = lookup k n c -- k /= j
|
||||
> lookup k n (insert k _ c) = lookup k (n - 1) c -- 1 <= n
|
||||
> lookup k n (insert j _ c) = lookup k n c -- k /= j
|
||||
-}
|
||||
lookup :: Text -> Integer -> Context a -> Maybe a
|
||||
lookup _ !_ (Context [] ) =
|
||||
Nothing
|
||||
lookup x !n (Context ((k, v):kvs)) =
|
||||
if x == k
|
||||
then if n == 0
|
||||
then Just v
|
||||
else lookup x (n - 1) (Context kvs)
|
||||
else lookup x n (Context kvs)
|
||||
lookup k n (Context kvs) = do
|
||||
vs <- Data.Map.lookup k kvs
|
||||
lookupSeq (fromIntegral n) vs
|
||||
where
|
||||
-- Replace this `Data.Sequence.lookup` once that's available on Stackage LTS
|
||||
lookupSeq :: Int -> Seq a -> Maybe a
|
||||
lookupSeq n s =
|
||||
if n < Data.Sequence.length s
|
||||
then Just (Data.Sequence.index s n)
|
||||
else Nothing
|
||||
{-# INLINABLE lookup #-}
|
||||
|
||||
{-| Return all key-value associations as a list
|
||||
|
||||
> toList empty = []
|
||||
> toList empty = []
|
||||
> toList (insert k v ctx) = (k, v) : toList ctx
|
||||
-}
|
||||
toList :: Context a -> [(Text, a)]
|
||||
toList = getContext
|
||||
toList (Context kvs) = do
|
||||
(k, vs) <- Data.Map.toList kvs
|
||||
v <- Data.Foldable.toList vs
|
||||
return (k, v)
|
||||
{-# INLINABLE toList #-}
|
||||
|
|
Loading…
Reference in New Issue
Block a user