Skip to content

Commit 6ffa6ac

Browse files
committed
Merge pull request #1 from RoboNickBot/cleanup
Cleanup
2 parents 6dfc04d + b7a8d66 commit 6ffa6ac

File tree

5 files changed

+135
-32
lines changed

5 files changed

+135
-32
lines changed

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# A Gentle Introduction to Linked Lists
2+
3+
An interactive web-page demonstration of Linked Lists and how they are
4+
stored in memory based (very very closely) on a Java Applet written by
5+
[Michael Goldwasser](http://mathcs.slu.edu/~goldwasser/)
6+
7+
This was written as a SLU Senior Capstone Project for Fall 2014 and is
8+
released under a BSD-style two-clause license (see
9+
[LICENSE](LICENSE)).
10+
11+
It's now finished as far as the assignment goes, so I guess
12+
pull-requests are welcome! I've added a
13+
[feature backlog](backlog.org) if you want ideas for contribution.
14+
15+
#### Building
16+
17+
You'll need to build the [GHCJS](https://github.com/GHCJS/GHCJS)
18+
(which is kind of a pain right now). Follow that link for
19+
instructions.
20+
21+
When you have GHCJS, just ```make``` and then install any dependencies
22+
that cabal asks for (oh yeah, you also need cabal-install) and then
23+
```make``` again and it should work.
24+
25+
The files should all end up in a folder called
26+
```linked-list-dist```. Navigating a web-browser to
27+
```linked-list-dist/index.html``` will give you the demo.
28+
29+
#### Other ways to use it
30+
31+
Since building is a pain, you can also grab a ```.tgz``` from
32+
[releases](https://github.com/RoboNickBot/linked-list-web-demo/releases)
33+
or (for now) go to
34+
[Linked List Demo](http://octalsrc.net/demos/linked_list.html)
35+
36+
#### Code Guide
37+
38+
This is written in Haskell and uses the JQuery and Canvas libraries
39+
from the [GHCJS project](https://github.com/ghcjs) as well as
40+
[Reactive-Banana](https://www.haskell.org/haskellwiki/Reactive-banana)
41+
for some minimal FRP business.
42+
43+
All the JQuery and Canvas things are in ```JS.hs``` and all FRP
44+
happens in ```LinkedListDemo.hs``` (which is also the Main file).
45+
List parsing and Layout generation happens in ```Links.hs```.

backlog.org

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Some features I intend to implement:
2+
3+
(this is an Emacs Org-Mode notes file; I'm sorry if GitHub renders it
4+
weirdly)
5+
6+
* Improve randomInput function
7+
Empty cells tend to bunch up at the end
8+
9+
* Highlight cell-drawing relationships
10+
On mouse-over of boxes and arrows in the drawn list, they and the
11+
cell in the memory-edit area that they represent should highlight in
12+
some way as a quick indication of their relationship
13+
14+
* Improve sizing and centering of drawing area
15+
** Properly center the drawn lists in the drawing area
16+
** Get rid of that white-space at the bottom of the screen
17+
(while making sure not to make a scroll-bar appear)
18+
** Make the drawing auto-resize whenever the window-size changes
19+
20+
* General re-factoring
21+
Particularly in JS.hs, it's kind of a mess in there

src/Demo/Links.hs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,20 @@ import qualified Data.List as L (delete, length)
1616
randomEmptyCells = 2 :: Int
1717
randomValueRange = ('A','Z')
1818

19-
{- I think there is a lot of refactoring-opportunity in here,
20-
especially concerning the use of monads, but we'll leave that
21-
for later.
19+
{- I think there is a lot of refactoring-opportunity in this file,
20+
especially concerning the use of monads for parsing, but we'll
21+
leave that for later.
2222
-}
2323

24-
mismatches :: InputState -> InputState -> (Bool,[Int])
25-
mismatches (InSt i1 s1 h1 m1) (InSt i2 s2 h2 m2) =
24+
mismatches :: InputState -> Maybe InputState -> (Bool,[Int])
25+
mismatches (InSt i1 s1 h1 m1) (Just (InSt i2 s2 h2 m2)) =
2626
let headChanged = h1 /= h2
2727
f = (matchIndex m1 m2)
2828
in (headChanged, (foldr
2929
f
3030
[]
3131
[i1 .. (i1 + s1 - 1)] ))
32+
mismatches _ _ = (False,[]) -- if last InSt is Nothing, no change
3233

3334
matchIndex :: M.Map Int Cell -> M.Map Int Cell -> Int -> [Int] -> [Int]
3435
matchIndex c b i is = let f m = (fmap snd (M.lookup i m))
@@ -76,9 +77,9 @@ box _ _ = []
7677
{- Failure for arrow: (end list)
7778
1. the arrow cell is empty/non-existant
7879
2. the value of the arrow cell is not an Int
79-
2. the cell which the arrow points to is empty/invalid
80+
3. the cell which the arrow points to is empty/invalid
8081
Special failure: (add loopback and end list)
81-
3. the cell which the arrow points to is already seen
82+
4. the cell which the arrow points to is already seen
8283
-}
8384
arrow :: Step
8485
arrow (m,s) (Just (i,val)) =

src/Demo/Types.hs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
{- Some types and constructors for the project -}
2+
13
module Demo.Types ( InputState (..)
24
, emptyInput
35
, emptyCell
@@ -15,23 +17,23 @@ module Demo.Types ( InputState (..)
1517

1618
import qualified Data.Map as M (Map, empty, lookup, insert)
1719

18-
{- Here's a map of how these types are used:
20+
{- Here's a map of how the main types are used:
1921
20-
InputState ---> Diagram ---> Layout ----*----> Drawable
22+
InputState ---> Diagram ---> Layout ----*----> LayoutD
2123
/
2224
(canvas dimensions) ----*
2325
-}
2426

25-
-- (Cell Index, Cell contents)
27+
-- (cell Index, cell contents)
2628
type Cell = (Int, String)
27-
-- M.Map (Cell Index) Cell
29+
-- M.Map (cell Index) Cell
2830
type MemSt = M.Map Int Cell
2931

3032
mkMemSt :: [Cell] -> MemSt
3133
mkMemSt = foldr (\(i,s) -> M.insert i (i,s)) M.empty
3234

3335
{- InputState: The current state of all the inputs on the
34-
the webpage right now -}
36+
the webpage -}
3537
data InputState = InSt { startIndex :: Int
3638
, cellCount :: Int
3739
, headVal :: String
@@ -65,4 +67,6 @@ type LElem = (DElem, (Int, Int), (Int, Int))
6567
unit-size -}
6668
type Layout = [LElem]
6769

70+
{- LayoutD: The Layout transformed with the scale-size as a Double
71+
replacing the previous unit-size Ints -}
6872
type LayoutD = [(DElem, (Double, Double), (Double, Double))]

src/LinkedListDemo.hs

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,22 @@ defaultHeadIndex = 23
1414
defaultNumCells = 14
1515

1616
main = do initializePage (defaultHeadIndex, defaultNumCells)
17-
(draw, rando, gener, clicks, keys) <- mkSources
18-
wireButton draw sDrawButton (cullErrors >> readInputState)
17+
18+
-- init and connect reactive event-sources
19+
(draw, rando, gener, clicks, keys) <- mkSources
20+
wireButton draw sDrawButton (cullErrors
21+
>> readInputState)
1922
wireButton rando sRandomButton (cullErrors
2023
>> mkCanvas
21-
>>mkRandomInput
24+
>> mkRandomInput
2225
>> readInputState)
23-
wireButton
24-
gener
25-
sCellGen
26-
(cullErrors >> generateCells >> readInputState)
27-
wireClicks clicks
26+
wireButton gener sCellGen (cullErrors
27+
>> generateCells
28+
>> readInputState)
29+
wireClicks clicks -- does nothing for now
2830
wireKeys keys readInputState
31+
32+
-- build and "actuate" the reactive event network
2933
n <- compile (mkNetwork (draw, rando, gener, clicks, keys))
3034
actuate n
3135

@@ -36,6 +40,8 @@ generateCells =
3640
Right (start,size) -> initializePage (start,size)
3741
Left err -> printHighError err
3842

43+
{- Tests number of cells and starting index to make sure they're valid.
44+
Readability as Ints was already checked for in JS.hs code -}
3945
checkGenInfo :: (Int,Int) -> Either String (Int,Int)
4046
checkGenInfo (i,s)
4147
| i < 0 = Left "Starting Index cannot be negative"
@@ -48,50 +54,76 @@ initializePage (start,size) =
4854
>> mkCanvas
4955

5056
wireClicks _ = return () -- maybe implement this later?
57+
58+
-- Events responding to keypresses in the memory editing area
5159
wireKeys (addHandler, fire) f = do let handler _ = f >>= fire
5260
box <- sLowerControls
5361
keyup handler def box
5462

63+
-- convenience function for wiring all the buttons
5564
wireButton (addHandler, fire) button f = do
5665
let handler _ = f >>= fire
5766
b <- button
5867
click handler def b
5968

69+
-- sources are in the IO monad, so we have to do this?
6070
mkSources = do a <- newAddHandler
6171
b <- newAddHandler
6272
c <- newAddHandler
6373
d <- newAddHandler
6474
e <- newAddHandler
6575
return (a,b,c,d,e)
6676

77+
-- convenience
6778
addHandler = fst
6879
fire = snd
6980

70-
mkNetwork (drawSource, randomSource, genSource, clickSource, keySource) = do
81+
{- And now the fun stuff, describes the reactive "signal graph" of
82+
events and behaviors, mainly used here to control when cells
83+
are highlighted to show that they have been edited -}
84+
mkNetwork ( drawSource
85+
, randomSource
86+
, genSource
87+
, clickSource
88+
, keySource ) = do
89+
7190
eDraws <- fromAddHandler (addHandler drawSource)
7291
eRandoms <- fromAddHandler (addHandler randomSource)
7392
eGens <- fromAddHandler (addHandler genSource)
7493
eKeys <- fromAddHandler (addHandler keySource)
75-
let eResets = eRandoms `union` eGens
94+
95+
let -- some useful collections of event-sources
96+
eResets = eRandoms `union` eGens
7697
eInputs = eRandoms `union` eGens `union` eKeys
77-
--bInputState :: Behavior t InputState
78-
bInputState = stepper (emptyInput 5 20) eInputs
79-
eDrawnInputState = bInputState <@ eDraws
80-
bLastInputState = stepper (emptyInput 5 20)
81-
(eDrawnInputState `union` eResets)
98+
99+
-- convenience for processing LastInputStates
100+
bNothing :: Behavior t (Maybe InputState)
101+
bNothing = pure Nothing
102+
-- clicking 'draw' should only count if it actually draws
103+
bTest :: Behavior t (InputState -> Maybe InputState)
104+
bTest = pure (\inState -> case parseInput inState of
105+
Left _ -> Nothing
106+
Right _ -> Just inState)
107+
108+
bInputState = stepper (emptyInput 5 20) eInputs
109+
bLastInputState =
110+
stepper Nothing
111+
((bTest <@> eDraws) `union` (bNothing <@ eResets))
82112
bDirty = mismatches <$> bInputState <*> bLastInputState
113+
83114
cIn <- changes bInputState
84115
cLIn <- changes bLastInputState
85116
cDirty <- changes bDirty
86117

87-
88-
--reactimate' $ fmap (\is -> process is) eISChanged
89-
--reactimate' <$> (fmap (fmap process) eISChanged)
118+
-- Draw the list!
90119
reactimate (fmap (\a -> mkCanvas >> process a) eDraws)
91-
reactimate' $ fmap (\d -> mark d >> print (show d) >> return ()) <$> cDirty
120+
-- Mark the "dirty" edited cells (or unmark them if clean)
121+
reactimate' (fmap (\d -> mark d >> return ()) <$> cDirty)
122+
123+
-- (These are for debugging purposes and print only to the console)
92124
reactimate' $ fmap (\d -> print ("InputState: " ++ show d)) <$> cIn
93125
reactimate' $ fmap (\d -> print ("LastState: " ++ show d)) <$> cLIn
94-
--reactimate (fmap (\_ -> fmap process bInputState) eDraws)
126+
95127

96128
process :: InputState -> IO ()
97129
process = displayOutput . fmap mkLayout . parseInput

0 commit comments

Comments
 (0)