Skip to content

Commit 8ec8374

Browse files
committed
haddock-ready tutorial
1 parent 747a943 commit 8ec8374

File tree

1 file changed

+144
-129
lines changed

1 file changed

+144
-129
lines changed
Lines changed: 144 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,92 @@
1-
module Ide.Plugin.Eval.Tutorial where
2-
3-
{-
4-
The "Eval" plugin evaluates code in comment prompts and splice the results right below.
1+
{- |
2+
The Eval plugin evaluates code in comments.
53
64
This is mainly useful to:
5+
76
* quickly evaluate small code fragments
7+
88
* test and document functions
99
10+
Every line of code to be evaluated is introduced by __>>>__.
11+
1012
A quick calculation:
13+
1114
>>> 2**4.5/pi
1215
7.202530529256849
1316
14-
A little test for the following function:
15-
16-
>>> import Data.List(intercalate)
17-
>>> intercalate " " example
18-
"This is an example of interactive evaluation"
19-
-}
20-
example :: [String]
21-
example = ["This is an example", "of", "interactive", "evaluation"]
17+
A little test for the `double` function:
2218
23-
{-
24-
The basic unit that gets evaluated is a test, that's to say a sequence of:
25-
* imports
26-
* statements
27-
* directives
28-
* properties
29-
* expressions
30-
in no particular order, with every line introduced by `>>>` (or `prop>` in the case of properties).
31-
32-
For example:
19+
>>> double 11
20+
22
3321
34-
>>> import Data.Maybe(fromJust)
35-
>>> just2 = fromJust . fromJust
36-
>>> :type just2
37-
just2 :: forall c. Maybe (Maybe c) -> c
22+
You execute a test by clicking on the /Evaluate/ or /Refresh/ (if the test has been run previously) code lens that appears above it.
3823
39-
>>> just2 (Just (Just True))
40-
True
24+
All tests in the same comment are executed together.
4125
42-
Tests can appear in either plain comments like this one or in Haddock comments.
26+
Tests can appear in all kind of comments: plain or Haddock (forward of backwards), single line or multiple line.
4327
44-
You execute a test by clicking on `Evaluate` or `Refresh`.
45-
-}
28+
Both plain Haskell and Literate Haskell (Bird-style only) source files are supported.
4629
47-
{-
48-
A test can contain multiple expressions:
30+
A test can be composed of multiple lines:
4931
5032
>>> "AB" ++ "CD"
5133
>>> "CD" ++ "AB"
5234
"ABCD"
5335
"CDAB"
54-
-}
55-
56-
{- |
57-
All tests in the same comment sections are executed together.
58-
59-
This is very convenient to execute multiple tests on the same function, as in this example:
6036
61-
>>> double 0
62-
0
63-
64-
>>> double 11
65-
22
66-
67-
>>> double 22
68-
44
69-
-}
70-
double :: Num a => a -> a
71-
double n = n * 2
37+
In general, a test is a sequence of:
7238
73-
-- You can define a '$setup' section, whose code is executed before any other test.
39+
* imports
7440
75-
{- $setup
76-
>>> x = 11
77-
>>> y = 22
78-
-}
41+
* directives
7942
80-
-- 'x' and 'y' are available in any test:
81-
-- >>> (x,y)
82-
-- (11,22)
43+
* statements
8344
84-
{- |
85-
Haddock comments, like this one, constitute the external module's documentation.
45+
* expressions
8646
87-
Their tests are part of the module functions' definitions and their results are not supposed to change.
47+
* properties
8848
89-
So, whenever tests in Haddock comments are refreshed, their current result is compared with the previous one and differences are displayed.
49+
in no particular order, with every line introduced by __>>>__ (or __prop>__ in the case of properties).
9050
91-
If by mistake we change the definition of 'evens', we get a warning:
51+
= Test Components
9252
93-
>>> evens [1..7]
94-
WAS [2,4,6]
95-
NOW [1,3,5,7]
53+
== Imports
9654
97-
On the contrary, the result of tests in plain comments are simply updated.
98-
-}
99-
evens :: [Integer] -> [Integer]
100-
evens = filter odd
55+
>>> import Data.List
56+
>>> import GHC.TypeNats
10157
102-
{-
103-
Let's see in more detail the components of a test.
58+
From any package in scope but currently NOT from modules in the same source directory.
10459
105-
Language Extensions:
60+
== Language Extensions
10661
10762
>>> :set -XScopedTypeVariables -XStandaloneDeriving -XDataKinds -XTypeOperators -XExplicitNamespaces
10863
109-
Imports, from any package in scope:
110-
111-
>>> import Data.List
112-
>>> import GHC.TypeNats
113-
114-
or from modules in the same source directory:
115-
116-
>>> import Ide.Plugin.Eval.CodeLens
64+
=== Statements and Declarations
11765
118-
Statements/declarations (function declaration can optionally be introduced by 'let'):
66+
Function declarations (optionally introduced by /let/):
11967
12068
>>> let tuple x = (x,x)
69+
>>> let one=1;two=2
12170
>>> triple x = (x,x,x)
71+
72+
Any other declaration:
73+
12274
>>> data TertiumDatur = Truly | Falsely | Other deriving Show
12375
>>> class Display a where display :: a -> String
12476
>>> instance Display TertiumDatur where display = show
12577
126-
Type and Kind directives (as in ghci):
78+
Definitions are available to following tests in the __same__ comment.
79+
80+
If you want definitions to be available to all tests in the module, define a setup section:
81+
82+
@
83+
-- $setup
84+
-- >>> eleven = 11
85+
@
86+
87+
/eleven/ is now available to any test.
88+
89+
== Type and Kind directives
12790
12891
>>> :type Truly
12992
Truly :: TertiumDatur
@@ -143,82 +106,67 @@ TertiumDatur :: *
143106
N + M + 1 :: Nat
144107
= 42
145108
146-
Properties:
147-
148-
prop> \(l::[Int]) -> reverse (reverse l) == l
149-
+++ OK, passed 100 tests.
150-
151-
prop> \(l::[Bool]) -> reverse l == l
152-
*** Failed! Falsified (after 8 tests and 1 shrink):
153-
[False,True]
154-
155-
And finally expressions:
109+
== Expressions
156110
157111
>>> tuple 2
158112
>>> triple 3
159113
>>> display Other
160114
(2,2)
161115
(3,3,3)
162116
"Other"
163-
-}
164-
165-
-- Though the Eval plugin functionality is quite similar to that of <https://hackage.haskell.org/package/doctest Doctest>, some features are not supported.
166117
167-
{-
168-
Capturing stdout:
118+
IO expressions can also be evaluated but their output to stdout/stderr is NOT captured:
169119
170120
>>> print "foo"
171121
()
172-
-}
173122
174-
{- |
175-
Matching arbitrary output
123+
== Properties
176124
177-
>>> [1..5]
178-
[1,2,...
179-
-}
125+
prop> \(l::[Int]) -> reverse (reverse l) == l
126+
+++ OK, passed 100 tests.
180127
181-
{-
182-
Omitting lambda abstractions in property tests:
128+
= Haddock vs Plain Comments
183129
184-
prop> reverse (reverse l) == (l::[Int])
185-
Variable not in scope: l :: [Int]
186-
Variable not in scope: l :: [Int]
130+
There is a conceptual difference between Haddock and plain comments.
187131
188-
This works:
132+
Haddock comments constitute the external module's documentation while plain comments are internal documentation meant to explain how the code works (api vs implementation).
189133
190-
prop> \(l::[Int]) -> reverse (reverse l) == l
191-
+++ OK, passed 100 tests.
192-
-}
134+
This conceptual difference is reflected in the way tests results are refreshed by the Eval plugin.
193135
194-
{-
195-
Multiline comments like the following:
196-
>>> :{
197-
let
198-
x = 1
199-
y = 2
200-
in x + y + multiline
201-
:}
202-
-}
136+
Tests in plain comments are refreshed by overwriting the previous result.
137+
138+
On the contrary, when tests in Haddock comments are refreshed their current result is compared with the previous one and differences are displayed.
139+
140+
Say for example that we have defined a test on the `thrice` function, defined as:
203141
204-
{- Tip: Pretty printing.
142+
>>> thrice = (*3)
205143
206-
Say that you want to pretty print a value.
144+
If by mistake at a later time we change its definition to:
207145
208-
You could, for example, use the pretty-simple package:
146+
>>> thrice = (*2)
147+
148+
When we refresh its test we get a warning:
149+
150+
>>> thrice 11
151+
WAS 33
152+
NOW 22
153+
154+
== Tip: Multiline Output
155+
156+
By default, the output of every expression is returned as a single line.
157+
158+
This is a problem if you want, for example, to pretty print a value (in this case using the <https://hackage.haskell.org/package/pretty-simple pretty-simple> package):
209159
210160
>>> import Text.Pretty.Simple
211161
>>> pShowNoColor [1..3]
212162
"[ 1\n, 2\n, 3\n]"
213163
214-
But what we get is just a String.
215-
216-
We could try to print it, but stdout is not captured:
164+
We could try to print the pretty-print output, but stdout is not captured so we get just a ():
217165
218166
>>> print $ pShowNoColor [1..7]
219167
()
220168
221-
To 'print' it properly, we can exploit the fact that the output of an error is displayed as a multi-line text:
169+
To display it properly, we can exploit the fact that the output of an error is displayed as a multi-line text:
222170
223171
>>> import qualified Data.Text.Lazy as TL
224172
>>> import Text.Pretty.Simple
@@ -228,4 +176,71 @@ To 'print' it properly, we can exploit the fact that the output of an error is d
228176
, 2
229177
, 3
230178
]
179+
180+
= Differences with doctest
181+
182+
Though the Eval plugin functionality is quite similar to that of <https://hackage.haskell.org/package/doctest doctest>, some doctest features are not supported.
183+
184+
== Capturing Stdout
185+
186+
Only the value of the expression is spliced in, not its output:
187+
188+
>>> print "foo"
189+
()
190+
191+
== Pattern Matching
192+
193+
The arbitrary content matcher __...__ is unsupported.
194+
195+
== Missing lambda abstractions in property tests
196+
197+
Variables are not automatically introduced:
198+
199+
prop> reverse (reverse l) == (l::[Int])
200+
Variable not in scope: l :: [Int]
201+
Variable not in scope: l :: [Int]
202+
203+
This works:
204+
205+
prop> \(l::[Int]) -> reverse (reverse l) == l
206+
+++ OK, passed 100 tests.
207+
208+
== Multiline Expressions
209+
210+
@
211+
>>> :{
212+
let
213+
x = 1
214+
y = 2
215+
in x + y + multiline
216+
:}
217+
@
218+
-}
219+
module Ide.Plugin.Eval.Tutorial (
220+
double,
221+
) where
222+
223+
{- | Double a number
224+
225+
An example of a simple test suite for a function (all tests are executed together).
226+
227+
>>> double 1
228+
2
229+
230+
>>> double 11
231+
22
232+
233+
>>> double 22
234+
44
231235
-}
236+
double :: Num a => a -> a
237+
double n = n * 2
238+
239+
{- ORMOLU_DISABLE -}
240+
-- $setup
241+
-- >>> x = 11;
242+
-- >>> y = 22
243+
244+
-- >>> (x,y)
245+
-- (11,22)
246+

0 commit comments

Comments
 (0)