Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing a simple graph api #14

Merged
merged 35 commits into from
Aug 10, 2017
Merged

Conversation

thisiswhereitype
Copy link
Collaborator

No description provided.

@thisiswhereitype
Copy link
Collaborator Author

I have made a mistake with git and this code actually implements two graphs and but only uses the older one which was intended to be removed. I will have a commit up soon rectifying this.

@snowleopard
Copy link
Member

@thisiswhereitype No problem. Please let me know when the PR is ready for review.

…new graph. VHDL section has been disabled but I commit again before the pr is merged to fix it.
@thisiswhereitype
Copy link
Collaborator Author

Please see the graph implementation ready for review, as the commit message says there were some issues with the VHDL section so I have commented them out until they are resolved.
The graph implementation is now publicly exported through Pangraph but implemented in Pangraph.Internal.Graph I needed some helper functions for working with the other modules and have placed them in the former module.

@snowleopard
Copy link
Member

@thisiswhereitype Wait, but VHDL code and tests are important, we can't simply comment them out.

If you can't make the new design work for them, it means the PR is not yet ready to be merged.

Also, the fact that you need access to some of the non-exported internals hints that the current API may not be powerful/flexible enough.

Let's solve both of the above problems. First: we need to make the VHDL code/tests work.

Joe added 4 commits July 18, 2017 15:12
…ed in conversion to and from maps. Moved helper functions into the internal module. Added functions like and
…librarys have lots of fucntions for manipulating their respective instances
@thisiswhereitype
Copy link
Collaborator Author

thisiswhereitype commented Jul 19, 2017

Please could you provide feedback on the changes in Pangraph.Internal.Graph. I have removed the add and remove functions for the Pangraph type, instead the makePangraph verifies the graph is not malformed and has an Either return type accordingly. So users can extract from pangraph but not insert into it without constructing it again (at which point it is verified).

@thisiswhereitype thisiswhereitype self-assigned this Jul 19, 2017
@snowleopard
Copy link
Member

@thisiswhereitype Thank you, I hope I'll find time to do the review tonight.

@thisiswhereitype
Copy link
Collaborator Author

You can leave it until tomorrow, I have other branches.

Copy link
Member

@snowleopard snowleopard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite a few but mostly minor changes.

let graphVHDL = W2.writeGraphVhdl graphParsed
let simEnvVHDL = W1.writeEnvironmentVhdl graphParsed
Right graphParsed -> case graphParsed of
Left malformedEdges -> error $ show malformedEdges
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the case with malformed edges not considered an error at the top level? Is this because you'd like to give the user opportunity to recover from this error without failing? I can see how this can be useful, but I don't like that the user now have to do two steps to finally get to the graph.

I suggest you do the following instead. You currently have:

graphmlToPangraph :: BS.ByteString -> Either BS.ByteString (Either [MalformedEdge] Pangraph)

What about this instead?

-- Let's add a data type to capture errors
data PangraphError = ParseError BS.ByteString
                   | MalformedEdges [MalformedEdge]
                   | ... -- perhaps there are other types of errors

-- Pangraph errors can be shown, so the user can just print them if need be
instance Show PangraphError where ...

-- Now the user can get the graph in one step:
graphmlToPangraph :: BS.ByteString -> Either PangraphError Pangraph

src/Pangraph.hs Outdated
makeNodeWithID :: BS.ByteString -> Node
makeNodeWithID str = makeNode [makeAttribute ("id", str)]
-- import Data.Map.Strict (Map)
-- import qualified Data.Map.Strict as Map
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to commit this to the repository.

src/Pangraph.hs Outdated
) where

import Pangraph.Internal.Graph
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need the internal module? Does this module do anything useful?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember you mentioned you had some issues with exporting the right functions. Have you added the internal module simply to solve the export problem? If yes, don't do that. Move everything to the top-level module.


template:: [PT.Template]
template = PT.graphMLTemplate
graphmlToPangraph' :: BS.ByteString -> P.Pangraph
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an unsafe function, and it's useful to mark such functions with the unsafe prefix.

Let's rename it to unsafeGraphmlToPangraph. Or, even better, to unsafeParse, because the return type (Pangraph) is clear from the type signature, and the GraphML should be the name of the module instead.

vertexByID,
edgeByID,

-- -- Operations on Pangraph
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't commit commented out code to the repository. You can commit a comment instead, explaining that you plan to add more functions.

type Attribute = (Key, Value)
type Key = BS.ByteString
type Value = BS.ByteString
type MalformedEdge = (Edge, (Maybe Vertex, Maybe Vertex))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need a separate type synonym for malformed edges? I think you are giving too much attention to malformed edges. It's fine to simply return the EdgeID of the first malformed edge, so I suggest you drop this functionality, because it makes the API more complex.


-- List based constructors

makePangraph :: [Vertex] -> [Edge] -> Either [MalformedEdge] Pangraph
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would even go for [Vertex] -> [Edge] -> Maybe Pangraph in the first version of the API, explaining in the documentation that Nothing corresponds to the case where there is a malformed edge. No need to actually return it -- the user will be able to find it if need be.

import qualified Pangraph as P
import qualified Data.ByteString as BS
import Data.ByteString.Char8 (unpack)
import Data.ByteString.Char8 (unpack, pack)


type NodeName = String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to rename these type synonyms too and use "vertex" for consistency.

@@ -3,71 +3,90 @@
module Pangraph.XMLTemplate
( Template,
graphMLTemplate,
parseTemplateToAlga,
parseTemplateToPangraph
-- parseTemplateToAlga,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't commit dead code, just delete this.

-- A list of places to find nodes and edges.
data Template = XML [Node] [Edge]
-- A list of places to find nodes and extractEdges.
data Template = XML [NodeRule] [EdgeRule]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, please use "vertex" throughout the repository to avoid confusion.

@snowleopard
Copy link
Member

@thisiswhereitype Thanks for the PR! I've left a bunch of comments, please let me know if you have any questions. Ready to do the next review round.

…s, concrete implementations their instances are now in an internal file to avoid cyclic dependencies
@thisiswhereitype
Copy link
Collaborator Author

thisiswhereitype commented Jul 24, 2017

I have removed the dead code and added the errors as suggested.

In Pangraph.Internal.GraphType the core record types are exported into Pangraph.Internal.Graph and Pangraph.Internal.Error which then export the appropriate abstract types.
Pangraph then imports both and exports the external api. Meanwhile the other modules import Pangraph.Internal.Error in order to access error constructors which are not exported via Pangraph (the abstract types are however). So the only thing the user can do presently is show them.

I have not changed the makePangraph function (to Maybe Pangraph) yet as I think the issue is arising because the vertex and edge type are too tightly coupled between their data and implementation in data structures and this is making it hard to have separation of concerns between modules. Please could you review and I will elaborate if you are satisfied with these changes?

@snowleopard
Copy link
Member

Meanwhile the other modules import Pangraph.Internal.Error in order to access error constructors which are not exported via Pangraph (the abstract types are however). So the only thing the user can do presently is show them.

I think this is wrong: the user should be able to pattern match on errors in order to appropriately handle them, so I suggest you move their definition to Pangraph and export them.

@snowleopard
Copy link
Member

the vertex and edge type are too tightly coupled between their data and implementation in data structures and this is making it hard to have separation of concerns between modules.

I think your separation of Pangraph data structure into multiple modules is premature. I don't see much benefit yet, but it does introduce complexity, which seems unnecessary. Can you refactor into a single module? We can decompose later if need be.

Just to reiterate: we are going for the simplest possible solution that works. Once we have it, we'll proceed by refactoring it further (if need be).

Test.hs Outdated
showInstanceTestCase = ("Pangraph [Node [Attribute (\"id\",\"0\")],Node [Attribute (\"id\",\"1\")]] [Edge [Attribute (\"source\",\"0\"),Attribute (\"target\",\"1\")]]",
P.makePangraph [P.makeNode [P.makeAttribute ("id","0")],P.makeNode [P.makeAttribute ("id","1")]] [P.makeEdge [P.makeAttribute ("source","0"),P.makeAttribute ("target","1")]])
testShowInstance = do
literal <-fmap (head . lines) $ readFile "test/show-string.txt"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should avoid reading files during tests. There is no point to test readFile, we should focus on testing our own code instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a test that the show instance has not been changed between commits unexpectedly, I just moved string literal into the text file to remove clutter from the where clause.

Copy link
Collaborator Author

@thisiswhereitype thisiswhereitype Jul 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the avoidance of IO to make it clear exactly what is being tested? I think maybe the test directory should mirror the src directory in terms of testing this will allow the files to be clearer and do less IO.

test/Pangraph.hs
test/Pangraph/GraphML.hs
             /VHDL.hs

@snowleopard
Copy link
Member

@thisiswhereitype Thanks! I have a few more comments. But I think we are getting close :-)

