hls-eval-plugin: Eval plugin for Haskell Language Server

[ apache, development, library ] [ Propose Tags ]

[Skip to Readme]
Versions [faq],,,,,
Dependencies aeson, base (>=4.12 && <5), containers, deepseq, Diff, directory, extra, filepath, ghc, ghc-boot-th, ghc-paths, ghcide, hashable, haskell-lsp, haskell-lsp-types, hls-plugin-api, parser-combinators, pretty-simple, QuickCheck, safe-exceptions, shake, temporary, text, time, transformers, unordered-containers [details]
License Apache-2.0
Author https://github.com/haskell/haskell-language-server/contributors
Maintainer https://github.com/haskell/haskell-language-server/contributors
Category Development
Bug tracker https://github.com/haskell/haskell-language-server/issues
Source repo head: git clone https://github.com/haskell/haskell-language-server
Uploaded by PasqualinoAssini at 2021-01-14T12:49:26Z
Downloads 75 total (75 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2021-01-14 [all 1 reports]


[Index] [Quick Jump]



Enable -Werror


Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info


Maintainer's Corner

For package maintainers and hackage trustees

Readme for hls-eval-plugin-

[back to package description]

Eval plugin for the Haskell Language Server

The Eval plugin evaluates code inserted in comments.

This is mainly useful to test and document functions and to quickly evaluate small expressions.

Every line of code to be evaluated is introduced by >>>

A quick calculation:

-- >>> 2**4.5/pi
-- 7.202530529256849

A little test for the double function:

{- |
A doubling function.

>>> double 11
double = (2*)


Eval Demo

Test Structure

A test is composed by a sequence of contiguous lines, the result of their evaluation is inserted after the test body:

>>> "AB" ++ "CD"
>>> "CD" ++ "AB"

You execute a test by clicking on the Evaluate code lens that appears above it (or Refresh, if the test has been run previously).

All tests in the same comment block are executed together.

Tests can appear in all kind of comments:

  • plain comments (both single and multi line)
>>> "ab" ++ "c"

-- >>> "ab" ++ "c"
-- "abc"
  • Haddock commands (both single and multi line, forward and backward)
>>> "ab" ++ "c"

-- >>> "ab" ++ "c"
-- "abc"

double a = a + a
-- ^ A doubling function
-- >>> double 11
-- 22

Both plain Haskell and Literate Haskell (Bird-style only) source files are supported.

Test Components

In general, a test is a sequence of:

  • imports
  • directives
  • statements
  • expressions
  • properties

in no particular order, with every line introduced by >>> (or prop> in the case of properties).


>>> import Data.List
>>> import GHC.TypeNats

From any package in scope but currently NOT from modules in the same source directory.

Language Extensions

>>> :set -XScopedTypeVariables -XStandaloneDeriving -XDataKinds -XTypeOperators -XExplicitNamespaces

Statements and Declarations

Function declarations (optionally introduced by let):

>>> let tuple x = (x,x)
>>> let one=1;two=2
>>> triple x = (x,x,x)

Any other declaration:

>>> data TertiumDatur = Truly | Falsely | Other deriving Show
>>> class Display a where display :: a -> String
>>> instance Display TertiumDatur where display = show

Definitions are available to following tests in the same comment:

>>> two = 2

>>> two

-- >>> two
-- Variable not in scope: two

If you want definitions to be available to all tests in the module, define a setup section:

-- $setup
-- >>> eleven = 11

eleven is now available to any test:

>>> eleven*2

Type and Kind directives

>>> :type Truly
Truly :: TertiumDatur

>>> :kind TertiumDatur
TertiumDatur :: *

>>> :type 3
3 :: forall p. Num p => p

>>> :type +d 3
3 :: Integer

>>> type N = 1
>>> type M = 40
>>> :kind! N + M + 1
N + M + 1 :: Nat
= 42


>>> tuple 2
>>> triple 3
>>> display Other

IO expressions can also be evaluated but their output to stdout/stderr is NOT captured:

>>> print "foo"


prop> \(l::[Int]) -> reverse (reverse l) == l
+++ OK, passed 100 tests.

Haddock vs Plain Comments

There is a conceptual difference between Haddock and plain comments:

  • Haddock comments constitute the external module's documentation, they state the contract between the implementor and the module users (API)
  • Plain comments are internal documentation meant to explain how the code works (implementation).

This conceptual difference is reflected in the way tests results are refreshed by the Eval plugin.

Say that we have defined a double function as:

double = (*2)

And, in an Haddock comment, we run the test:

{- |
>>> double 11

We then change the definition to:

double = (*3)

When we refresh the test, its current result is compared with the previous one and differences are displayed (as they change the API):

{- |
>>> double 11
WAS 22
NOW 33

On the contrary, if the test were into a plain comment, the result would simply be replaced:

>>> double 11

Multiline Output

By default, the output of every expression is returned as a single line.

This is a problem if you want, for example, to pretty print a value (in this case using the pretty-simple package):

>>> import Text.Pretty.Simple
>>> pShowNoColor [1..3]
"[ 1\n, 2\n, 3\n]"

We could try to print the pretty-print output, but stdout is not captured so we get just a ():

>>> print $ pShowNoColor [1..7]

To display it properly, we can exploit the fact that the output of an error is displayed as a multi-line text:

>>> import qualified Data.Text.Lazy as TL
>>> import Text.Pretty.Simple
>>> prettyPrint v = error (TL.unpack $ pShowNoColor v) :: IO String
>>> prettyPrint [1..3]
[ 1
, 2
, 3

Differences with doctest

Though the Eval plugin functionality is quite similar to that of doctest, some doctest's features are not supported.

Capturing Stdout

Only the value of an IO expression is spliced in, not its output:

>>> print "foo"

Pattern Matching

The arbitrary content matcher ... is unsupported.

Missing lambda abstractions in property tests

Variables are not automatically introduced:

prop> reverse (reverse l) == (l::[Int])
Variable not in scope: l :: [Int]

This works:

prop> \(l::[Int]) -> reverse (reverse l) == l
+++ OK, passed 100 tests.

Multiline Expressions

 >>> :{
    x = 1
    y = 2
  in x + y + multiline


Design/features derived from: