refactor: compile Hugr using abstract graph datatype#97
Conversation
This reverts commit 99a7416.
b242b34 to
0651887
Compare
0651887 to
365dddc
Compare
brat/Data/HugrGraph.hs
Outdated
| nameSupply :: Namespace | ||
| } deriving (Eq, Show) -- we probably want a better `show` | ||
|
|
||
| splitNamespace :: String -> State HugrGraph Namespace |
There was a problem hiding this comment.
It might be nice to implement instance FreshMonad (State HugrGraph a) from Brat.Naming
There was a problem hiding this comment.
See other comment - have indeed moved namespace out, so the instance FreshMonad would be something else...
brat/Data/HugrGraph.hs
Outdated
| _ -> error "addEdge to/from node not present" | ||
| where | ||
| addToMap :: Ord k => k -> v -> M.Map k [v] -> M.Map k [v] | ||
| addToMap k v m = M.insert k (v:(fromMaybe [] $ M.lookup k m)) m |
There was a problem hiding this comment.
Extra lookup could be avoided with M.alter (though still a bit awkward!)
addToMap k v m = M.alter k (\maybeVs -> Just (v : fromMaybe [] maybeVs)) mor
addToMap k v m = M.alter k (maybe (Just [v]) (fmap (v:))) mThere was a problem hiding this comment.
Got me going in the right direction....M.alter (Just . (v:) . fromMaybe []) k m is succinct and I think reasonably clear
There was a problem hiding this comment.
tho it got a bit more complex with the single-incoming-edge check (fromMaybe [] -> maybe [] chk)
brat/Data/HugrGraph.hs
Outdated
| nodes :: M.Map NodeId HugrOp, | ||
| edges_out :: M.Map NodeId [(Int, PortId NodeId)], | ||
| edges_in :: M.Map NodeId [(PortId NodeId, Int)], | ||
| nameSupply :: Namespace |
There was a problem hiding this comment.
Not sure if having the nameSupply here makes sense, it feels like it should be provided by the monad. The monadic functions here could be defined on e.g.:
freshNode :: (MonadState HugrGraph m, FreshMonad m) => NodeId -> String -> m Namespacebut I'm splitting hairs and that smells of premature abstraction
There was a problem hiding this comment.
I agree it's better to move the nameSupply out into the Compile monad as that let's up parametrize everything that doesn't use Namespace/create names by the node type - which will be better for #98 among others. So I've done that.
This parametrized freshNode seems pretty complex, but maybe very neat. What instance would I want - instance FreshMonad Compile? instance FrsehMonad (State (HugrGraph NodeId, Namespace)) ?? (I am slightly nervous of this. FreshMonad requires the !- operator to run in a sub-namespace, but when we do that we also want to run on a completely separate hugr so I do kinda feel that running a separate/inner monad is better...)
And would that requirement be MonadState (HugrGraph NodeId, Namespace) m, FreshMonad? Ah - no need to grab the Namespace out of the state, FreshMonad does that work for us? (I have not seen MonadState before, nor the | m -> s syntax in class Monad m => MonadState s (m :: Type -> Type) | m -> s where ....)
If it is just instance (MonadState (HugrGraph NodeId)) Compile....then all the things in HugrGraph.hs that are ... -> State (HugrGraph n/N) Result could be MonadState (HugrGraph n/N) m => ... -> m Result and we do away with onHugr, is that it? (So onHugr was my poor man's way of doing MonadState, but in this case the poor man's way is simple....)
There was a problem hiding this comment.
I did try instance (MonadState (HugrGraph NodeId)) Compile but got:
error: [GHC-46208]
Functional dependencies conflict between instance declarations:
instance MonadState (HugrGraph NodeId) Compile
-- Defined at Brat/Compile/Hugr.hs:75:10
instance [safe] Monad m => MonadState s (StateT s m)
-- Defined in ‘Control.Monad.State.Class’
There was a problem hiding this comment.
I think haskell is confused because Compile, being a state monad, already implements MonadState CompilationState and it doesn't know which one to choose
brat/Data/HugrGraph.hs
Outdated
| addEdge (src@(Port s o), tgt@(Port t i)) = state $ \h@HugrGraph {..} -> | ||
| ((), ) $ case (M.lookup s nodes, M.lookup t nodes) of | ||
| (Just _, Just _) -> h { | ||
| edges_out = addToMap s (o, tgt) edges_out, |
There was a problem hiding this comment.
We're not doing anything to avoid doubly-wiring a port, maybe there should be a check here?
There was a problem hiding this comment.
Can check the inport yes good plan, checking the output would require understanding the type which feels a bit out of scope for this HugrGraph
brat/Data/HugrGraph.hs
Outdated
| }) | ||
|
|
||
| setFirstChildren :: NodeId -> [NodeId] -> State HugrGraph () | ||
| setFirstChildren p cs = state $ \h -> let nch = M.alter (\Nothing -> Just cs) p (first_children h) |
There was a problem hiding this comment.
this state could be a modify :: (s -> s) -> State s ()
brat/Data/HugrGraph.hs
Outdated
| setOp :: NodeId -> HugrOp -> State HugrGraph () | ||
| -- Insist the parent exists | ||
| setOp name op = state $ \h@HugrGraph {parents, nodes} -> case M.lookup name parents of | ||
| Nothing -> error "name has no parent" |
There was a problem hiding this comment.
| Nothing -> error "name has no parent" | |
| Nothing -> error ("Node " ++ show name ++ " has no parent") |
brat/Data/HugrGraph.hs
Outdated
| parents = M.alter (\Nothing -> Just parent) (NodeId freshName) parents | ||
| }) | ||
|
|
||
| setFirstChildren :: NodeId -> [NodeId] -> State HugrGraph () |
There was a problem hiding this comment.
| setFirstChildren :: NodeId -> [NodeId] -> State HugrGraph () | |
| -- INVARIANT: first children for this node must not already be set | |
| setFirstChildren :: NodeId -> [NodeId] -> State HugrGraph () |
There was a problem hiding this comment.
Invariants are true afterwards too ;-). "ERRORS if already set"....in Rust this would be "PANICS if alreday set"
brat/Data/HugrGraph.hs
Outdated
| setFirstChildren p cs = state $ \h -> let nch = M.alter (\Nothing -> Just cs) p (first_children h) | ||
| in ((), h {first_children = nch}) | ||
|
|
||
| setOp :: NodeId -> HugrOp -> State HugrGraph () |
There was a problem hiding this comment.
| setOp :: NodeId -> HugrOp -> State HugrGraph () | |
| -- INVARIANT: The node op must not already be set | |
| setOp :: NodeId -> HugrOp -> State HugrGraph () |
brat/Brat/Compile/Hugr.hs
Outdated
| defNode <- addNode (show fnName ++ "_def") (OpDefn $ FuncDefn moduleNode (show fnName) funTy []) | ||
| registerFuncDef idNode (defNode, extra_call) | ||
| pure (body defNode) | ||
| ctr@Ctr {parent} <- freshNodeWithIO (show fnName ++ "_def") moduleNode |
There was a problem hiding this comment.
retain calling the parent "defNode" for clarity pls
brat/Data/HugrGraph.hs
Outdated
| nodeStackAndIndices :: StackAndIndices | ||
| nodeStackAndIndices = let just_root = (B0 :< (root, nodes M.! root), M.singleton root 0) | ||
| init = foldl addNode just_root (first_children root) | ||
| in foldl addNode init (M.keys parents) |
There was a problem hiding this comment.
Haskell folklore usually goes "Use foldr instead of foldl. If you really want foldl, use foldl1." Though I looked into it and couldn't convince myself that this use of foldl is problematic
There was a problem hiding this comment.
You mean foldl' not foldl1 but done, also combined the two uses - copying first_children root is harmless, it'll be at most two elements!
|
LGTM |
93d9302 to
00b4b16
Compare
Uses the HugrGraph ADT added in #97. `splice` replaces a HoleOp (ignoring the index) with a DFG-rooted Hugr of matching signature, i.e. inserts the DFG. `inlineDFG` flattens the result, if desired. I wasn't sure what the best approach was for dealing with new/old keys, but the `splice` method is general over both key types by taking a translation function, which gives some guarantee that we *are* translating the keys. `splice_prepend` (both NodeID Hugr's) and `splice_new` (arbitrary-keyed into NodeID) offer two possibilities. My hope ATM is that we don't need to deal with order edges since these are *only* added for nonlocal edges, and so we can do splicing/inlining *before* adding order edges. Tests are pretty basic here, i.e. about the simplest possible case, with/without inline.
Uh oh!
There was an error while loading. Please reload this page.