@thisiswhereitype
Copy link
Collaborator Author

As the commit messages say I have updated the testing using HUnit and created a CLT to convert files into Haskell String Literals.

stack.yaml Outdated
@@ -1,6 +1,6 @@
extra-deps:
resolver: lts-8.22
flags: {}
extra-package-dbs: []
extra-package-dbs: [ ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover spaces?

@thisiswhereitype
Copy link
Collaborator Author

thisiswhereitype commented Aug 4, 2017

I have corrected the changes and added haddock markup to the API.

@thisiswhereitype
Copy link
Collaborator Author

Please review when you have chance, thank you.


import Data.ByteString (readFile)

import qualified Pangraph.GraphML.Parser as GraphML_P
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use GraphML as the qualified name


type NodeName = String
type NodeIndex = Int
-- | Writes a Pangraph to VHDL

writeGraphVhdl :: P.Pangraph -> String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name this simply write

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have opted to name these writeGraph and writeEnviroment and re-export these in Pangraph.VHDL which saves the user from using two imports. I also pack these into ByteStrings to save breaking changes later.

import Data.ByteString.Char8 (unpack)


type VertexName = String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a TODO here to switch to ByteString.

++ "\tEND COMPONENT;\n\n"

createSignals :: [P.Node] -> String
-- createRegisterGeneric :: Int -> String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a TODO to convert these large literals into the following form:

s x = unlines [ "qwe"
              , "xru" ++ show x
              , "iop" ]

@@ -0,0 +1,3492 @@
{-# LANGUAGE OverloadedStrings #-}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not go beyond 10-20 vertices in hard-coded tests.

…moved large VHDL test case, corrected import syntax in some modules
@thisiswhereitype
Copy link
Collaborator Author

I have addressed all the concerns raised. Is the approach taken with the VHDL module acceptable?

import qualified Pangraph.VHDL.EnvironmentWriter as W1
import qualified Pangraph.VHDL.GraphWriter as W2
import Data.ByteString.Char8 (pack)
import qualified Pangraph.VHDL as V
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's import as VHDL. If you see a V.foo in the code you'll need to guess what it means and/or look at the list of imports, whereas VHDL.foo is pretty self-explanatory.

@snowleopard
Copy link
Member

@thisiswhereitype Thanks! Just one more tweak and I'm ready to merge.

@thisiswhereitype
Copy link
Collaborator Author

thisiswhereitype commented Aug 10, 2017

I have made the change requested and I also moved the VHDL module into Pangraph.VHDL.Writer for consistent module naming. Please review @snowleopard.

@snowleopard snowleopard merged commit 56da512 into tuura:master Aug 10, 2017
@snowleopard
Copy link
Member

@thisiswhereitype Looks good, thank you. I've merged the PR.

@snowleopard
Copy link
Member

I haven't reviewed documentation -- please make sure it's up to date.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants