├── .gitignore
├── Test.hs
├── Test1.hs
├── app
└── Main.hs
├── chameleon
├── chameleon.cabal
├── hie.yaml
├── package.yaml
├── readme.md
├── src
├── Binding.hs
├── Builtin.hs
├── Constraint.hs
├── FieldOrdering.hs
├── Graph.hs
├── Instance.hs
├── JsonInstance.hs
├── Kanren.hs
├── Nameable.hs
├── Reasoning.hs
├── Run.hs
├── Scope.hs
└── Typing.hs
├── stack.yaml
├── stack.yaml.lock
├── static
├── Debugger.jsx
├── Editor.jsx
├── MenuBar.jsx
├── TabReport.jsx
├── Toggle.jsx
├── TypeSig.jsx
├── build
│ ├── challenge.html
│ ├── consent.html
│ ├── explanatory.html
│ ├── favicon.ico
│ ├── feedback.html
│ ├── index.html
│ ├── intro.html
│ ├── introduction.html
│ ├── logo.png
│ ├── main.css
│ ├── monash-logo.svg
│ ├── out.js
│ ├── out.js.map
│ ├── playground.html
│ ├── playground.js
│ ├── playground.js.map
│ ├── study.html
│ ├── tutorial.html
│ ├── tutorial.svg
│ └── tutorial
│ │ └── pngs
│ │ ├── Card Active.png
│ │ ├── Card Box.png
│ │ ├── Card Inactive.png
│ │ ├── Card X.png
│ │ ├── Card Y.png
│ │ ├── Debugging Message.png
│ │ ├── Deduction Example Step 1.png
│ │ ├── Deduction Example Step 2.png
│ │ ├── Deduction Example Step 3.png
│ │ ├── Deduction Example Step 4.png
│ │ ├── Deduction Step Active.png
│ │ ├── Deduction Step Annotation.png
│ │ ├── Deduction Step Buttons.png
│ │ ├── Deduction Step Highlight.png
│ │ ├── Deduction Step Inactive.png
│ │ ├── Deduction Step Menubar Button.png
│ │ ├── Deduction Step UI.png
│ │ ├── Expand Level 1.png
│ │ ├── Expand Level 2.png
│ │ ├── Highlight Left.png
│ │ ├── Highlight Right.png
│ │ ├── Highlights All.png
│ │ ├── Highlights Blue.png
│ │ ├── Highlights Orange.png
│ │ ├── Menubar.png
│ │ ├── Original.png
│ │ ├── Possible Types 1.png
│ │ ├── Possible Types 2.png
│ │ └── Possible Types.png
├── challenge.jsx
├── cm.js
├── code.js
├── debuggerSlice.js
├── index.jsx
├── package-lock.json
├── package.json
├── playground.jsx
├── report.js
├── store.js
└── util.js
└── test
├── Intro.hs
├── NewTest1.hs
├── NewTest2.hs
├── NewTest3.hs
├── Test2.hs
├── Test3.hs
├── Test4.hs
├── Test5.hs
├── Test6.hs
├── Test7.hs
├── Test8.hs
└── TypeClassBasic.hs
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .stack-work
3 | node_modules
4 | .env
5 | .envrc
--------------------------------------------------------------------------------
/Test.hs:
--------------------------------------------------------------------------------
1 | divides x y = y `mod` x == 0
2 |
3 |
4 | dropEvery [] _ = []
5 | dropEvery (x:xs) n = dropEvery' (x:xs) n 1
6 |
7 | dropEvery' :: [Int] -> Int -> Int -> [Int]
8 | dropEvery' [] _ _ = []
9 | dropEvery' (x:xs) n i =
10 | let current =
11 | if n `divides` i
12 | then []
13 | else [x]
14 | in current : dropEvery' xs n (i+1)
--------------------------------------------------------------------------------
/Test1.hs:
--------------------------------------------------------------------------------
1 | toWeekday n = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] !! n
2 |
3 | seperateByComma :: [String] -> String
4 | seperateByComma [] = ""
5 | seperateByComma [x] = x
6 | seperateByComma (x : xs) = x ++ "," ++ seperateByComma xs
7 |
8 | range xs
9 | | length xs < 3 = seperateByComma xs
10 | | otherwise = head xs ++ "-" ++ last xs
11 |
12 | dayRange :: [Int] -> [String]
13 | dayRange days =
14 | let grouped = groupBy' (\a b -> a + 1 == b) days
15 | in map (range . toWeekday) grouped
16 |
17 | -- unlike groupBy which compares any element
18 | -- to the first,
19 | -- groupBy' compares any element to the last
20 | -- element
21 | groupBy' :: (a -> a -> Bool) -> [a] -> [[a]]
22 | groupBy' f (x : xs) =
23 | let go f (x : xs) ((a : as) : bs) =
24 | if f a x
25 | then go f xs ((x : a : as) : bs)
26 | else go f xs ([x] : (a : as) : bs)
27 | go _ [] as = reverse (map reverse as)
28 | in go f xs [[x]]
--------------------------------------------------------------------------------
/app/Main.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 |
3 | module Main where
4 |
5 | import qualified Data.ByteString.Lazy.Char8 as BS
6 | import Data.Monoid (mconcat)
7 | import Control.Monad.IO.Class
8 | import qualified Data.Text.Lazy as T
9 | import JsonInstance
10 | import Run hiding (main)
11 | import Web.Scotty
12 | ( ScottyM,
13 | body,
14 | file,
15 | get,
16 | json,
17 | param,
18 | post,
19 | regex,
20 | scotty,
21 | setHeader,
22 | )
23 |
24 | main = scotty 5000 (
25 | typecheck
26 | >> home
27 | >> sourceMap
28 | >> js
29 | >> css
30 | >> svg
31 | >> jpg
32 | >> png
33 | >> favicon
34 | >> page
35 | )
36 |
37 | typecheck :: ScottyM ()
38 | typecheck = post "/typecheck" $ do
39 | content <- body
40 | let result = processFile (BS.unpack content)
41 | json result
42 |
43 |
44 |
45 |
46 | home :: ScottyM ()
47 | home = get "/" $ do
48 | file "static/build/index.html"
49 |
50 | js :: ScottyM ()
51 | js = get (regex "^.*\\.js$") $ do
52 | path <- param "0"
53 | let filename = "static/build" `T.append` path
54 | setHeader "Content-Type" "application/javascript"
55 | file (T.unpack filename)
56 |
57 | css :: ScottyM ()
58 | css = get (regex "^.*\\.css") $ do
59 | path <- param "0"
60 | let filename = "static/build" `T.append` path
61 | setHeader "Content-Type" "text/css"
62 | file (T.unpack filename)
63 |
64 |
65 | svg :: ScottyM ()
66 | svg = get (regex "^.*\\.svg") $ do
67 | path <- param "0"
68 | let filename = "static/build" `T.append` path
69 | setHeader "Content-Type" "image/svg+xml"
70 | file (T.unpack filename)
71 |
72 | jpg :: ScottyM ()
73 | jpg = get (regex "^.*\\.jpg") $ do
74 | path <- param "0"
75 | let filename = "static/build" `T.append` path
76 | setHeader "Content-Type" "image/jpeg"
77 | file (T.unpack filename)
78 |
79 | png :: ScottyM ()
80 | png = get (regex "^.*\\.png") $ do
81 | path <- param "0"
82 | let filename = "static/build" `T.append` path
83 | setHeader "Content-Type" "image/png"
84 | file (T.unpack filename)
85 |
86 | favicon :: ScottyM ()
87 | favicon = get "/favicon.ico" $ do
88 | setHeader "Content-Type" "image/vnd.microsoft.icon"
89 | file "static/build/favicon.ico"
90 |
91 | sourceMap :: ScottyM ()
92 | sourceMap = get (regex "^.*\\.js\\.map") $ do
93 | path <- param "0"
94 | let filename = "static/build" `T.append` path
95 | setHeader "Content-Type" "application/json"
96 | file (T.unpack filename)
97 |
98 | page :: ScottyM ()
99 | page = get "/:page" $ do
100 | pageName <- param "page"
101 | let filename = "static/build/" `T.append` pageName `T.append` ".html"
102 | file (T.unpack filename)
--------------------------------------------------------------------------------
/chameleon:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maybetonyfu/chameleon/d7664303a8b4bf180b9dea4a64226ba0f9aa877b/chameleon
--------------------------------------------------------------------------------
/chameleon.cabal:
--------------------------------------------------------------------------------
1 | cabal-version: 1.12
2 |
3 | -- This file has been generated from package.yaml by hpack version 0.34.4.
4 | --
5 | -- see: https://github.com/sol/hpack
6 |
7 | name: chameleon
8 | version: 0.1.0.0
9 | description: Please see the README on GitHub
10 | homepage: https://github.com/maybetonyfu/chameleon#readme
11 | bug-reports: https://github.com/maybetonyfu/chameleon/issues
12 | author: Tony Fu
13 | maintainer: tonyfu@fastmail.com
14 | copyright: 2021 Tony Fu
15 | license: BSD3
16 | build-type: Simple
17 |
18 | source-repository head
19 | type: git
20 | location: https://github.com/maybetonyfu/chameleon
21 |
22 | library
23 | exposed-modules:
24 | Binding
25 | Builtin
26 | Constraint
27 | FieldOrdering
28 | Graph
29 | Instance
30 | JsonInstance
31 | Kanren
32 | Nameable
33 | Reasoning
34 | Run
35 | Scope
36 | Typing
37 | other-modules:
38 | Paths_chameleon
39 | hs-source-dirs:
40 | src
41 | build-depends:
42 | Agda
43 | , aeson
44 | , base
45 | , bytestring
46 | , containers
47 | , haskell-src-exts
48 | , lens
49 | , scotty
50 | , string-qq
51 | , text
52 | , transformers
53 | default-language: Haskell2010
54 |
55 | executable chameleon
56 | main-is: Main.hs
57 | other-modules:
58 | Paths_chameleon
59 | hs-source-dirs:
60 | app
61 | ghc-options: -threaded -rtsopts -with-rtsopts=-N
62 | build-depends:
63 | Agda
64 | , aeson
65 | , base
66 | , bytestring
67 | , chameleon
68 | , containers
69 | , haskell-src-exts
70 | , lens
71 | , scotty
72 | , string-qq
73 | , text
74 | , transformers
75 | default-language: Haskell2010
76 |
--------------------------------------------------------------------------------
/hie.yaml:
--------------------------------------------------------------------------------
1 | cradle:
2 | stack:
--------------------------------------------------------------------------------
/package.yaml:
--------------------------------------------------------------------------------
1 | name: chameleon
2 | version: 0.1.0.0
3 | github: "maybetonyfu/chameleon"
4 | license: BSD3
5 | author: "Tony Fu"
6 | maintainer: "tonyfu@fastmail.com"
7 | copyright: "2021 Tony Fu"
8 |
9 | # Metadata used when publishing your package
10 | # synopsis: Short description of your package
11 | # category: Web
12 |
13 | # To avoid duplicated efforts in documentation and dealing with the
14 | # complications of embedding Haddock markup inside cabal files, it is
15 | # common to point users to the README.md file.
16 | description: Please see the README on GitHub
17 |
18 | dependencies:
19 | - aeson
20 | - base
21 | - bytestring
22 | - containers
23 | - haskell-src-exts
24 | - transformers
25 | - Agda
26 | - scotty
27 | - string-qq
28 | - lens
29 | - text
30 |
31 | library:
32 | source-dirs: src
33 |
34 | executables:
35 | chameleon:
36 | main: Main.hs
37 | source-dirs: app
38 | ghc-options:
39 | - -threaded
40 | - -rtsopts
41 | - -with-rtsopts=-N
42 | dependencies:
43 | - chameleon
44 |
45 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Chameleon
2 |
3 |
4 | A tool to make solving type errors in Haskell simple and fun
5 |
6 | ## Why Chameleon
7 |
8 | - Human-Centered Methods
9 |
10 | We improve each feature in Chameleon based on the feedback from the Haskell community. Debugging idioms in Chameleon has been tested individually and in combinations.
11 |
12 | - Multi-location Type Errors
13 |
14 | While many type systems try to pinpoint one exact error location in the code, the accuracy is often hit or miss. Chameleon tries to narrow down to a few suspects and asks the programmer to identify the real culprit. While both approaches have pros and cons, we believe Chameleon is more flexible and catches bugs faster.
15 |
16 | - Unbiased Type Errors
17 |
18 | Instead of assuming one type is "Expected" and one type is "Actual", Chameleon will report two equally possible alternatives that type errors can happen. Many techniques have been proposed to solve this problem (Known as left-right bias) on type solver level. Chameleon combines the type solver capable of eliminating this bias and smart visual cues to distinguish the evidence for one type and the other.
19 |
20 | - Step-by-step Debugging
21 |
22 | The deduction step is a tool to peek inside the type checking engine. It shows step-by-step reasoning that explains why one type cannot reconcile with another in simple language. Chameleon's interactive interface allows users to make incremental assumptions and see how that affects the typing of the whole program.
23 |
24 |
25 | - More are coming!
26 |
27 | Hang tight! We are working on more features to make Chameleon even better!
28 |
29 | ## Playground
30 |
31 | The chameleon playground is an online editor for you to test out some of the chameleon features.
32 |
33 | [Go to playground](https://chameleon.typecheck.me/playground)
34 |
--------------------------------------------------------------------------------
/src/Builtin.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE QuasiQuotes #-}
2 |
3 | module Builtin where
4 |
5 | import Data.String.QQ
6 |
7 | builtin :: String
8 | builtin =
9 | [s|
10 |
11 | mod :: Int -> Int -> Int
12 | mod = undefined
13 |
14 | div :: Int -> Int -> Int
15 | div = undefined
16 |
17 | (/=) :: a -> a -> Bool
18 | (/=) = undefined
19 |
20 | data Ordering = LT | EQ | GT
21 |
22 | data IO a = IO a
23 |
24 | data Maybe a = Nothing | Just a
25 |
26 | compare :: a -> a -> Ordering
27 | compare = undefined
28 |
29 | (<) :: a -> a -> Bool
30 | (<) = undefined
31 |
32 | (<=) :: a -> a -> Bool
33 | (<=) = undefined
34 |
35 | (>) :: a -> a -> Bool
36 | (>) = undefined
37 |
38 | (>=) :: a -> a -> Bool
39 | (>=) = undefined
40 |
41 | max :: a -> a -> a
42 | max = undefined
43 |
44 | min :: a -> a -> a
45 | min = undefined
46 |
47 | (==) :: a -> a -> Bool
48 | (==) = undefined
49 |
50 | (+) :: Int -> Int -> Int
51 | (+) = undefined
52 |
53 | (-) :: Int -> Int -> Int
54 | (-) = undefined
55 |
56 | (*) :: Int -> Int -> Int
57 | (*) = undefined
58 |
59 | negate :: a -> a
60 | negate = undefined
61 |
62 | abs :: a -> a
63 | abs = undefined
64 |
65 | signum :: a -> a
66 | signum = undefined
67 |
68 | fromInteger :: Integer -> a
69 | fromInteger = undefined
70 |
71 | id :: a -> a
72 | id = undefined
73 |
74 | map :: (a -> b) -> [a] -> [b]
75 | map = undefined
76 |
77 | not :: Bool -> Bool
78 | not = undefined
79 |
80 | (&&) :: Bool -> Bool -> Bool
81 | (&&) = undefined
82 |
83 | (||) :: Bool -> Bool -> Bool
84 | (||) = undefined
85 |
86 | otherwise :: Bool
87 | otherwise = undefined
88 |
89 | error :: [Char] -> a
90 | error = undefined
91 |
92 | length :: [a] -> Int
93 | length = undefined
94 |
95 | toUpper :: Char -> Char
96 | toUpper = undefined
97 |
98 | toLower :: Char -> Char
99 | toLower = undefined
100 |
101 | (++) :: [a] -> [a] -> [a]
102 | (++) = undefined
103 |
104 | filter :: (a -> Bool) -> [a] -> [a]
105 | filter = undefined
106 |
107 | lines :: [Char] -> [[Char]]
108 | lines = undefined
109 |
110 | unlines :: [[Char]] -> [Char]
111 | unlines = undefined
112 |
113 | (<>) :: a -> a -> a
114 | (<>) = undefined
115 |
116 | mempty :: a
117 | mempty = undefined
118 |
119 | mappend :: a -> a -> a
120 | mappend = undefined
121 |
122 | mconcat :: [a] -> a
123 | mconcat = undefined
124 |
125 | fmap :: (a -> b) -> f a -> f b
126 | fmap = undefined
127 |
128 | pure :: a -> f a
129 | pure = undefined
130 |
131 | (<*>) :: f (a -> b) -> f a -> f b
132 | (<*>) = undefined
133 |
134 | (>>=) :: m a -> (a -> m b) -> m b
135 | (>>=) = undefined
136 |
137 | (>>) :: m a -> m b -> m b
138 | (>>) = undefined
139 |
140 | return :: a -> m a
141 | return = undefined
142 |
143 | putStrLn :: [Char] -> IO ()
144 | putStrLn = undefined
145 |
146 | zip :: [a] -> [b] -> [(a, b)]
147 | zip = undefined
148 |
149 | readFile :: [Char] -> IO [Char]
150 | readFile = undefined
151 |
152 | toEnum :: Int -> a
153 | toEnum = undefined
154 |
155 | fromEnum :: a -> Int
156 | fromEnum = undefined
157 |
158 | fst :: (a, b) -> a
159 | fst = undefined
160 |
161 | snd :: (a, b) -> b
162 | snd = undefined
163 |
164 | (^) :: Int -> Int -> Int
165 | (^) = undefined
166 |
167 | rem :: Int -> Int -> Int
168 | rem = undefined
169 |
170 | getContents :: IO [Char]
171 | getContents = undefined
172 |
173 | ($) :: (a -> b) -> a -> b
174 | ($) = undefined
175 |
176 | reverse :: [a] -> [a]
177 | reverse = undefined
178 |
179 | foldr :: (a -> b -> b) -> b -> [a] -> b
180 | foldr = undefined
181 |
182 | foldl' :: (b -> a -> b) -> b -> [a] -> b
183 | foldl' = undefined
184 |
185 | foldl :: (b -> a -> b) -> b -> [a] -> b
186 | foldl = undefined
187 |
188 | head :: [a] -> a
189 | head = undefined
190 |
191 | last :: [a] -> a
192 | last = undefined
193 |
194 | init :: [a] -> [a]
195 | init = undefined
196 |
197 | tail :: [a] -> [a]
198 | tail = undefined
199 |
200 | elem :: a -> [a] -> Bool
201 | elem = undefined
202 |
203 | show :: a -> [Char]
204 | show = undefined
205 |
206 | type String = [Char]
207 |
208 | permutations :: [a] -> [[a]]
209 | permutations = undefined
210 |
211 | (.) :: (b -> c) -> (a -> b) -> a -> c
212 | (.) = undefined
213 |
214 | take :: n -> [a] -> [a]
215 | take = undefined
216 |
217 | drop :: n -> [a] -> [a]
218 | drop = undefined
219 |
220 |
221 | and :: [Bool] -> Bool
222 | and = undefined
223 |
224 | or :: [Bool] -> Bool
225 | or = undefined
226 |
227 | (!!) :: [a] -> Int -> a
228 | (!!) = undefined
229 | |]
230 |
--------------------------------------------------------------------------------
/src/Constraint.hs:
--------------------------------------------------------------------------------
1 | module Constraint where
2 |
3 | import qualified Data.Map as Map
4 | import Data.Maybe
5 | import Debug.Trace
6 | import Kanren
7 |
8 | walkUpdateTags :: Term -> Subst -> Subst
9 | walkUpdateTags Atom {} subs = subs
10 | walkUpdateTags Unit subs = subs
11 | walkUpdateTags (Pair x y _) subs = walkUpdateTags y (walkUpdateTags x subs)
12 | walkUpdateTags (Var x tags) subs =
13 | case Map.lookup x subs of
14 | Nothing -> subs
15 | Just t -> trace ("\nFor " ++ x ++ " -> " ++ show t ++ " with tags " ++ show tags) $
16 | walkUpdateTags t (Map.insert x (appendTags tags t) subs)
17 |
18 | walkSyncTermTags :: Subst -> Subst
19 | walkSyncTermTags subs =
20 | let addTerm (Var v tags) mapping = Map.insertWith (++) (var v) tags mapping
21 | addTerm (Atom a tags) mapping = Map.insertWith (++) (atom a) tags mapping
22 | addTerm (Pair x y tags) mapping = Map.insertWith (++) (pair (setTags [] x) (setTags [] y)) tags (addTerm y . addTerm x $ mapping)
23 | addTerm _ mapping = mapping
24 | lookUpAndSetTag (Var v _) mapping = Var v (fromMaybe [] (Map.lookup (var v) mapping))
25 | lookUpAndSetTag (Atom a _) mapping = Atom a (fromMaybe [] (Map.lookup (atom a) mapping))
26 | lookUpAndSetTag (Pair x y _) mapping =
27 | Pair
28 | (lookUpAndSetTag x mapping)
29 | (lookUpAndSetTag y mapping)
30 | (fromMaybe [] (Map.lookup (pair (setTags [] x) (setTags [] y)) mapping))
31 | lookUpAndSetTag Unit mapping = Unit
32 | tagmap = foldr addTerm Map.empty (Map.elems subs ++ Map.elems subs)
33 | reinsertTags (key, term) sub = Map.insert key (lookUpAndSetTag term tagmap) sub
34 | in trace ("\nTagMap:\n" ++ unlines (map show (Map.toList tagmap)) ++ "\n") $
35 | foldr reinsertTags subs (Map.toList subs)
36 |
37 | deriveRequirement :: Subst -> Subst
38 | deriveRequirement subs =
39 | let allKeys = Map.keys subs
40 | unifiedTags = trace ("\nAll keys:\n" ++ show allKeys) foldr (walkUpdateTags . var) (walkSyncTermTags subs) allKeys
41 | in trace ("\nUnified Tags:\n" ++ unlines (map show (Map.toList unifiedTags)) ++ "\n") $ walkSyncTermTags unifiedTags
--------------------------------------------------------------------------------
/src/FieldOrdering.hs:
--------------------------------------------------------------------------------
1 | module FieldOrdering where
2 |
3 | import Debug.Trace
4 | import Language.Haskell.Exts.Parser
5 | import Language.Haskell.Exts.Pretty
6 | import Language.Haskell.Exts.Syntax
7 | import Nameable
8 | import System.Environment
9 |
10 | processFile :: String -> IO ()
11 | processFile filepath = do
12 | contents <- readFile filepath
13 | let pResult = parseModuleWithMode parseMode contents
14 | parseMode = defaultParseMode {parseFilename = filepath}
15 | case pResult of
16 | ParseOk hModule -> do
17 | print "OK"
18 | let order = getFieldOrdering hModule
19 | mapM_ print order
20 | ParseFailed srcLoc message ->
21 | putStrLn $
22 | unlines
23 | [ prettyPrint srcLoc,
24 | message
25 | ]
26 |
27 | type FieldOrdering = (String, Int)
28 |
29 | class HasFieldOrdering f where
30 | getFieldOrdering :: f a -> [FieldOrdering]
31 |
32 | instance HasFieldOrdering Module where
33 | getFieldOrdering (Module _ _ _ _ decls) = concatMap getFieldOrdering decls
34 | getFieldOrdering _ = error "Not a module"
35 |
36 | instance HasFieldOrdering Decl where
37 | getFieldOrdering (DataDecl _ _ _ _ conDecls _) = concatMap getFieldOrdering conDecls
38 | getFieldOrdering _ = []
39 |
40 | instance HasFieldOrdering QualConDecl where
41 | getFieldOrdering (QualConDecl _ _ _ conDecl) = getFieldOrdering conDecl
42 |
43 | instance HasFieldOrdering ConDecl where
44 | getFieldOrdering (RecDecl _ name fields) =
45 | let filedsNames = concatMap (\(FieldDecl _ names _) -> map getName names) fields
46 | in zip filedsNames (take (length filedsNames) [0 ..])
47 | getFieldOrdering _ = []
48 |
49 | main :: IO ()
50 | main = do
51 | args <- getArgs
52 | let path = head args
53 | processFile path
54 |
--------------------------------------------------------------------------------
/src/Graph.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TemplateHaskell #-}
2 |
3 | module Graph where
4 |
5 | import qualified Data.Set as Set
6 | import Data.Maybe
7 | import Control.Lens
8 |
9 | data Graph a = Graph {
10 | _nodes :: Set.Set a,
11 | _edges :: Set.Set (a, a)
12 | } deriving Show
13 | makeLenses ''Graph
14 |
15 | emptyGraph :: Graph a
16 | emptyGraph = Graph {
17 | _nodes = Set.empty,
18 | _edges = Set.empty
19 | }
20 |
21 | addNode :: Ord a => a -> Graph a -> Graph a
22 | addNode a = over nodes (Set.insert a)
23 |
24 | addEdge :: Ord a => a -> a -> Graph a -> Graph a
25 | addEdge a1 a2 graph = over edges (Set.insert (min a1 a2, max a1 a2)) (addNode a2 (addNode a1 graph))
26 |
27 | reachable :: Ord a => a -> Graph a -> [a]
28 | reachable a graph = concat . Set.toList . Set.insert [a] . Set.map (\(x,y) -> if x == a then [y] else [x | y == a]) $ view edges graph
--------------------------------------------------------------------------------
/src/Instance.hs:
--------------------------------------------------------------------------------
1 | module Instance where
2 |
3 | import Binding hiding (main, processFile)
4 | import Debug.Trace
5 | import Kanren
6 | import Language.Haskell.Exts.Parser
7 | import Language.Haskell.Exts.Pretty
8 | import Language.Haskell.Exts.SrcLoc
9 | import Language.Haskell.Exts.Syntax
10 | import Nameable
11 | import System.Environment
12 |
13 | processFile :: String -> IO ()
14 | processFile filepath = do
15 | contents <- readFile filepath
16 | let pResult = parseModuleWithMode parseMode contents
17 | parseMode = defaultParseMode {parseFilename = filepath}
18 | case pResult of
19 | ParseOk hModule -> do
20 | print "OK"
21 | let bindings = moduleBindings hModule
22 | cons = getInstances bindings hModule
23 | res = run1 [] (hasInstance cons "X" (Pair (atom "Maybe") (var "abcd") []))
24 | print res
25 | ParseFailed srcLoc message ->
26 | putStrLn $
27 | unlines
28 | [ prettyPrint srcLoc,
29 | message
30 | ]
31 |
32 | data Instance = Instance {tcName :: String, tcVar :: Term, tcAdditional :: [Instance] -> Term -> Goal}
33 |
34 | class HasInstance f where
35 | getInstances :: [(Binding, Int)] -> f SrcSpanInfo -> [Instance]
36 |
37 | instance HasInstance Module where
38 | getInstances bindings (Module _ _ _ _ decls) = concatMap (getInstances bindings) decls
39 | getInstances bindings _ = error "Not a module"
40 |
41 | instance HasInstance Decl where
42 | getInstances bindings (InstDecl _ _ instanceRule _) = getInstances bindings instanceRule
43 | getInstances bindings _ = []
44 |
45 | instance HasInstance InstRule where
46 | getInstances bindings (IParen _ insRule) = getInstances bindings insRule
47 | getInstances bindings (IRule _ _ maybeContext (IHApp _ (IHCon _ qname) typeDef)) =
48 | let term = typeToTerm bindings typeDef
49 | in case maybeContext of
50 | Nothing -> [Instance (getName qname) term (\_ _ -> succeeds)]
51 | Just ctx ->
52 | case ctx of
53 | (CxSingle _ (TypeA _ t)) ->
54 | let (Pair x y _) = typeToTerm bindings t
55 | tVar = y
56 | className = atomToString x
57 | fun = \ins t ->
58 | callFresh
59 | ( \t' ->
60 | let newTerm = replaceTerm tVar t' term
61 | in conj2 (t === newTerm) (hasInstance ins className t')
62 | )
63 | in [Instance (getName qname) term fun]
64 | _ -> []
65 | getInstances bindings _ = error "Multiple parameter type class is not supported"
66 |
67 | typeToTerm :: [(Binding, Int)] -> Type SrcSpanInfo -> Term
68 | typeToTerm bindings (TyVar (SrcSpanInfo sp _) name) = var $ uniqueNameFromBinding bindings Nothing (getName name) sp
69 | typeToTerm bindings (TyCon _ qname) = atom (getName qname)
70 | typeToTerm bindings (TyList _ t) = lstOf (typeToTerm bindings t)
71 | typeToTerm bindings (TyFun _ t1 t2) = funOf [typeToTerm bindings t1, typeToTerm bindings t2]
72 | typeToTerm bindings (TyTuple _ _ ts) = tupOf . map (typeToTerm bindings) $ ts
73 | typeToTerm bindings (TyUnboxedSum _ ts) = tupOf . map (typeToTerm bindings) $ ts
74 | typeToTerm bindings (TyApp _ t1 t2) = Pair (typeToTerm bindings t1) (typeToTerm bindings t2) []
75 | typeToTerm bindings (TyParen _ t) = typeToTerm bindings t
76 | typeToTerm _ _ = error "Unsupported type"
77 |
78 | varsFromType :: [(Binding, Int)] -> Type SrcSpanInfo -> [Term]
79 | varsFromType bindings (TyVar (SrcSpanInfo sp _) name) = [var $ uniqueNameFromBinding bindings Nothing (getName name) sp]
80 | varsFromType bindings (TyCon _ qname) = []
81 | varsFromType bindings (TyList _ t) = varsFromType bindings t
82 | varsFromType bindings (TyFun _ t1 t2) = varsFromType bindings t1 ++ varsFromType bindings t2
83 | varsFromType bindings (TyTuple _ _ ts) = concatMap (varsFromType bindings) ts
84 | varsFromType bindings (TyUnboxedSum _ ts) = concatMap (varsFromType bindings) ts
85 | varsFromType bindings (TyApp _ t1 t2) = varsFromType bindings t1 ++ varsFromType bindings t2
86 | varsFromType bindings (TyParen _ t) = varsFromType bindings t
87 | varsFromType _ _ = error "Unsupported type"
88 |
89 | hasInstance :: [Instance] -> String -> Term -> Goal
90 | hasInstance ins typeclass t =
91 | let instances = filter (\(Instance cl _ _) -> cl == typeclass) ins
92 | generateCondaLine fromClass fromInstance additional =
93 | [ callFresh
94 | ( \newVar1 ->
95 | callFresh
96 | ( \newVar2 ->
97 | conjN
98 | [ newVar1 ==< fromClass,
99 | newVar2 ==< fromInstance,
100 | newVar2 === newVar1,
101 | additional ins newVar2
102 | ]
103 | )
104 | ),
105 | succeeds
106 | ]
107 | condaLines = map (\i -> generateCondaLine t (tcVar i) (tcAdditional i)) instances
108 | in conda $ condaLines ++ [[succeeds, fails]]
109 |
110 | main :: IO ()
111 | main = do
112 | args <- getArgs
113 | let path = head args
114 | processFile path
115 |
--------------------------------------------------------------------------------
/src/JsonInstance.hs:
--------------------------------------------------------------------------------
1 | module JsonInstance where
2 |
3 | import Data.Aeson
4 | import Kanren
5 | import Language.Haskell.Exts.SrcLoc
6 | import Reasoning
7 | import Run
8 |
9 | instance ToJSON TypeForm
10 |
11 | instance ToJSON SrcLoc
12 |
13 | instance ToJSON Order
14 |
15 | instance ToJSON ChStep
16 |
17 | instance ToJSON SrcSpan
18 |
19 | instance ToJSON ChContext
20 |
21 | instance ToJSON ChResult
22 |
23 | instance ToJSON Affinity
24 |
--------------------------------------------------------------------------------
/src/Nameable.hs:
--------------------------------------------------------------------------------
1 | module Nameable where
2 |
3 | import Language.Haskell.Exts.Syntax
4 |
5 | newtype StringLike a = StringLike {getString :: String} deriving (Show, Eq)
6 |
7 | class Nameable n where
8 | getName :: n a -> String
9 |
10 | instance Nameable Name where
11 | getName (Ident _ name) = name
12 | getName (Symbol _ name) = name
13 |
14 | instance Nameable QName where
15 | getName (Qual _ _ name) = getName name
16 | getName (UnQual _ name) = getName name
17 | getName (Special _ special) = getName special
18 |
19 | instance Nameable SpecialCon where
20 | getName (UnitCon l) = "()"
21 | getName (ListCon l) = "List"
22 | getName (FunCon l) = "Function"
23 | getName (TupleCon l _ n) = "(" ++ take (n - 1) (repeat ',') ++ ")"
24 | getName (Cons l) = ":"
25 | getName (UnboxedSingleCon l) = "(# #)"
26 | getName (ExprHole l) = "_"
27 |
28 | instance Nameable Match where
29 | getName (Match _ name _ _ _) = getName name
30 | getName (InfixMatch _ _ name _ _ _) = getName name
31 |
32 | instance Nameable TyVarBind where
33 | getName (KindedVar _ name _) = getName name
34 | getName (UnkindedVar _ name) = getName name
35 |
36 | instance Nameable StringLike where
37 | getName (StringLike x) = x
38 |
39 | instance Nameable QOp where
40 | getName (QVarOp _ qname) = getName qname
41 | getName (QConOp _ qname) = getName qname
--------------------------------------------------------------------------------
/src/Reasoning.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DeriveGeneric #-}
2 |
3 | module Reasoning where
4 |
5 | import Debug.Trace
6 | import GHC.Generics
7 | import Kanren
8 | import Language.Haskell.Exts.SrcLoc (SrcSpan)
9 | import Scope
10 | import Typing
11 |
12 | data ChStep = ChStep
13 | { explanation :: String,
14 | order :: Order,
15 | stepA :: SrcSpan,
16 | stepB :: SrcSpan,
17 | stepId :: (Int, Int)
18 | }
19 | deriving (Show, Eq, Generic)
20 |
21 | data Order = LR | RL deriving (Show, Eq, Generic)
22 |
23 | compareConstraints :: LabeledGoal -> LabeledGoal -> [ChStep]
24 | compareConstraints (Label n1 (term1, term1') _ reason1 loc1 _) (Label n2 (term2, term2') _ reason2 loc2 _)
25 | | loc1 == loc2 = []
26 | | loc1 `within` loc2
27 | && reason1 == "Annotated"
28 | && reason2 == "Annotated" =
29 | [ChStep "is in the context of" LR loc1 loc2 (n1, n2)]
30 | | loc2 `within` loc1
31 | && reason1 == "Annotated"
32 | && reason2 == "Annotated" =
33 | [ChStep "is in the context of" RL loc1 loc2 (n1, n2)]
34 |
35 | | loc1 `within` loc2
36 | && reason2 == "Applied"
37 | =
38 | -- application expression: x = y z
39 | case (isFunction term2, isFunction term2') of
40 | (True, False) -> if term1 == term2' || term1' == term2'
41 | then [ChStep "is applied at" LR loc1 loc2 (n1, n2)]
42 | else [ChStep "is an argument in" LR loc1 loc2 (n1, n2)]
43 | (False, True) -> if term1 == term2 || term1' == term2
44 | then [ChStep "is applied at" LR loc1 loc2 (n1, n2)]
45 | else [ChStep "is an argument in" LR loc1 loc2 (n1, n2)]
46 | (_ , _) -> [ChStep "is part of" LR loc1 loc2 (n1, n2)]
47 |
48 | | loc2 `within` loc1
49 | && reason1 == "Applied"
50 | =
51 |
52 | case (isFunction term1, isFunction term1') of
53 | (True, False) -> if term2 == term1' || term2' == term1'
54 | then [ChStep "is applied at" RL loc1 loc2 (n1, n2)]
55 | else [ChStep "is an argument in" RL loc1 loc2 (n1, n2)]
56 | (False, True) -> if term2 == term1 || term2' == term1
57 | then [ChStep "is applied at" RL loc1 loc2 (n1, n2)]
58 | else [ChStep "is an argument in" RL loc1 loc2 (n1, n2)]
59 | (_ , _) -> [ChStep "is part of" RL loc1 loc2 (n1, n2)]
60 | | loc1 `within` loc2
61 | && reason1 == "Matched"
62 | && reason2 == "Matched"
63 | =
64 | -- application expression in pattern matching:x (Y z) = u
65 | case (isFunction term2, isFunction term2') of
66 | (True, False) -> if term1 == term2' || term1' == term2'
67 | then [ChStep "is applied at" LR loc1 loc2 (n1, n2)]
68 | else [ChStep "is an argument in" LR loc1 loc2 (n1, n2)]
69 | (False, True) -> if term1 == term2 || term1' == term2
70 | then [ChStep "is applied at" LR loc1 loc2 (n1, n2)]
71 | else [ChStep "is an argument in" LR loc1 loc2 (n1, n2)]
72 | (_ , _) -> [ChStep "is part of" LR loc1 loc2 (n1, n2)]
73 |
74 | | loc2 `within` loc1
75 | && reason1 == "Matched"
76 | && reason2 == "Matched"
77 | =
78 | -- application expression in pattern matching:x (Y z) = u
79 | case (isFunction term1, isFunction term1') of
80 | (True, False) -> if term2 == term1' || term2' == term1'
81 | then [ChStep "is applied at" RL loc1 loc2 (n1, n2)]
82 | else [ChStep "is an argument in" RL loc1 loc2 (n1, n2)]
83 | (False, True) -> if term2 == term1 || term2' == term1
84 | then [ChStep "is applied at" RL loc1 loc2 (n1, n2)]
85 | else [ChStep "is an argument in" RL loc1 loc2 (n1, n2)]
86 | (_ , _) -> [ChStep "is part of" RL loc1 loc2 (n1, n2)]
87 |
88 | | loc2 `within` loc1 && reason1 == "Literal" = [ChStep "is defined in" RL loc1 loc2 (n1, n2)]
89 | | loc1 `within` loc2 && reason2 == "Literal" = [ChStep "is defined in" LR loc1 loc2 (n1, n2)]
90 | | reason1 == "Annotated" && reason2 == "Annotated" = []
91 | | reason1 == "Annotated" && reason2 /= "Annotated" = [ChStep "is annotated at" RL loc1 loc2 (n1, n2)]
92 | | reason1 /= "Annotated" && reason2 == "Annotated" = [ChStep "is annotated at" LR loc1 loc2 (n1, n2)]
93 | | term1 == term2 && not (isFresh term1) && not (isFresh term2) = [ChStep "is identical to" LR loc1 loc2 (n1, n2)]
94 | | term1 == term2' && not (isFresh term1) && not (isFresh term2') = [ChStep "is identical to" LR loc1 loc2 (n1, n2)]
95 | | term1' == term2 && not (isFresh term1') && not (isFresh term2) = [ChStep "is identical to" LR loc1 loc2 (n1, n2)]
96 | | term1' == term2' && not (isFresh term1') && not (isFresh term2') = [ChStep "is identical to" LR loc1 loc2 (n1, n2)]
97 | | reason1 == "Defined" && reason2 == "Matched" = [ChStep "takes argument" LR loc1 loc2 (n1, n2)]
98 | | reason1 == "Matched" && reason2 == "Defined" = [ChStep "takes argument" RL loc1 loc2 (n1, n2)]
99 | |
100 | -- trace
101 | -- ( "\nreason1: "
102 | -- ++ reason1
103 | -- ++ " ("
104 | -- ++ show term1
105 | -- ++ " === "
106 | -- ++ show term1'
107 | -- ++ ")\nreason2: "
108 | -- ++ reason2
109 | -- ++ " ("
110 | -- ++ show term2
111 | -- ++ " === "
112 | -- ++ show term2'
113 | -- ++ ")\n"
114 | -- ) $
115 | reason1 /= "Defined" && reason2 == "Defined" = [ChStep "is defined as" RL loc1 loc2 (n1, n2)]
116 | |
117 | -- trace
118 | -- ( "\nreason1: "
119 | -- ++ reason1
120 | -- ++ " ("
121 | -- ++ show term1
122 | -- ++ " === "
123 | -- ++ show term1'
124 | -- ++ ")\nreason2: "
125 | -- ++ reason2
126 | -- ++ " ("
127 | -- ++ show term2
128 | -- ++ " === "
129 | -- ++ show term2'
130 | -- ++ ")\n"
131 | -- ) $
132 | reason1 == "Defined" && reason2 /= "Defined" = [ChStep "is defined as" LR loc1 loc2 (n1, n2)]
133 | |
134 |
135 | reason1 /= "Defined" && reason2 == "Defined" = [ChStep "is defined as" RL loc1 loc2 (n1, n2)]
136 | | reason1 == "Applied" && reason2 == "Applied" && loc1 <= loc2 = [ChStep "is applied at" LR loc1 loc2 (n1, n2)]
137 | | reason1 == "Applied" && reason2 == "Applied" && loc1 > loc2 = [ChStep "is applied at" RL loc1 loc2 (n1, n2)]
138 | | reason1 == "Applied" && reason2 /= "Applied" = [ChStep "is applied at" RL loc1 loc2 (n1, n2)]
139 | | reason1 /= "Applied" && reason2 == "Applied" = [ChStep "is applied at" LR loc1 loc2 (n1, n2)]
140 | | reason2 == "Instanciated"
141 | || reason1 == "Instanciated" = [ChStep "has same type as" LR loc1 loc2 (n1, n2)]
142 | | otherwise =
143 | -- trace
144 | -- ( "\nreason1: "
145 | -- ++ reason1
146 | -- ++ " ("
147 | -- ++ show term1
148 | -- ++ " === "
149 | -- ++ show term1'
150 | -- ++ ")\nreason2: "
151 | -- ++ reason2
152 | -- ++ " ("
153 | -- ++ show term2
154 | -- ++ " === "
155 | -- ++ show term2'
156 | -- ++ ")\n"
157 | -- ) $
158 | [ChStep "has same type as" LR loc1 loc2 (n1, n2)]
159 |
--------------------------------------------------------------------------------
/stack.yaml:
--------------------------------------------------------------------------------
1 | # This file was automatically generated by 'stack init'
2 | #
3 | # Some commonly used options have been documented as comments in this file.
4 | # For advanced use and comprehensive documentation of the format, please see:
5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/
6 |
7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version.
8 | # A snapshot resolver dictates the compiler version and the set of packages
9 | # to be used for project dependencies. For example:
10 | #
11 | # resolver: lts-3.5
12 | # resolver: nightly-2015-09-21
13 | # resolver: ghc-7.10.2
14 | #
15 | # The location of a snapshot can be provided as a file or url. Stack assumes
16 | # a snapshot provided as a file might change, whereas a url resource does not.
17 | #
18 | # resolver: ./custom-snapshot.yaml
19 | # resolver: https://example.com/snapshots/2018-01-01.yaml
20 | resolver: lts-18.18
21 |
22 | # User packages to be built.
23 | # Various formats can be used as shown in the example below.
24 | #
25 | # packages:
26 | # - some-directory
27 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz
28 | # subdirs:
29 | # - auto-update
30 | # - wai
31 | packages:
32 | - .
33 | # Dependency packages to be pulled from upstream that are not in the resolver.
34 | # These entries can reference officially published versions as well as
35 | # forks / in-progress versions pinned to a git hash. For example:
36 | #
37 | # extra-deps:
38 | # - acme-missiles-0.3
39 | # - git: https://github.com/commercialhaskell/stack.git
40 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
41 | #
42 | extra-deps:
43 |
44 | # Override default flag values for local packages and extra-deps
45 | # flags: {}
46 |
47 | # Extra package databases containing global packages
48 | # extra-package-dbs: []
49 |
50 | # Control whether we use the GHC we find on the path
51 | # system-ghc: true
52 | #
53 | # Require a specific version of stack, using version ranges
54 | # require-stack-version: -any # Default
55 | # require-stack-version: ">=2.5"
56 | #
57 | # Override the architecture used by stack, especially useful on Windows
58 | # arch: i386
59 | # arch: x86_64
60 | #
61 | # Extra directories used by stack for building
62 | # extra-include-dirs: [/path/to/dir]
63 | # extra-lib-dirs: [/path/to/dir]
64 | #
65 | # Allow a newer minor version of GHC than the snapshot specifies
66 | # compiler-check: newer-minor
67 |
--------------------------------------------------------------------------------
/stack.yaml.lock:
--------------------------------------------------------------------------------
1 | # This file was autogenerated by Stack.
2 | # You should not edit this file by hand.
3 | # For more information, please see the documentation at:
4 | # https://docs.haskellstack.org/en/stable/lock_files
5 |
6 | packages: []
7 | snapshots:
8 | - completed:
9 | size: 586296
10 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/18.yaml
11 | sha256: 63539429076b7ebbab6daa7656cfb079393bf644971156dc349d7c0453694ac2
12 | original: lts-18.18
13 |
--------------------------------------------------------------------------------
/static/Debugger.jsx:
--------------------------------------------------------------------------------
1 | import React from "react"
2 | import { useSelector, useDispatch } from 'react-redux';
3 | import { unAlias } from './util';
4 | import * as R from "ramda"
5 | import {
6 | prevStep,
7 | nextStep,
8 | setStep,
9 | switchTaskThunk,
10 | } from './debuggerSlice';
11 | import { ArrowCircleUpIcon, ArrowCircleDownIcon } from "@heroicons/react/solid"
12 | import TabReport from "./TabReport"
13 | import TypeSig from './TypeSig'
14 |
15 | const Debugger = () => {
16 | let wellTyped = useSelector(state => state.debugger.wellTyped);
17 | let loadError = useSelector(state => state.debugger.loadError);
18 | let parseError = useSelector(state => state.debugger.parseError);
19 | return (
20 |
A syntax error was found in the code
44 |{parseError.message}
46 |47 | Location: {parseError.loc.srcLine}:{parseError.loc.srcColumn} 48 |
49 |A variable is used without being declared.
59 | {loadError.map(([v, loc]) => { 60 | return ( 61 |Variable: {v}
63 |64 | Location: {' ' + loc.srcSpanStartLine}: 65 | {loc.srcSpanStartColumn} -{' ' + loc.srcSpanEndLine}: 66 | {loc.srcSpanEndColumn} 67 |
68 |Load examples:
93 | {' '} 108 | > 109 | ) : Task {currentTaskNum + 1}/9 } 110 | 126 | {currentTaskAttemps > 5 && OUTPUT_TARGET === 'userstudy' ? ( 127 | 150 | ) : null} 151 | {mode === editorModes.normal ? null : ( 152 | 168 | )} 169 | 170 | 176 |Our study is finished. Feel free to check out chameleon at the playground -------------------------------------------------------------------------------- /static/build/explanatory.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 |
21 |
22 |
Project ID: 27885
27 |Prof. Tim Dwyer | 35 |Prof. Peter Stuckey | 36 |Shuai Fu (Ph.D. student) | 37 |
---|---|---|
Department of Human Centred Computing | 42 |Department of Data Science and Artificial Intelligence | 43 |Department of Human Centred Computing | 44 |
Phone: 9905 0234 | 47 |Phone: 9903 2405 | 48 |Phone: 0449 127 003 | 49 |
52 | Email: 53 | tim.dwyer@monash.edu 54 | | 55 |56 | Email: 57 | peter.stuckey@monash.edu 59 | | 60 |61 | Email: 62 | shuai.fu@monash.edu 63 | | 64 |
69 | You are invited to take part in this study. Please read this Explanatory 70 | Statement in full before deciding whether or not to participate in this 71 | research. If you would like further information regarding any aspect of 72 | this project, you are encouraged to contact the researchers via the phone 73 | numbers or email addresses listed above. 74 |
75 |79 | This study is designed to study the effectiveness of different 80 | types of error messages in the Chameleon type checking tool. 81 | Chameleon is a tool we have developed to help programmers 82 | identify and solve Haskell type errors. 83 |
84 |85 | Once you have navigated to the user study website, you will be 86 | prompted with 9 different code challenges. Each code challenge is an 87 | editable code fragment in a web page, presented with debugging 88 | information on the right side of the webpage to help you investigate 89 | the type errors (if there are any). 90 |
91 |92 | Chameleon can be configured in three modes: basic mode, balanced 93 | mode, and advanced mode. In basic mode, debugging information is 94 | minimal, and you have little control over the display of the error 95 | message. In balanced mode, you have moderate control to narrow down 96 | the possible cause of a type error. In advanced mode, you have 97 | access to a maximal amount of debugging information and granular 98 | control in tracing the root cause of type errors. At the start of 99 | each code challenge, the Chameleon debugging tool is set to one of 100 | the three modes. However, you are free to switch between modes by 101 | pressing the tab key or clicking the dedicated mode switching 102 | buttons. 103 |
104 |105 | After the study, you will be asked to leave overall feedback of your 106 | experience in working with Chameleon debugger. 107 |
108 |We will collect the data in two methods:
112 |123 | We will use first party cookie (under the same domain name) to store 124 | a temporary unique identifier in your browser. We will not share 125 | this cookie to any other parties. Nor will we access any other 126 | cookie you already have installed from other websites. All data 127 | transmission from your browser to our server are encrypted. 128 |
129 |133 | You opt in to participate this research by click the link we posted 134 | on internet forum and interest group websites. 135 |
136 |The consent process includes the following steps:
142 |154 | You can choose to withdraw from further participation at any stage. 155 | To withdraw from this study, you simply close the user study website 156 | tab in the web browser. We will erase all the data associate with 157 | your session if you did not complete the study. Additionally, we 158 | will provide you a withdraw ID at the start of this study. You can 159 | email the student investigator ( 161 | shuai.fu@monash.edu 162 | ) with your withdraw ID at anytime to withdraw your data, even 163 | if you completed the study. 164 |
165 |169 | There's no risks involved in participating this research. You 170 | are not required to be physically present. The study does not 171 | require you to perform physical activities or interact with physical 172 | objects. We will keep the study within a minimal time frame of 20 173 | minutes to avoid strain. The information you will provide in the 174 | study are limited to the area of programming language knowledge and 175 | skill. 176 |
177 |178 | The user study gives no immediate benefit to the participants, 179 | However in the long-term, you may benefit from the tools that we 180 | develop as part of the research. Broadly, this user study will help 181 | researchers to better understand how to design, and improve the 182 | compiler interface and interactive IDE. 183 |
184 |186 | We will not collect any data that can be linked to your identity, 187 | your device, or your location. We will use a random identifier (such 188 | as GUID) to group the collected data by each participant. 189 |
190 |191 | All published data will remain anonymous. The collected data and 192 | questionnaire answers will be used in the publication. 193 |
194 |195 | The members of the this project may include the user study results 196 | in journal articles, conference papers, or book chapters. Student 197 | co-investigator may include the user study result in his Ph.D. 198 | thesis. 199 |
200 |202 | The data will be stored offline-only, in an encrypted hard drive 203 | located in Australia. The members of the Interactive Haskell Type 204 | Inference Exploration project will be able to access the data. The 205 | data will be erased from the hard drive 12 months after it is 206 | collected. 207 |
208 |210 | The members of the Interactive Haskell Type Inference Exploration 211 | project may include the user study results in journal articles, 212 | conference papers, or book chapters. Student co-investigator (Shuai 213 | Fu) may include the user study result in his Ph.D. thesis. We will 214 | only publish summary-formed results. No personal data will be 215 | published. Participants may gain access to a pre-print electronic 216 | version of a journal article or conference paper after it will have 217 | been published. Participants may gain access to the electronic 218 | version of the student investigator’s Ph.D. thesis after it has been 219 | published. 220 |
221 |223 | Should you have any concerns or complaints about the conduct of the 224 | project, you are welcome to contact the Executive Officer, Monash 225 | University Human Research Ethics Committee (MUHREC): 226 |
227 |228 | Executive Officer Monash University Human Research Ethics Committee 229 | (MUHREC) Room 111, Chancellery Building D, 26 Sports Walk, Clayton 230 | Campus Research Office Monash University VIC 3800 231 |
232 |Tel: +61 3 9905 2052
233 | 234 |235 | Email: 236 | muhrec@monash.edu 237 |
238 | 239 |Fax: +61 3 9905 3831
240 |Thank you,
241 |Prof. Tim Dwyer
242 |28 | A tool to make solving type errors in Haskell simple and fun. 29 |
30 | Go to playground 31 |63 | We improve each feature in Chameleon based on the 64 | feedback from the Haskell community. Debugging idioms 65 | in Chameleon has been tested individually and in combinations. 66 |
67 |71 | 72 | While many type systems try to pinpoint one exact error location in the code, the accuracy is often 73 | hit or 74 | miss. Chameleon tries to narrow down to a few suspects and asks the programmer to identify the real 75 | culprit. 76 | While both approaches have pros and cons, we believe Chameleon is more flexible and catches bugs 77 | faster. 78 |
79 |83 | Instead of assuming one type is "Expected" and one type is "Actual", Chameleon will report two 84 | equally 85 | possible alternatives that type errors can happen. Many techniques have been proposed to solve this 86 | problem 87 | (Known as left-right bias) on type solver level. Chameleon combines the type solver capable of 88 | eliminating 89 | this bias and smart visual cues to distinguish the evidence for one type and the other. 90 |
91 |95 | The deduction step is a tool to peek inside the type checking engine. It shows step-by-step 96 | reasoning that 97 | explains why one type cannot reconcile with another in simple language. Chameleon's interactive 98 | interface 99 | allows users to make incremental assumptions and see how that affects the typing of the whole 100 | program. 101 |
102 |106 | Hang tight! We are working on more features to make Chameleon even better! 107 |
108 |The chameleon playground is an online editor for you to test out some of the chameleon 115 | features. 116 |
117 | Go to playground 118 |Start
25 | 26 |226 | Congratulations. You fixed the type error! 227 |
228 | {currentTaskNum === 8 ? ( 229 |Click next to leave us some feedback.
230 | ) : ( 231 |232 | Click next to head over to the next challenge. 233 |
234 | )} 235 |